Eine Datenbank auswählen

Auf dieser Seite

Wählen Sie für jedes Deployment einen Datenbankadapter. Die Datenbank enthält das Inhaltsmodell, Einträge, Benutzer, Einstellungen und Plugin-Daten. Medien-Binärdateien gehören in ein separates Speicher-Backend.

Überblick

DatenbankVerwenden Sie sie, wennLaufzeit
SQLiteEin Node.js-Prozess hat einen persistenten DatenträgerNode.js oder lokale Entwicklung
D1Die Site auf Cloudflare Workers läuft und Cloudflare SQL verwenden sollCloudflare Workers
HyperdriveDie Site auf Workers läuft und einen vorhandenen PostgreSQL-Origin nutzen mussCloudflare Workers
PostgreSQLMehrere Node.js-Prozesse eine gemeinsame Datenbank benötigenNode.js
libSQLEin Node.js-Deployment eine entfernte, SQLite-kompatible Datenbank brauchtNode.js

D1 ist der Standard für die Cloudflare-Vorlagen. SQLite ist die einfachste Option für Node.js, erfordert aber ein beschreibbares persistentes Volume und betriebliche Datenbank-Backups.

SQLite

SQLite verwendet den in Node.js eingebauten Datenbanktreiber und ist die einfachste Option für Node.js-Deployments.

import { sqlite } from "emdash/db";

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

Konfiguration

OptionTypBeschreibung
urlstringDateipfad mit Präfix file:

Dateipfad

Die url muss mit file: beginnen:

// 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

EmDash öffnet SQLite-Datenbanken im Write-Ahead-Logging-Modus (WAL). Solange die Site läuft, hält SQLite zwei zusätzliche Dateien neben der Datenbank vor, etwa emdash.db-wal und emdash.db-shm. Der Prozess benötigt Schreibzugriff auf das Datenbankverzeichnis, um sie zu erstellen.

Die -wal-Datei kann bestätigte Änderungen enthalten, die noch nicht in der Hauptdatenbankdatei stehen. Sichern Sie mit dem Backup-Befehl von SQLite, statt nur die .db-Datei zu kopieren. Siehe SQLite-Backup und Wiederherstellung.

WAL erfordert Shared Memory, halten Sie die Datenbank deshalb auf einem lokalen Datenträger oder Block-Volume und nicht auf einem Netzwerkdateisystem wie NFS oder SMB.

Cloudflare D1

D1 ist die serverlose SQLite-Datenbank von Cloudflare. Verwenden Sie sie beim Deployment auf Cloudflare Workers.

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

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

Konfiguration

OptionTypStandardBeschreibung
bindingstring—Name des D1-Bindings aus wrangler.jsonc
sessionstring"disabled"Modus der Lesereplikation (siehe unten)
bookmarkCookiestring"__em_d1_bookmark"Cookie-Name für Session-Bookmarks

Wrangler-Binding

wrangler.jsonc

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

wrangler.toml

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

Wrangler kann beim Deployment aus diesem Binding eine fehlende D1-Datenbank bereitstellen. EmDash-Migrationen sind ein separater Schritt. Folgen Sie Auf Cloudflare bereitstellen für den vollständigen Satz an Bindings und Core-Datenbankmigrationen verwalten für das Migrations-Runbook.

Read Replicas

D1 unterstützt Lesereplikation, um die Leselatenz für global verteilte Sites zu senken. Ist sie aktiviert, werden Leseanfragen an nahe gelegene Replikate geleitet, statt immer die primäre Datenbank anzusprechen.

EmDash verwendet die D1 Sessions API, um dies transparent zu verwalten. Aktivieren Sie es mit der Option session:

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

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

Session-Modi

ModusVerhalten
"disabled"Keine Sessions. Alle Abfragen gehen an die primäre Datenbank. Standard.
"auto"Anonyme Anfragen lesen vom nächstgelegenen Replikat. Authentifizierte Benutzer erhalten über Bookmark-Cookies Read-your-writes-Konsistenz.
"primary-first"Wie "auto", aber die erste Abfrage geht immer an die primäre Datenbank. Für Sites mit sehr häufigen Schreibvorgängen.

