Skip to content

Database Options

EmDash supports multiple database backends. Choose based on your deployment target.

DatabaseBest ForDeployment
D1Cloudflare WorkersEdge, globally distributed
PostgreSQLProduction Node.jsAny platform with Postgres
libSQLRemote databasesEdge or Node.js
SQLiteNode.js, local devSingle server

D1 is Cloudflare’s serverless SQLite database. Use it when deploying to Cloudflare Workers.

astro.config.mjs
import { d1 } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: d1({ binding: "DB" }),
}),
],
});
OptionTypeDefaultDescription
bindingstringD1 binding name from wrangler.jsonc
sessionstring"disabled"Read replication mode (see below)
bookmarkCookiestring"__ec_d1_bookmark"Cookie name for session bookmarks
{
"d1_databases": [
{
"binding": "DB",
"database_name": "emdash-db",
"database_id": "your-database-id"
}
]
}
Terminal window
wrangler d1 create emdash-db

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:

astro.config.mjs
import { d1 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: d1({
binding: "DB",
session: "auto",
}),
}),
],
});
ModeBehavior
"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.
  • 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 is a fork of SQLite that supports remote connections. Use it when you need a remote database without Cloudflare D1.

astro.config.mjs
import { libsql } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
}),
}),
],
});
OptionTypeDescription
urlstringDatabase URL (libsql://... or file:...)
authTokenstringAuth token for remote databases (optional for local)

Use a local libSQL file during development:

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

PostgreSQL is supported for Node.js deployments that need a full relational database.

astro.config.mjs
import { postgres } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: postgres({
connectionString: process.env.DATABASE_URL,
}),
}),
],
});

You can connect with a connection string or individual parameters:

// 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,
});
OptionTypeDescription
connectionStringstringPostgreSQL connection URL
hoststringDatabase host
portnumberDatabase port
databasestringDatabase name
userstringDatabase user
passwordstringDatabase password
sslbooleanEnable SSL
pool.minnumberMinimum pool connections (default 0)
pool.maxnumberMaximum pool connections (default 10)

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 with better-sqlite3 is the simplest option for Node.js deployments.

astro.config.mjs
import { sqlite } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
}),
],
});
OptionTypeDescription
urlstringFile path with file: prefix

The url must start with 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}` });

EmDash handles migrations automatically for SQLite, libSQL, and PostgreSQL. For D1, run migrations via Wrangler.

Terminal window
npx emdash init --database ./data.db

This command:

  1. Creates the database file if needed
  2. Runs any pending migrations
  3. Reports the current migration status

Migrations are bundled with EmDash. To run them manually:

Terminal window
# SQLite/libSQL - migrations run automatically
# D1 - run via wrangler
wrangler d1 migrations apply DB

Use different databases per environment:

astro.config.mjs
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" });