Opções de banco de dados

Nesta página

O EmDash suporta vários backends de banco de dados. Escolha com base no seu alvo de deploy.

Visão geral

Banco de dadosIdeal paraDeploy
D1Cloudflare WorkersEdge, distribuído globalmente
HyperdrivePostgreSQL no Cloudflare WorkersEdge, Postgres existente
PostgreSQLProdução Node.jsQualquer plataforma com Postgres
libSQLBancos de dados remotosEdge ou Node.js
SQLiteNode.js, desenvolvimento localServidor único

Cloudflare D1

D1 é o banco de dados SQLite serverless da Cloudflare. Use ao fazer deploy no Cloudflare Workers.

import { d1 } from "@emdash-cms/cloudflare";

export default defineConfig({
	integrations: [
		emdash({
			database: d1({ binding: "DB" }),
		}),
	],
});

Configuração

OpçãoTipoPadrãoDescrição
bindingstringNome do binding D1 do wrangler.jsonc
sessionstring"disabled"Modo de replicação de leitura (veja abaixo)
bookmarkCookiestring"__em_d1_bookmark"Nome do cookie para bookmarks de sessão

Setup

wrangler.jsonc

{
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "emdash-db"
    }
  ]
}

wrangler.toml

[[d1_databases]]
binding = "DB"
database_name = "emdash-db"

Read Replicas

O D1 suporta 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 consultar o banco de dados primário.

O EmDash usa a API D1 Sessions 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

ModoComportamento
"disabled"Sem sessões. Todas as consultas vão para o primário. Padrão.
"auto"Requisições anônimas leem da réplica mais próxima. Usuários autenticados obtêm consistência read-your-writes via cookies de bookmark.
"primary-first"Como "auto", mas a primeira consulta sempre vai para o primário. Para sites com escritas muito frequentes.

Como funciona

  • Visitantes anônimos obtêm first-unconstrained — leituras vão para a réplica mais próxima para menor latência. Como usuários anônimos nunca escrevem, não precisam de garantias de consistência.
  • Usuários autenticados (editores, autores) obtêm sessões baseadas em bookmark. Após uma escrita, um cookie de bookmark garante que a próxima requisição veja pelo menos aquele estado.
  • Requisições de escrita (POST, PUT, DELETE) sempre começam no banco de dados primário.
  • Consultas em tempo de build (Astro content collections) contornam sessões completamente e usam diretamente o primário.

libSQL