So funktioniert es

  • Anonyme Besucher erhalten first-unconstrained — Lesevorgänge gehen an das nächstgelegene Replikat, für die geringste Latenz. Da anonyme Benutzer nie schreiben, brauchen sie keine Konsistenzgarantien.
  • Authentifizierte Benutzer (Redakteure, Autoren) erhalten Bookmark-basierte Sessions. Nach einem Schreibvorgang stellt ein Bookmark-Cookie sicher, dass die nächste Anfrage mindestens diesen Stand sieht.
  • Schreibanfragen (POST, PUT, DELETE) beginnen immer bei der primären Datenbank.
  • Abfragen zur Build-Zeit (Astro-Inhaltssammlungen) umgehen Sessions vollständig und verwenden direkt die primäre Datenbank.

libSQL

libSQL ist ein Fork von SQLite, der Remote-Verbindungen unterstützt. Verwenden Sie es, wenn Sie eine entfernte Datenbank ohne Cloudflare D1 benötigen.

import { libsql } from "emdash/db";

export default defineConfig({
	integrations: [
		emdash({
			database: libsql({
				url: process.env.LIBSQL_DATABASE_URL,
				authToken: process.env.LIBSQL_AUTH_TOKEN,
			}),
		}),
	],
});

Konfiguration

OptionTypBeschreibung
urlstringDatenbank-URL (libsql://... oder file:...)
authTokenstringLaufzeit-Auth-Token für entfernte Datenbanken (lokal optional)
migrationAuthTokenEnvstringVariablenname des Migrations-Tokens (Standard TURSO_AUTH_TOKEN)

Lokale Entwicklung

Verwenden Sie während der Entwicklung eine lokale libSQL-Datei:

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

PostgreSQL

PostgreSQL wird für Node.js-Deployments unterstützt, die eine vollwertige relationale Datenbank benötigen.

import { postgres } from "emdash/db";

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

Konfiguration

Sie können sich mit einer Verbindungszeichenfolge oder mit einzelnen Parametern verbinden:

// 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,
});
OptionTypBeschreibung
connectionStringstringPostgreSQL-Verbindungs-URL
hoststringDatenbank-Host
portnumberDatenbank-Port
databasestringDatenbankname
userstringDatenbankbenutzer
passwordstringDatenbankpasswort
sslbooleanSSL aktivieren
pool.minnumberMinimale Pool-Verbindungen (Standard 0)
pool.maxnumberMaximale Pool-Verbindungen (Standard 10)
pool.connectionTimeoutMillisnumberMaximale Wartezeit auf eine Verbindung (pg-Standard: 0, kein Timeout)
pool.idleTimeoutMillisnumberLebensdauer inaktiver Clients (pg-Standard: 10.000 ms)
migrationConnectionStringEnvstringVariablenname der Verbindungszeichenfolge für Migrationen (Standard DATABASE_URL)

Setzen Sie pool.connectionTimeoutMillis auf einen Wert ungleich null, um zu begrenzen, wie lange eine Anfrage wartet, wenn PostgreSQL nicht erreichbar ist oder keine Verbindung aus dem Pool verfügbar wird. Setzen Sie pool.idleTimeoutMillis auf 0, um inaktive Clients offen zu halten, bis der Pool geschlossen wird. Wird eine der beiden Optionen weggelassen, bleibt der pg-Standard erhalten.

Anforderungen an die Datenbankrolle

EmDash erstellt und aktualisiert seine eigenen PostgreSQL-Tabellen. Core-Migrationen erstellen und ändern System- und Sammlungstabellen, Inhaltstypen erstellen ec_*-Tabellen, und das Hinzufügen oder Entfernen eines Feldes ändert die zugehörige Sammlungstabelle. Die konfigurierte PostgreSQL-Rolle benötigt daher für die gesamte Lebensdauer der Site Schema-Berechtigungen, nicht nur während der Ersteinrichtung.

Verwenden Sie für EmDash eine einzige kanonische Rolle. Sie benötigt:

  • CONNECT für die Datenbank;
  • USAGE und CREATE für das aktive Schema;
  • die Eigentümerschaft an jeder EmDash-Tabelle und -Funktion, entweder direkt oder durch Mitgliedschaft mit INHERIT in der besitzenden Rolle; und
  • SELECT, INSERT, UPDATE und DELETE für diese Tabellen.

