Escolha um adaptador de banco de dados para cada implantação. O banco de dados guarda o modelo de conteúdo, as entradas, os usuários, as configurações e os dados dos plugins. Os binários de mídia ficam em um backend de armazenamento separado.
Visão geral
| Banco de dados | Use quando | Ambiente de execução |
|---|---|---|
| SQLite | Um processo Node.js tem um disco persistente | Node.js ou desenvolvimento local |
| D1 | O site roda no Cloudflare Workers e deve usar o Cloudflare SQL | Cloudflare Workers |
| Hyperdrive | O site roda no Workers e precisa usar uma origem PostgreSQL existente | Cloudflare Workers |
| PostgreSQL | Vários processos Node.js precisam de um banco de dados compartilhado | Node.js |
| libSQL | Uma implantação Node.js precisa de um banco de dados remoto compatível com SQLite | Node.js |
O D1 é o padrão dos templates do Cloudflare. O SQLite é a opção mais simples para Node.js, mas exige um volume persistente gravável e backups operacionais do banco de dados.
SQLite
O SQLite usa o driver de banco de dados embutido do Node.js e é a opção mais simples para implantações Node.js.
import { sqlite } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
}),
],
});
Configuração
| Opção | Tipo | Descrição |
|---|---|---|
url | string | Caminho do arquivo com o prefixo file: |
Caminho do arquivo
A url deve começar com file::
// Relative path
database: sqlite({ url: "file:./data/emdash.db" });
// Absolute path
database: sqlite({ url: "file:/var/data/emdash.db" });
// From environment variable
database: sqlite({ url: `file:${process.env.DATABASE_PATH}` });
Write-ahead logging
O EmDash abre os bancos de dados SQLite no modo write-ahead logging (WAL). Enquanto o site está em execução, o SQLite mantém dois arquivos extras ao lado do banco de dados, como emdash.db-wal e emdash.db-shm. O processo precisa de acesso de escrita ao diretório do banco de dados para criá-los.
O arquivo -wal pode conter alterações confirmadas que ainda não estão no arquivo principal do banco de dados. Faça o backup com o comando de backup do SQLite em vez de copiar apenas o arquivo .db. Consulte Backup e recuperação SQLite.
O WAL exige memória compartilhada, então mantenha o banco de dados em um disco local ou volume de blocos, e não em um sistema de arquivos de rede como NFS ou SMB.
Cloudflare D1
O D1 é o banco de dados SQLite serverless do Cloudflare. Use-o ao implantar no Cloudflare Workers.
import { d1 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: d1({ binding: "DB" }),
}),
],
});
Configuração
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
binding | string | — | Nome do binding do D1 em wrangler.jsonc |
session | string | "disabled" | Modo de replicação de leitura (veja abaixo) |
bookmarkCookie | string | "__em_d1_bookmark" | Nome do cookie dos marcadores de sessão |
Binding Wrangler
wrangler.jsonc
{
"d1_databases": [
{
"binding": "DB",
"database_name": "emdash-db"
}
]
} wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "emdash-db" O Wrangler pode provisionar um banco de dados D1 ausente a partir deste binding durante a implantação. As migrações do EmDash são uma etapa separada. Siga Implantar no Cloudflare para o conjunto completo de bindings e Gerenciar as migrações do banco de dados do núcleo para o runbook de migrações.
Réplicas de leitura
O D1 oferece suporte à replicação de leitura para reduzir a latência de leitura em sites distribuídos globalmente. Quando habilitada, as consultas de leitura são roteadas para réplicas próximas em vez de sempre acessarem o banco de dados primário.
O EmDash usa a D1 Sessions API para gerenciar isso de forma transparente. Habilite com a opção session:
import { d1 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: d1({
binding: "DB",
session: "auto",
}),
}),
],
});
Modos de sessão
| Modo | Comportamento |
|---|---|
"disabled" | Sem sessões. Todas as consultas vão para o primário. Padrão. |
"auto" | As requisições anônimas leem da réplica mais próxima. Os usuários autenticados obtêm consistência read-your-writes por meio de cookies de marcador. |
"primary-first" | Como "auto", mas a primeira consulta sempre vai para o primário. Use em sites com gravações muito frequentes. |
Como funciona
- Os visitantes anônimos recebem
first-unconstrained— as leituras vão para a réplica mais próxima, com a menor latência. Como os usuários anônimos nunca gravam, eles não precisam de garantias de consistência. - Os usuários autenticados (editores, autores) recebem sessões baseadas em marcadores. Depois de uma gravação, um cookie de marcador garante que a próxima requisição veja pelo menos esse estado.
- As requisições de gravação (
POST,PUT,DELETE) sempre começam no banco de dados primário. - As consultas em tempo de build (coleções de conteúdo do Astro) ignoram completamente as sessões e usam diretamente o primário.
libSQL
O libSQL é um fork do SQLite que oferece suporte a conexões remotas. Use-o quando precisar de um banco de dados remoto sem o Cloudflare D1.
import { libsql } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
}),
}),
],
});
Configuração
| Opção | Tipo | Descrição |
|---|---|---|
url | string | URL do banco de dados (libsql://... ou file:...) |
authToken | string | Token de autenticação em tempo de execução para bancos remotos (opcional em local) |
migrationAuthTokenEnv | string | Nome da variável do token de migração (padrão TURSO_AUTH_TOKEN) |
Desenvolvimento local
Use um arquivo libSQL local durante o desenvolvimento:
database: libsql({ url: "file:./data.db" });
PostgreSQL
O PostgreSQL tem suporte em implantações Node.js que precisam de um banco de dados relacional completo.
import { postgres } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: postgres({
connectionString: process.env.DATABASE_URL,
}),
}),
],
});
Configuração
Você pode se conectar com uma string de conexão ou com parâmetros individuais:
// Connection string
database: postgres({
connectionString: "postgres://user:password@localhost:5432/emdash",
});
// Individual parameters
database: postgres({
host: "localhost",
port: 5432,
database: "emdash",
user: "emdash",
password: process.env.DB_PASSWORD,
ssl: true,
});
| Opção | Tipo | Descrição |
|---|---|---|
connectionString | string | URL de conexão do PostgreSQL |
host | string | Host do banco de dados |
port | number | Porta do banco de dados |
database | string | Nome do banco de dados |
user | string | Usuário do banco de dados |
password | string | Senha do banco de dados |
ssl | boolean | Habilitar SSL |
pool.min | number | Conexões mínimas do pool (padrão 0) |
pool.max | number | Conexões máximas do pool (padrão 10) |
pool.connectionTimeoutMillis | number | Espera máxima por conexão (padrão do pg: 0, sem timeout) |
pool.idleTimeoutMillis | number | Tempo de vida dos clientes ociosos (padrão do pg: 10.000 ms) |
migrationConnectionStringEnv | string | Nome da variável da string de conexão de migração (padrão DATABASE_URL) |
Defina pool.connectionTimeoutMillis com um valor diferente de zero para limitar quanto tempo uma requisição espera quando o PostgreSQL está inacessível ou nenhuma conexão do pool fica disponível. Defina pool.idleTimeoutMillis como 0 para manter os clientes ociosos abertos até o pool ser fechado. Omitir qualquer uma das opções preserva o padrão do pg.
Requisitos da função do banco
O EmDash cria e atualiza as próprias tabelas do PostgreSQL. As migrações do núcleo criam e alteram as tabelas do sistema e das coleções, os tipos de conteúdo criam tabelas ec_*, e adicionar ou remover um campo altera a tabela da coleção dele. A função do PostgreSQL configurada precisa, portanto, de autoridade sobre o esquema durante toda a vida do site, e não só durante a configuração inicial.
Use uma única função canônica para o EmDash. Ela precisa de:
CONNECTno banco de dados;USAGEeCREATEno esquema ativo;- a propriedade de cada tabela e função do EmDash, diretamente ou por meio da participação com
INHERITna função proprietária; e SELECT,INSERT,UPDATEeDELETEnessas tabelas.
Ela não precisa ser superusuário, ter CREATEDB ou CREATEROLE, nem criar extensões. O PostgreSQL não oferece uma concessão ALTER ou DROP de tabela: essas operações pertencem ao proprietário do objeto e às funções que herdam seus privilégios. Conceder ALL em uma tabela a outra função não torna essa função proprietária. O EmDash não executa SET ROLE, então uma participação configurada sem herança não é suficiente.
A maioria das instalações pode usar o esquema existente do banco de dados, normalmente public. É a opção mais simples quando o banco de dados é dedicado ao EmDash. Nos exemplos abaixo, emdash_app é a função de login na string de conexão do EmDash; use uma função existente do seu provedor ou crie um login dedicado. Conceda acesso a ela com uma conexão administrativa, substituindo os nomes do seu banco de dados, esquema e função:
GRANT CONNECT ON DATABASE app TO emdash_app;
GRANT USAGE, CREATE ON SCHEMA public TO emdash_app;
Essas concessões permitem que a função crie novos objetos. Elas não mudam o proprietário das tabelas existentes; use o runbook de reparo da propriedade do PostgreSQL quando um site existente tiver proprietários mistos.
O EmDash usa o current_schema() ativo do PostgreSQL. Ele não cria um esquema nem define search_path, então verifique a conexão antes da implantação:
SELECT
current_database(),
session_user,
current_user,
current_schema(),
current_setting('search_path');
Opcional: usar um esquema dedicado
Use um esquema dedicado quando o EmDash compartilhar um banco de dados com outra aplicação ou quando você quiser que os objetos dele fiquem isolados de public. Isso é opcional e é mais fácil de configurar antes da primeira configuração do EmDash. Um banco de dados dedicado ao EmDash não precisa de um esquema separado.
Supondo que a função canônica emdash_app já exista, crie e selecione o esquema dela com uma conexão administrativa:
GRANT CONNECT ON DATABASE app TO emdash_app;
CREATE SCHEMA emdash AUTHORIZATION emdash_app;
ALTER ROLE emdash_app IN DATABASE app SET search_path = emdash;
Isso não move uma instalação existente de public nem repara uma propriedade mista. Os sites existentes devem manter o esquema atual e usar, em vez disso, o runbook de reparo da propriedade do PostgreSQL.
Pool de conexões
O adaptador usa pg.Pool. Ajuste o tamanho do pool de acordo com a sua implantação:
database: postgres({
connectionString: process.env.DATABASE_URL,
pool: { min: 2, max: 20 },
});
Hyperdrive
Use o adaptador hyperdrive() para executar o EmDash no Cloudflare Workers com um banco de dados PostgreSQL existente — ou compatível com Postgres (por exemplo, PlanetScale Postgres). O Hyperdrive agrupa e acelera a conexão pela rede do Cloudflare; o dialeto PostgreSQL do EmDash executa as consultas.
import { hyperdrive, r2 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: hyperdrive({ binding: "HYPERDRIVE" }),
storage: r2({ binding: "MEDIA" }),
}),
],
});
Requisitos
pg >= 8.16.3instalado no seu site (pnpm add pg)compatibility_flags: ["nodejs_compat"]compatibility_date >= "2024-09-23"
Configuração
Primeiro, prepare a função do PostgreSQL. Depois, crie a configuração do Hyperdrive com a string de conexão dessa função e adicione o binding à sua configuração do Wrangler:
wrangler hyperdrive create emdash-db \
--connection-string "postgres://user:password@host/db?sslmode=verify-full" \
--caching-disabled
wrangler.jsonc
{
"hyperdrive": [
{
"binding": "HYPERDRIVE",
"id": "<your-hyperdrive-id>"
}
]
} wrangler.toml
[[hyperdrive]]
binding = "HYPERDRIVE"
id = "<your-hyperdrive-id>" Configuração
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
binding | string | "HYPERDRIVE" | Nome do binding primário do Hyperdrive (cache desativado) |
cachedBinding | string | — | Binding opcional com cache ativado para leituras anônimas (veja abaixo) |
preferUncachedAfterWriteMs | number | 60000* | Após a publicação de conteúdo, prefere binding por esta quantidade de ms nas leituras públicas anônimas (iguale ao max_age do Hyperdrive) |
migrationConnectionStringEnv | string | CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> | Variável de ambiente que contém a URL direta da origem PostgreSQL para emdash migrate |
max | number | 5 | Tamanho máximo do pool de conexões com o Hyperdrive dentro do Worker |
*O padrão 60000 só se aplica quando cachedBinding está definido; caso contrário, é ignorado.
Servir leituras anônimas do cache
Por padrão, você desativa totalmente o cache do Hyperdrive, porque o administrador e as gravações precisam de consistência de leitura após gravação. Mas as requisições públicas anônimas que usam GET ou HEAD podem tolerar uma janela curta de desatualização. Se essa troca for aceitável, execute duas configurações do Hyperdrive sobre o mesmo banco de dados: uma com o cache desativado (o binding primário) e outra com o cache ativado (cachedBinding). O EmDash roteia essas requisições públicas anônimas pelo binding com cache e todas as outras pelo primário sem cache.
# Primary — caching OFF (used by admin, auth'd requests, writes, migrations)
wrangler hyperdrive create emdash-db \
--connection-string "postgres://user:password@host/db?sslmode=verify-full" \
--caching-disabled
# Cached — SAME database role and connection string, caching ON
wrangler hyperdrive create emdash-db-cached \
--connection-string "postgres://user:password@host/db?sslmode=verify-full"
{
"hyperdrive": [
{ "binding": "HYPERDRIVE", "id": "<caching-disabled-id>" },
{ "binding": "HYPERDRIVE_CACHED", "id": "<caching-enabled-id>" }
]
}
database: hyperdrive({ binding: "HYPERDRIVE", cachedBinding: "HYPERDRIVE_CACHED" });
Este é o padrão de duas configurações que o Cloudflare documenta para o cache. O EmDash decide qual binding usar em cada requisição:
- Leituras anônimas de caminhos do site público (
GET/HEAD, sem sessão, fora de/_emdash) →cachedBindingcom cache ativado, exceto em uma janela curta após a publicação de conteúdo (60 s por padrão; definapreferUncachedAfterWriteMscom omax_agedo seu Hyperdrive), quando o EmDash prefere obindingsem cache para que uma reconstrução não possa repopular os caches de borda/objeto com resultados do Hyperdrive ainda desatualizados. - Requisições autenticadas (editores, autores) →
bindingsem cache. - Requisições de mutação (
POST,PUT,PATCH,DELETE, inclusive as anônimas) →bindingsem cache. - Qualquer requisição sob
/_emdash(administrador, configuração, autenticação, APIs internas), mesmo umGETanônimo →bindingsem cache. - Migrações em tempo de execução e cold start → sempre o
bindingprimário. - Migrações gerenciadas pela implantação → conectam-se diretamente à origem PostgreSQL usando
migrationConnectionStringEnv; nunca usam nenhum dos dois bindings do Hyperdrive.
Opcional: usar uma função em cache separada
As migrações, a configuração, as requisições autenticadas e as requisições de gravação explícitas sempre usam o binding primário. Uma função separada para cachedBinding não precisa de propriedade do esquema nem de CREATE, mas precisa de CONNECT, USAGE no esquema e SELECT em todas as tabelas usadas pelo site público.
As requisições públicas anônimas GET e HEAD também podem registrar acertos de redirecionamento e erros 404. Para preservar esses recursos, a função em cache precisa adicionalmente de UPDATE em _emdash_redirects e de SELECT, INSERT, UPDATE e DELETE em _emdash_404_log. Plugins ou código da aplicação que gravem durante um GET ou HEAD público podem exigir mais. Use a mesma função para os dois bindings, a menos que você tenha testado o site com uma função em cache restrita.
Adicione a função em cache depois que o EmDash concluir as migrações iniciais. Os exemplos abaixo usam o esquema opcional emdash; substitua pelo seu esquema ativo, como public. Crie o login e as configurações do banco de dados com a função administrativa do seu provedor:
CREATE ROLE emdash_cached LOGIN PASSWORD 'replace-with-a-secret';
GRANT CONNECT ON DATABASE app TO emdash_cached;
ALTER ROLE emdash_cached IN DATABASE app SET search_path = emdash;
Depois, conecte-se como emdash_app, o proprietário do esquema e das tabelas, para conceder acesso às tabelas existentes e futuras:
GRANT USAGE ON SCHEMA emdash TO emdash_cached;
GRANT SELECT ON ALL TABLES IN SCHEMA emdash TO emdash_cached;
GRANT UPDATE ON emdash._emdash_redirects TO emdash_cached;
GRANT SELECT, INSERT, UPDATE, DELETE ON emdash._emdash_404_log TO emdash_cached;
ALTER DEFAULT PRIVILEGES IN SCHEMA emdash
GRANT SELECT ON TABLES TO emdash_cached;
Conecte-se com as duas funções e verifique se elas informam o mesmo current_database() e current_schema() antes de habilitar cachedBinding. Em um esquema compartilhado, GRANT SELECT ON ALL TABLES também expõe tabelas não relacionadas. Em vez disso, conceda acesso a cada tabela do EmDash individualmente e atualize essas concessões quando coleções ou outros objetos do esquema forem adicionados.
Migrações principais
O EmDash executa as migrações principais automaticamente por padrão para todos os dialetos com suporte. O build e a sincronização do Astro também geram um .emdash/migrations.json validado e sem segredos, que o emdash migrate pode aplicar antes da implantação. SQLite, libSQL, PostgreSQL, D1 e a origem PostgreSQL direta por trás do Hyperdrive têm executores de implantação.
Consulte Gerenciar as migrações do banco de dados do núcleo para as credenciais do destino, a serialização em CI, a política de execução auto/check/manual e a recuperação de registros desconhecidos ou gravações ambíguas do D1.
No PostgreSQL, as migrações em tempo de execução rodam pela conexão configurada; as migrações em tempo de execução do Hyperdrive sempre usam o binding primário dele. As migrações do Hyperdrive gerenciadas pela implantação se conectam diretamente à origem PostgreSQL. As migrações principais podem criar tabelas, índices e funções, alterar ou remover colunas e restrições e atualizar linhas existentes. Uma função que consegue se conectar e modificar linhas, mas não é proprietária dos objetos existentes do EmDash, não é suficiente. O assistente de configuração não consegue reparar privilégios de banco de dados ausentes, porque as migrações em tempo de execução rodam antes da configuração.
Até que o assistente de configuração seja concluído, o EmDash também aplica uma única vez, na primeira requisição, o esquema e a estrutura de um arquivo seed. O conteúdo de exemplo do seed só é aplicado quando um administrador o escolhe no assistente de configuração. O seed é lido de .emdash/seed.json, do caminho em package.json#emdash.seed ou de seed/seed.json — o que for encontrado primeiro — e incorporado ao build em tempo de compilação. Se nenhum estiver presente, um seed padrão embutido é usado. As inicializações seguintes contra um banco de dados existente deixam o conteúdo dele intacto.
Usar bancos separados para ambientes separados
Dê ao desenvolvimento, ao preview, ao staging e à produção o próprio banco de dados. Uma implantação de preview apontada para a produção pode executar migrações principais ou comandos destrutivos do modelo de conteúdo contra dados em produção.
No Cloudflare, defina cada binding do D1 ou do Hyperdrive sob o ambiente Wrangler correspondente e passe --env aos comandos do Wrangler. No Node.js, injete uma URL de banco de dados diferente em cada ambiente de execução. Mantenha as credenciais em segredos de runtime, não em astro.config.mjs.