Database Options
EmDash supports multiple database backends. Choose based on your deployment target.
Overview
Section titled “Overview”| Database | Best For | Deployment |
|---|---|---|
| D1 | Cloudflare Workers | Edge, globally distributed |
| PostgreSQL | Production Node.js | Any platform with Postgres |
| libSQL | Remote databases | Edge or Node.js |
| SQLite | Node.js, local dev | Single server |
Cloudflare D1
Section titled “Cloudflare D1”D1 is Cloudflare’s serverless SQLite database. Use it when deploying to Cloudflare Workers.
import { d1 } from "emdash/db";
export default defineConfig({ integrations: [ emdash({ database: d1({ binding: "DB" }), }), ],});Configuration
Section titled “Configuration”| Option | Type | Default | Description |
|---|---|---|---|
binding | string | — | D1 binding name from wrangler.jsonc |
session | string | "disabled" | Read replication mode (see below) |
bookmarkCookie | string | "__ec_d1_bookmark" | Cookie name for session bookmarks |
{ "d1_databases": [ { "binding": "DB", "database_name": "emdash-db", "database_id": "your-database-id" } ]}[[d1_databases]]binding = "DB"database_name = "emdash-db"database_id = "your-database-id"Create a D1 Database
Section titled “Create a D1 Database”wrangler d1 create emdash-dbRead Replicas
Section titled “Read Replicas”D1 supports read replication to lower read latency for globally distributed sites. When enabled, read queries are routed to nearby replicas instead of always hitting the primary database.
EmDash uses the D1 Sessions API to manage this transparently. Enable it with the session option:
import { d1 } from "@emdash-cms/cloudflare";
export default defineConfig({ integrations: [ emdash({ database: d1({ binding: "DB", session: "auto", }), }), ],});Session Modes
Section titled “Session Modes”| Mode | Behavior |
|---|---|
"disabled" | No sessions. All queries go to primary. Default. |
"auto" | Anonymous requests read from the nearest replica. Authenticated users get read-your-writes consistency via bookmark cookies. |
"primary-first" | Like "auto", but the first query always goes to the primary. Use for sites with very frequent writes. |
How It Works
Section titled “How It Works”- Anonymous visitors get
first-unconstrained— reads go to the nearest replica for the lowest latency. Since anonymous users never write, they don’t need consistency guarantees. - Authenticated users (editors, authors) get bookmark-based sessions. After a write, a bookmark cookie ensures the next request sees at least that state.
- Write requests (
POST,PUT,DELETE) always start at the primary database. - Build-time queries (Astro content collections) bypass sessions entirely and use the primary directly.
libSQL
Section titled “libSQL”libSQL is a fork of SQLite that supports remote connections. Use it when you need a remote database without 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, }), }), ],});Configuration
Section titled “Configuration”| Option | Type | Description |
|---|---|---|
url | string | Database URL (libsql://... or file:...) |
authToken | string | Auth token for remote databases (optional for local) |
Local Development
Section titled “Local Development”Use a local libSQL file during development:
database: libsql({ url: "file:./data.db" });PostgreSQL
Section titled “PostgreSQL”PostgreSQL is supported for Node.js deployments that need a full relational database.
import { postgres } from "emdash/db";
export default defineConfig({ integrations: [ emdash({ database: postgres({ connectionString: process.env.DATABASE_URL, }), }), ],});Configuration
Section titled “Configuration”You can connect with a connection string or individual parameters:
// Connection stringdatabase: postgres({ connectionString: "postgres://user:password@localhost:5432/emdash",});
// Individual parametersdatabase: postgres({ host: "localhost", port: 5432, database: "emdash", user: "emdash", password: process.env.DB_PASSWORD, ssl: true,});| Option | Type | Description |
|---|---|---|
connectionString | string | PostgreSQL connection URL |
host | string | Database host |
port | number | Database port |
database | string | Database name |
user | string | Database user |
password | string | Database password |
ssl | boolean | Enable SSL |
pool.min | number | Minimum pool connections (default 0) |
pool.max | number | Maximum pool connections (default 10) |
Connection Pooling
Section titled “Connection Pooling”The adapter uses pg.Pool under the hood. Tune pool size based on your deployment:
database: postgres({ connectionString: process.env.DATABASE_URL, pool: { min: 2, max: 20 },});SQLite
Section titled “SQLite”SQLite with better-sqlite3 is the simplest option for Node.js deployments.
import { sqlite } from "emdash/db";
export default defineConfig({ integrations: [ emdash({ database: sqlite({ url: "file:./data.db" }), }), ],});Configuration
Section titled “Configuration”| Option | Type | Description |
|---|---|---|
url | string | File path with file: prefix |
File Path
Section titled “File Path”The url must start with file::
// Relative pathdatabase: sqlite({ url: "file:./data/emdash.db" });
// Absolute pathdatabase: sqlite({ url: "file:/var/data/emdash.db" });
// From environment variabledatabase: sqlite({ url: `file:${process.env.DATABASE_PATH}` });Migrations
Section titled “Migrations”EmDash handles migrations automatically for SQLite, libSQL, and PostgreSQL. For D1, run migrations via Wrangler.
Check Migration Status
Section titled “Check Migration Status”npx emdash init --database ./data.dbThis command:
- Creates the database file if needed
- Runs any pending migrations
- Reports the current migration status
Migration Files
Section titled “Migration Files”Migrations are bundled with EmDash. To run them manually:
# SQLite/libSQL - migrations run automatically
# D1 - run via wranglerwrangler d1 migrations apply DBEnvironment-Based Configuration
Section titled “Environment-Based Configuration”Use different databases per environment:
import { d1, sqlite, libsql, postgres } from "emdash/db";
const database = import.meta.env.PROD ? d1({ binding: "DB" }) : sqlite({ url: "file:./data.db" });
export default defineConfig({ integrations: [emdash({ database })],});Or switch based on environment variables:
const database = process.env.DATABASE_URL ? postgres({ connectionString: process.env.DATABASE_URL }) : sqlite({ url: "file:./data.db" });