Sie muss kein Superuser sein, weder CREATEDB noch CREATEROLE haben und keine Erweiterungen erstellen. PostgreSQL bietet keine Berechtigung ALTER oder DROP für Tabellen: Diese Operationen gehören dem Objekteigentümer und Rollen, die dessen Rechte erben. Wenn Sie einer anderen Rolle ALL für eine Tabelle gewähren, wird diese Rolle dadurch nicht zum Eigentümer. EmDash führt kein SET ROLE aus, daher genügt eine ohne Vererbung konfigurierte Mitgliedschaft nicht.

Die meisten Installationen können das vorhandene Schema der Datenbank verwenden, üblicherweise public. Das ist die einfachste Option, wenn die Datenbank EmDash vorbehalten ist. In den folgenden Beispielen ist emdash_app die Login-Rolle in der Verbindungszeichenfolge von EmDash; verwenden Sie eine vorhandene Rolle Ihres Anbieters oder erstellen Sie ein eigenes Login. Gewähren Sie ihr den Zugriff über eine administrative Verbindung und ersetzen Sie dabei Ihre Datenbank-, Schema- und Rollennamen:

GRANT CONNECT ON DATABASE app TO emdash_app;
GRANT USAGE, CREATE ON SCHEMA public TO emdash_app;

Mit diesen Berechtigungen kann die Rolle neue Objekte erstellen. Sie ändern nicht den Eigentümer vorhandener Tabellen; verwenden Sie das Runbook zur Reparatur der PostgreSQL-Eigentümerschaft, wenn eine bestehende Site gemischte Eigentümer hat.

EmDash verwendet das aktive current_schema() von PostgreSQL. Es erstellt kein Schema und setzt keinen search_path, prüfen Sie die Verbindung daher vor dem Deployment:

SELECT
  current_database(),
  session_user,
  current_user,
  current_schema(),
  current_setting('search_path');

Optional: ein dediziertes Schema verwenden

Verwenden Sie ein dediziertes Schema, wenn EmDash sich eine Datenbank mit einer anderen Anwendung teilt oder wenn Sie möchten, dass seine Objekte von public getrennt sind. Das ist optional und lässt sich am einfachsten vor der ersten EmDash-Einrichtung konfigurieren. Eine Datenbank, die ausschließlich EmDash gehört, braucht kein separates Schema.

Angenommen, die kanonische Rolle emdash_app existiert bereits, erstellen und wählen Sie ihr Schema über eine administrative Verbindung:

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;

Dadurch wird eine bestehende Installation weder von public verschoben noch eine gemischte Eigentümerschaft repariert. Bestehende Sites sollten ihr aktuelles Schema behalten und stattdessen das Runbook zur Reparatur der PostgreSQL-Eigentümerschaft verwenden.

Connection Pooling

Der Adapter verwendet pg.Pool. Passen Sie die Pool-Größe an Ihr Deployment an:

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

Hyperdrive

Verwenden Sie den Adapter hyperdrive(), um EmDash auf Cloudflare Workers zu betreiben, gestützt auf eine vorhandene PostgreSQL- oder Postgres-kompatible Datenbank (z. B. PlanetScale Postgres). Hyperdrive bündelt und beschleunigt die Verbindung über das Netzwerk von Cloudflare; der PostgreSQL-Dialekt von EmDash führt die Abfragen aus.

import { hyperdrive, r2 } from "@emdash-cms/cloudflare";

export default defineConfig({
	integrations: [
		emdash({
			database: hyperdrive({ binding: "HYPERDRIVE" }),
			storage: r2({ binding: "MEDIA" }),
		}),
	],
});

Anforderungen

  • pg >= 8.16.3 in Ihrer Site installiert (pnpm add pg)
  • compatibility_flags: ["nodejs_compat"]
  • compatibility_date >= "2024-09-23"

Einrichtung

Bereiten Sie zuerst die PostgreSQL-Rolle vor. Erstellen Sie dann die Hyperdrive-Konfiguration mit der Verbindungszeichenfolge dieser Rolle und fügen Sie das Binding zu Ihrer Wrangler-Konfiguration hinzu:

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>"

