Opciones de base de datos

En esta página

EmDash soporta múltiples backends de base de datos. Elige según tu objetivo de despliegue.

Resumen

Base de datosIdeal paraDespliegue
D1Cloudflare WorkersEdge, distribuido globalmente
HyperdrivePostgreSQL en Cloudflare WorkersEdge, Postgres existente
PostgreSQLProducción Node.jsCualquier plataforma con Postgres
libSQLBases de datos remotasEdge o Node.js
SQLiteNode.js, desarrollo localServidor único

Cloudflare D1

D1 es la base de datos SQLite serverless de Cloudflare. Úsala al desplegar en Cloudflare Workers.

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

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

Configuración

OpciónTipoPredeterminadoDescripción
bindingstringNombre del binding D1 de wrangler.jsonc
sessionstring"disabled"Modo de replicación de lectura (ver abajo)
bookmarkCookiestring"__em_d1_bookmark"Nombre de cookie para bookmarks de sesión

Configuración

wrangler.jsonc

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

wrangler.toml

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

Read Replicas

D1 soporta replicación de lectura para reducir la latencia de lectura en sitios distribuidos globalmente. Cuando está habilitada, las consultas de lectura se enrutan a réplicas cercanas en lugar de acceder siempre a la base de datos primaria.

EmDash usa la API de Sesiones D1 para gestionar esto de forma transparente. Habilítalo con la opción session:

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

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

Modos de sesión

ModoComportamiento
"disabled"Sin sesiones. Todas las consultas van a la primaria. Predeterminado.
"auto"Las solicitudes anónimas leen de la réplica más cercana. Los usuarios autenticados obtienen consistencia read-your-writes via cookies de bookmark.
"primary-first"Como "auto", pero la primera consulta siempre va a la primaria. Para sitios con escrituras muy frecuentes.

Cómo funciona

  • Visitantes anónimos obtienen first-unconstrained — las lecturas van a la réplica más cercana para la menor latencia. Como los usuarios anónimos nunca escriben, no necesitan garantías de consistencia.
  • Usuarios autenticados (editores, autores) obtienen sesiones basadas en bookmarks. Después de una escritura, una cookie de bookmark asegura que la siguiente solicitud vea al menos ese estado.
  • Solicitudes de escritura (POST, PUT, DELETE) siempre comienzan en la base de datos primaria.
  • Consultas en tiempo de build (Astro content collections) omiten las sesiones completamente y usan la primaria directamente.

libSQL

libSQL es un fork de SQLite que soporta conexiones remotas. Úsalo cuando necesites una base de datos remota sin 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,
			}),
		}),
	],
});

Configuración