libSQL é um fork do SQLite que suporta conexões remotas. Use quando precisar de um banco de dados remoto sem 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çãoTipoDescrição
urlstringURL do banco de dados (libsql://... ou file:...)
authTokenstringToken de autenticação para bancos remotos (opcional localmente)

Desenvolvimento local

Use um arquivo libSQL local durante o desenvolvimento:

database: libsql({ url: "file:./data.db" });

PostgreSQL

PostgreSQL é suportado para deploys Node.js que necessitam 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 conectar com uma string de conexão ou parâmetros individuais:

// String de conexão
database: postgres({
	connectionString: "postgres://user:password@localhost:5432/emdash",
});

// Parâmetros individuais
database: postgres({
	host: "localhost",
	port: 5432,
	database: "emdash",
	user: "emdash",
	password: process.env.DB_PASSWORD,
	ssl: true,
});
OpçãoTipoDescrição
connectionStringstringURL de conexão PostgreSQL
hoststringHost do banco de dados
portnumberPorta do banco de dados
databasestringNome do banco de dados
userstringUsuário do banco de dados
passwordstringSenha do banco de dados
sslbooleanHabilitar SSL
pool.minnumberConexões mínimas do pool (padrão 0)
pool.maxnumberConexões máximas do pool (padrão 10)

Pool de conexões

O adaptador usa pg.Pool internamente. Ajuste o tamanho do pool baseado no seu deploy:

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 (ex. PlanetScale Postgres). O Hyperdrive faz pool e acelera a conexão pela rede 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.3 instalado no seu site (pnpm add pg)
  • compatibility_flags: ["nodejs_compat"]
  • compatibility_date >= "2024-09-23"

Setup

Crie a configuração Hyperdrive e adicione o binding à sua configuração 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çãoTipoPadrãoDescrição
bindingstring"HYPERDRIVE"Binding Hyperdrive primário (cache desabilitado)
cachedBindingstringBinding opcional com cache habilitado para leituras anônimas
maxnumber5Tamanho máximo do pool de conexões in-Worker para o Hyperdrive

Servir leituras anônimas do cache

Por padrão, você desabilita completamente o cache do Hyperdrive porque admin e escritas precisam de consistência read-after-write. Mas leituras públicas anônimas — sem sessão, sem escrita — podem tolerar uma janela curta de dados obsoletos. Se esse tradeoff é aceitável, execute duas configurações Hyperdrive sobre o mesmo banco de dados: uma com cache desabilitado (o binding primário) e uma com cache habilitado (cachedBinding). O EmDash roteia as requisições de leitura anônimas pelo binding com cache enquanto todas as requisições autenticadas e escritas permanecem no primário sem cache, preservando a consistência read-after-write.

# Primário — cache DESABILITADO (admin, requisições auth., escritas, migrações)
wrangler hyperdrive create emdash-db \
  --connection-string "postgres://user:password@host/db?sslmode=verify-full" \
  --caching-disabled

# Com cache — MESMA string de conexão, cache HABILITADO (apenas leituras anônimas)
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 a Cloudflare documenta para cache. O EmDash decide qual binding usar por requisição:

  • Leituras anônimas dos caminhos do site público (GET/HEAD, sem sessão, não sob /_emdash) → cachedBinding com cache.
  • Requisições autenticadas (editores, autores) → binding sem cache.
  • Escritas (POST, PUT, DELETE, incluindo anônimas) → binding sem cache.
  • Qualquer requisição sob /_emdash (admin, setup, auth, APIs internas), mesmo um GET anônimo → binding sem cache.
  • Migrações e cold start → sempre o binding primário.

SQLite

SQLite com better-sqlite3 é a opção mais simples para deploys Node.js.

import { sqlite } from "emdash/db";

export default defineConfig({
	integrations: [
		emdash({
			database: sqlite({ url: "file:./data.db" }),
		}),
	],
});

Configuração

OpçãoTipoDescrição
urlstringCaminho do arquivo com prefixo file:

Caminho do arquivo

A url deve começar com file::

// Caminho relativo
database: sqlite({ url: "file:./data/emdash.db" });

// Caminho absoluto
database: sqlite({ url: "file:/var/data/emdash.db" });

// De variável de ambiente
database: sqlite({ url: `file:${process.env.DATABASE_PATH}` });

Migrações

O EmDash executa migrações automaticamente na primeira requisição, para cada dialeto suportado (D1, SQLite, libSQL, PostgreSQL). As migrações estão empacotadas no pacote emdash e embutidas no seu build.

Se o banco de dados está vazio (sem collections) e o assistente de setup não foi completado, o EmDash também aplica um arquivo seed na primeira inicialização. O seed é lido de .emdash/seed.json, do caminho em package.json#emdash.seed, ou de seed/seed.json — o primeiro encontrado — e embutido no build em tempo de compilação. Se nenhum estiver presente, um seed padrão embutido é usado. Inicializações subsequentes contra um banco de dados existente deixam seu conteúdo intacto.

Configuração baseada em ambiente

Use bancos de dados diferentes por ambiente:

import { sqlite, libsql, postgres } from "emdash/db";
import { d1 } from "@emdash-cms/cloudflare";

const database = import.meta.env.PROD ? d1({ binding: "DB" }) : sqlite({ url: "file:./data.db" });

export default defineConfig({
	integrations: [emdash({ database })],
});

A escolha também pode ser baseada em variável de ambiente em vez do modo de build:

const database = process.env.DATABASE_URL
	? postgres({ connectionString: process.env.DATABASE_URL })
	: sqlite({ url: "file:./data.db" });