Konfiguration

OptionTypStandardBeschreibung
bindingstring"HYPERDRIVE"Name des primären Hyperdrive-Bindings (Caching deaktiviert)
cachedBindingstring—Optionales Binding mit aktiviertem Caching für anonyme Lesevorgänge (siehe unten)
preferUncachedAfterWriteMsnumber60000*Nach dem Veröffentlichen von Inhalten bei anonymen öffentlichen Lesevorgängen für so viele ms binding bevorzugen (an das max_age von Hyperdrive anpassen)
migrationConnectionStringEnvstringCLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING>Umgebungsvariable mit der direkten URL des PostgreSQL-Origins für emdash migrate
maxnumber5Maximale Größe des Verbindungspools im Worker zu Hyperdrive

*Der Standardwert 60000 gilt nur, wenn cachedBinding gesetzt ist; andernfalls wird er ignoriert.

Anonyme Lesevorgänge aus dem Cache bedienen

Standardmäßig deaktivieren Sie das Hyperdrive-Caching vollständig, weil der Admin-Bereich und Schreibvorgänge Read-after-write-Konsistenz benötigen. Anonyme öffentliche Anfragen mit GET oder HEAD können jedoch ein kurzes Zeitfenster veralteter Daten tolerieren. Wenn dieser Kompromiss akzeptabel ist, betreiben Sie zwei Hyperdrive-Konfigurationen für dieselbe Datenbank: eine mit deaktiviertem Caching (das primäre binding) und eine mit aktiviertem Caching (cachedBinding). EmDash leitet diese anonymen öffentlichen Anfragen über das Binding mit aktiviertem Cache und alle anderen Anfragen über das primäre Binding ohne 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" });

Das ist das Zwei-Konfigurationen-Muster, das Cloudflare für das Caching dokumentiert. EmDash entscheidet pro Anfrage, welches Binding verwendet wird:

  • Anonyme Lesevorgänge auf Pfaden der öffentlichen Site (GET/HEAD, keine Session, nicht unter /_emdash) → cachedBinding mit aktiviertem Cache, außer in einem kurzen Zeitfenster nach dem Veröffentlichen von Inhalten (Standard 60 s; setzen Sie preferUncachedAfterWriteMs auf das max_age Ihres Hyperdrive), in dem EmDash das binding ohne Cache bevorzugt, damit ein Rebuild Edge- und Objekt-Caches nicht erneut mit noch veralteten Hyperdrive-Ergebnissen füllen kann.
  • Authentifizierte Anfragen (Redakteure, Autoren) → binding ohne Cache.
  • Änderungsanfragen (POST, PUT, PATCH, DELETE, auch anonyme) → binding ohne Cache.
  • Jede Anfrage unter /_emdash (Admin-Bereich, Einrichtung, Authentifizierung, interne APIs), auch ein anonymes GET → binding ohne Cache.
  • Laufzeitmigrationen und Kaltstart → immer das primäre binding.
  • Vom Deployment verwaltete Migrationen → verbinden sich mit migrationConnectionStringEnv direkt mit dem PostgreSQL-Origin; sie verwenden nie eines der beiden Hyperdrive-Bindings.

Optional: eine separate gecachte Rolle verwenden

Migrationen, Einrichtung, authentifizierte Anfragen und explizite Schreibanfragen verwenden immer das primäre binding. Eine separate Rolle für cachedBinding braucht keine Schema-Eigentümerschaft und kein CREATE, benötigt aber CONNECT, USAGE für das Schema und SELECT für jede Tabelle, die die öffentliche Site nutzt.

Anonyme öffentliche GET- und HEAD-Anfragen können außerdem Weiterleitungstreffer und 404-Fehler protokollieren. Um diese Funktionen zu erhalten, benötigt die gecachte Rolle zusätzlich UPDATE für _emdash_redirects sowie SELECT, INSERT, UPDATE und DELETE für _emdash_404_log. Plugins oder Anwendungscode, die während eines öffentlichen GET oder HEAD schreiben, benötigen unter Umständen mehr. Verwenden Sie für beide Bindings dieselbe Rolle, sofern Sie die Site nicht mit einer eingeschränkten gecachten Rolle getestet haben.