OpciónTipoDescripción
urlstringURL de base de datos (libsql://... o file:...)
authTokenstringToken de autenticación para bases remotas (opcional para local)

Desarrollo local

Usa un archivo libSQL local durante el desarrollo:

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

PostgreSQL

PostgreSQL está soportado para despliegues Node.js que necesitan una base de datos relacional completa.

import { postgres } from "emdash/db";

export default defineConfig({
	integrations: [
		emdash({
			database: postgres({
				connectionString: process.env.DATABASE_URL,
			}),
		}),
	],
});

Configuración

Puedes conectar con un string de conexión o parámetros individuales:

// String de conexión
database: postgres({
	connectionString: "postgres://user:password@localhost:5432/emdash",
});

// Parámetros individuales
database: postgres({
	host: "localhost",
	port: 5432,
	database: "emdash",
	user: "emdash",
	password: process.env.DB_PASSWORD,
	ssl: true,
});
OpciónTipoDescripción
connectionStringstringURL de conexión PostgreSQL
hoststringHost de la base de datos
portnumberPuerto de la base de datos
databasestringNombre de la base de datos
userstringUsuario de la base de datos
passwordstringContraseña de la base de datos
sslbooleanHabilitar SSL
pool.minnumberConexiones mínimas del pool (predeterminado 0)
pool.maxnumberConexiones máximas del pool (predeterminado 10)

Connection Pooling

El adaptador usa pg.Pool internamente. Ajusta el tamaño del pool según tu despliegue:

database: postgres({
	connectionString: process.env.DATABASE_URL,
	pool: { min: 2, max: 20 },
});

Hyperdrive

Usa el adaptador hyperdrive() para ejecutar EmDash en Cloudflare Workers respaldado por una base de datos PostgreSQL existente — o compatible con Postgres (ej. PlanetScale Postgres). Hyperdrive agrupa y acelera la conexión a través de la red de Cloudflare; el dialecto PostgreSQL de EmDash ejecuta las 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 en tu sitio (pnpm add pg)
  • compatibility_flags: ["nodejs_compat"]
  • compatibility_date >= "2024-09-23"

Configuración

Crea la configuración Hyperdrive y añade el binding a tu configuración 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>"

Configuración

OpciónTipoPredeterminadoDescripción
bindingstring"HYPERDRIVE"Binding Hyperdrive primario (caché deshabilitada)
cachedBindingstringBinding opcional con caché habilitada para lecturas anónimas
maxnumber5Tamaño máximo del pool de conexiones in-Worker a Hyperdrive

Servir lecturas anónimas desde caché

Por defecto deshabilitas el caché de Hyperdrive completamente, porque el admin y las escrituras necesitan consistencia read-after-write. Pero las lecturas públicas anónimas — sin sesión, sin escritura — pueden tolerar una corta ventana de desactualización. Si ese compromiso es aceptable, ejecuta dos configuraciones Hyperdrive sobre la misma base de datos: una con caché desactivada (el binding primario) y una con caché activada (cachedBinding). EmDash entonces enruta solicitudes de lectura anónimas a través del binding con caché mientras cada solicitud autenticada y cada escritura permanece en el primario sin caché, preservando la consistencia read-after-write.

# Primario — caché DESACTIVADA (usada por admin, solicitudes auth., escrituras, migraciones)
wrangler hyperdrive create emdash-db \
  --connection-string "postgres://user:password@host/db?sslmode=verify-full" \
  --caching-disabled

# Con caché — MISMO string de conexión, caché ACTIVADA (solo lecturas 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 es el patrón de dos configuraciones que Cloudflare documenta para caché. EmDash decide qué binding usar por solicitud:

  • Lecturas anónimas de rutas del sitio público (GET/HEAD, sin sesión, no bajo /_emdash) → cachedBinding con caché.
  • Solicitudes autenticadas (editores, autores) → binding sin caché.
  • Escrituras (POST, PUT, DELETE, incluyendo anónimas) → binding sin caché.
  • Cualquier solicitud bajo /_emdash (admin, setup, auth, APIs internas), incluso un GET anónimo → binding sin caché.
  • Migraciones y arranque en frío → siempre el binding primario.

SQLite

SQLite con better-sqlite3 es la opción más simple para despliegues Node.js.

import { sqlite } from "emdash/db";

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

Configuración

OpciónTipoDescripción
urlstringRuta de archivo con prefijo file:

Ruta de archivo

La url debe comenzar con file::

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

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

// Desde variable de entorno
database: sqlite({ url: `file:${process.env.DATABASE_PATH}` });

Migraciones

EmDash ejecuta migraciones automáticamente en la primera solicitud, para cada dialecto soportado (D1, SQLite, libSQL, PostgreSQL). Las migraciones están empaquetadas con el paquete emdash e integradas en tu build.

Si la base de datos está vacía (sin colecciones) y el asistente de configuración no se ha completado, EmDash también aplica un archivo seed en el primer arranque. El seed se lee de .emdash/seed.json, la ruta en package.json#emdash.seed, o seed/seed.json — el primero encontrado — y se integra en el build en tiempo de compilación. Si ninguno está presente, se usa un seed predeterminado integrado. Los arranques posteriores contra una base de datos existente dejan su contenido intacto.

Configuración basada en entorno

Usa diferentes bases de datos por entorno:

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 })],
});

La elección también puede basarse en una variable de entorno en vez del modo de build:

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