Fügen Sie die gecachte Rolle hinzu, nachdem EmDash seine ersten Migrationen abgeschlossen hat. Die folgenden Beispiele verwenden das optionale Schema emdash; ersetzen Sie es durch Ihr aktives Schema, etwa public. Erstellen Sie das Login und die Datenbankeinstellungen mit der administrativen Rolle Ihres Anbieters:

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;

Verbinden Sie sich dann als emdash_app, dem Eigentümer des Schemas und der Tabellen, um Zugriff auf vorhandene und künftige Tabellen zu gewähren:

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;

Verbinden Sie sich mit beiden Rollen und prüfen Sie, dass sie dieselben Werte für current_database() und current_schema() melden, bevor Sie cachedBinding aktivieren. In einem gemeinsam genutzten Schema legt GRANT SELECT ON ALL TABLES auch nicht zugehörige Tabellen offen. Gewähren Sie stattdessen Zugriff auf einzelne EmDash-Tabellen und aktualisieren Sie diese Berechtigungen, wenn Sammlungen oder andere Schemaobjekte hinzukommen.

Core-Migrationen

EmDash führt Core-Migrationen standardmäßig für jeden unterstützten Dialekt automatisch aus. Astro-Build und -Sync erzeugen außerdem eine validierte, geheimnisfreie .emdash/migrations.json, die emdash migrate vor dem Deployment anwenden kann. SQLite, libSQL, PostgreSQL, D1 und der direkte PostgreSQL-Origin hinter Hyperdrive haben Deployment-Executors.

Unter Core-Datenbankmigrationen verwalten finden Sie Zugangsdaten für das Ziel, CI-Serialisierung, die Laufzeitrichtlinien auto/check/manual und die Wiederherstellung nach unbekannten Einträgen oder mehrdeutigen D1-Schreibvorgängen.

Bei PostgreSQL laufen Laufzeitmigrationen über die konfigurierte Verbindung; Laufzeitmigrationen von Hyperdrive verwenden immer das primäre Binding. Vom Deployment verwaltete Hyperdrive-Migrationen verbinden sich direkt mit dem PostgreSQL-Origin. Core-Migrationen können Tabellen, Indizes und Funktionen erstellen, Spalten und Constraints ändern oder löschen und vorhandene Zeilen aktualisieren. Eine Rolle, die sich verbinden und Zeilen ändern kann, aber die vorhandenen EmDash-Objekte nicht besitzt, genügt nicht. Der Einrichtungsassistent kann fehlende Datenbankberechtigungen nicht reparieren, weil Laufzeitmigrationen vor der Einrichtung laufen.

Bis der Einrichtungsassistent abgeschlossen wurde, wendet EmDash außerdem einmalig bei der ersten Anfrage das Schema und die Struktur einer Seed-Datei an. Beispielinhalte aus dem Seed werden nur angewendet, wenn ein Administrator sie im Einrichtungsassistenten auswählt. Der Seed wird aus .emdash/seed.json, dem Pfad in package.json#emdash.seed oder seed/seed.json gelesen — je nachdem, was zuerst gefunden wird — und zur Kompilierzeit in den Build eingebettet. Ist keiner vorhanden, wird ein eingebauter Standard-Seed verwendet. Spätere Starts gegen eine vorhandene Datenbank lassen deren Inhalt unangetastet.

Separate Datenbanken für separate Umgebungen verwenden

Geben Sie Entwicklung, Preview, Staging und Produktion jeweils eine eigene Datenbank. Ein Preview-Deployment, das auf die Produktion zeigt, kann Core-Migrationen oder destruktive Befehle für das Inhaltsmodell gegen Live-Daten ausführen.

Definieren Sie bei Cloudflare jedes D1- oder Hyperdrive-Binding unter der passenden Wrangler-Umgebung und übergeben Sie --env an Wrangler-Befehle. Injizieren Sie bei Node.js in jede Laufzeitumgebung eine andere Datenbank-URL. Bewahren Sie Zugangsdaten in Laufzeit-Secrets auf, nicht in astro.config.mjs.