Scegliere un database

In questa pagina

Scegli un adattatore di database per ogni deployment. Il database contiene il modello di contenuto, le voci, gli utenti, le impostazioni e i dati dei plugin. I binari multimediali appartengono a un backend di storage separato.

Panoramica

DatabaseUse it whenRuntime
SQLiteUn processo Node.js ha un disco persistenteNode.js o sviluppo locale
D1Il sito gira su Cloudflare Workers e deve usare Cloudflare SQLCloudflare Workers
HyperdriveIl sito gira su Workers e deve usare un’origine PostgreSQL esistenteCloudflare Workers
PostgreSQLPiù processi Node.js necessitano di un database condivisoNode.js
libSQLUn deployment Node.js necessita di un database remoto compatibile con SQLiteNode.js

D1 è il valore predefinito per i template Cloudflare. SQLite è l’opzione Node.js più semplice, ma richiede un volume persistente scrivibile e backup operativi del database.

SQLite

SQLite usa il driver di database integrato di Node.js ed è l’opzione più semplice per i deployment Node.js.

import { sqlite } from "emdash/db";

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

Configurazione

OptionTypeDescription
urlstringPercorso file con prefisso file:

Percorso file

L’url deve iniziare con 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}` });

Write-ahead logging

EmDash apre i database SQLite in modalità write-ahead logging (WAL). Mentre il sito è in esecuzione, SQLite mantiene due file aggiuntivi accanto al database, come emdash.db-wal e emdash.db-shm. Il processo necessita di accesso in scrittura alla directory del database per crearli.

Il file -wal può contenere modifiche confermate non ancora nel file principale del database. Esegui il backup con il comando di backup di SQLite invece di copiare solo il file .db. Vedi Backup e ripristino SQLite.

WAL richiede memoria condivisa, quindi tieni il database su un disco locale o un volume a blocchi piuttosto che su un filesystem di rete come NFS o SMB.

Cloudflare D1

D1 è il database SQLite serverless di Cloudflare. Usalo quando esegui il deployment su Cloudflare Workers.

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

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

Configurazione

OptionTypeDefaultDescription
bindingstring—Nome del binding D1 da wrangler.jsonc
sessionstring"disabled"Modalità di replica in lettura (vedi sotto)
bookmarkCookiestring"__em_d1_bookmark"Nome cookie per i bookmark di sessione

Binding Wrangler

wrangler.jsonc

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

wrangler.toml

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

Wrangler può provvedere un database D1 mancante da questo binding durante il deployment. Le migrazioni EmDash sono un passo separato. Segui Deploy su Cloudflare per l’insieme completo dei binding e Gestire le migrazioni del database principale per il runbook delle migrazioni.

Repliche in lettura

D1 supporta la replica in lettura per ridurre la latenza di lettura nei siti distribuiti globalmente. Quando abilitata, le query di lettura vengono indirizzate a repliche vicine invece di colpire sempre il database primario.

EmDash usa l’API Sessions di D1 per gestirlo in modo trasparente. Abilitala con l’opzione session:

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

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

Modalità di sessione

ModeBehavior
"disabled"Nessuna sessione. Tutte le query vanno al primario. Predefinito.
"auto"Le richieste anonime leggono dalla replica più vicina. Gli utenti autenticati ottengono consistenza read-your-writes tramite cookie di bookmark.
"primary-first"Come "auto", ma la prima query va sempre al primario. Per siti con scritture molto frequenti.

Come funziona

  • Visitatori anonimi ottengono first-unconstrained — le letture vanno alla replica più vicina per la latenza più bassa. Poiché gli utenti anonimi non scrivono mai, non necessitano di garanzie di consistenza.
  • Utenti autenticati (editor, autori) ottengono sessioni basate su bookmark. Dopo una scrittura, un cookie di bookmark assicura che la richiesta successiva veda almeno quello stato.
  • Richieste di scrittura (POST, PUT, DELETE) partono sempre dal database primario.
  • Query in fase di build (Astro content collections) bypassano del tutto le sessioni e usano direttamente il primario.

libSQL

libSQL è un fork di SQLite che supporta connessioni remote. Usalo quando ti serve un database remoto senza 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,
			}),
		}),
	],
});

Configurazione

OptionTypeDescription
urlstringURL del database (libsql://... o file:...)
authTokenstringToken di auth runtime per database remoti (opzionale in locale)
migrationAuthTokenEnvstringNome della variabile del token di migrazione (predefinito TURSO_AUTH_TOKEN)

Sviluppo locale

Usa un file libSQL locale durante lo sviluppo:

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

PostgreSQL

PostgreSQL è supportato per i deployment Node.js che necessitano di un database relazionale completo.

import { postgres } from "emdash/db";

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

Configurazione

Puoi connetterti con una connection string o parametri individuali:

// 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
connectionStringstringURL di connessione PostgreSQL
hoststringHost del database
portnumberPorta del database
databasestringNome del database
userstringUtente del database
passwordstringPassword del database
sslbooleanAbilitare SSL
pool.minnumberConnessioni minime del pool (predefinito 0)
pool.maxnumberConnessioni massime del pool (predefinito 10)
pool.connectionTimeoutMillisnumberAttesa massima di connessione (predefinito pg: 0, nessun timeout)
pool.idleTimeoutMillisnumberDurata del client idle (predefinito pg: 10.000 ms)
migrationConnectionStringEnvstringNome della variabile della connection string di migrazione (predefinito DATABASE_URL)

Imposta pool.connectionTimeoutMillis a un valore diverso da zero per limitare quanto una richiesta attende quando PostgreSQL è irraggiungibile o nessuna connessione del pool diventa disponibile. Imposta pool.idleTimeoutMillis a 0 per mantenere aperti i client idle finché il pool non si chiude. Omettere entrambe le opzioni conserva il predefinito di pg.

Requisiti del ruolo del database

EmDash crea e aggiorna le proprie tabelle PostgreSQL. Le migrazioni principali creano e alterano tabelle di sistema e di collection, i tipi di contenuto creano tabelle ec_*, e aggiungere o rimuovere un campo altera la sua tabella di collection. Il ruolo PostgreSQL configurato necessita quindi di autorità sullo schema per tutta la vita del sito, non solo durante la configurazione iniziale.

Usa un ruolo canonico per EmDash. Necessita di:

  • CONNECT sul database;
  • USAGE e CREATE sullo schema attivo;
  • proprietà di ogni tabella e funzione EmDash, direttamente o tramite appartenenza con INHERIT al ruolo proprietario; e
  • SELECT, INSERT, UPDATE e DELETE su quelle tabelle.

Non deve essere un superuser, avere CREATEDB o CREATEROLE, né creare estensioni. PostgreSQL non fornisce un diritto di tabella ALTER o DROP: tali operazioni appartengono al proprietario dell’oggetto e ai ruoli che ereditano i suoi privilegi. Concedere ALL su una tabella a un ruolo diverso non lo rende proprietario. EmDash non esegue SET ROLE, quindi l’appartenenza configurata senza ereditarietà non è sufficiente.

La maggior parte delle installazioni può usare lo schema esistente del database, comunemente public. È l’opzione più semplice quando il database è dedicato a EmDash. Negli esempi seguenti, emdash_app è il ruolo di login nella connection string di EmDash; usa un ruolo provider esistente o crea un login dedicato. Concedigli l’accesso con una connessione amministrativa, sostituendo i nomi di database, schema e ruolo:

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

Questi grant permettono al ruolo di creare nuovi oggetti. Non cambiano il proprietario delle tabelle esistenti; usa il runbook di riparazione della proprietà PostgreSQL quando un sito esistente ha proprietari misti.

EmDash usa il current_schema() attivo di PostgreSQL. Non crea uno schema e non imposta search_path, quindi verifica la connessione prima del deployment:

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

Opzionale: usare uno schema dedicato

Usa uno schema dedicato quando EmDash condivide un database con un’altra applicazione o quando vuoi isolare i suoi oggetti da public. È opzionale ed è più facile da configurare prima della prima configurazione di EmDash. Un database dedicato a EmDash non necessita di uno schema separato.

Assumendo che il ruolo canonico emdash_app esista già, crea e seleziona il suo schema con una connessione amministrativa:

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;

Questo non sposta un’installazione esistente da public né ripara la proprietà mista. I siti esistenti devono mantenere lo schema corrente e usare invece il runbook di riparazione della proprietà PostgreSQL.

Connection pooling

L’adattatore usa pg.Pool. Regola la dimensione del pool in base al tuo deployment:

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

Hyperdrive

Usa l’adattatore hyperdrive() per eseguire EmDash su Cloudflare Workers supportato da un database PostgreSQL esistente — o compatibile con Postgres (es. PlanetScale Postgres). Hyperdrive mette in pool e accelera la connessione sulla rete di Cloudflare; il dialetto PostgreSQL di EmDash esegue le query.

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

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

Requisiti

  • pg >= 8.16.3 installato nel tuo sito (pnpm add pg)
  • compatibility_flags: ["nodejs_compat"]
  • compatibility_date >= "2024-09-23"

Configurazione

Prima prepara il ruolo PostgreSQL. Poi crea la configurazione Hyperdrive con la connection string di quel ruolo e aggiungi il binding alla configurazione 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>"

Configurazione

OptionTypeDefaultDescription
bindingstring"HYPERDRIVE"Nome del binding Hyperdrive primario (cache disabilitata)
cachedBindingstring—Binding opzionale con cache abilitata per letture anonime (vedi sotto)
preferUncachedAfterWriteMsnumber60000*Dopo una pubblicazione di contenuto, preferire binding per questi ms sulle letture pubbliche anonime (allinea a max_age di Hyperdrive)
migrationConnectionStringEnvstringCLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING>Variabile d’ambiente con l’URL diretto dell’origine PostgreSQL per emdash migrate
maxnumber5Dimensione max del pool di connessioni in-Worker verso Hyperdrive

*Il predefinito 60000 si applica solo quando cachedBinding è impostato; altrimenti ignorato.

Servire letture anonime dalla cache

Per impostazione predefinita disabiliti del tutto la cache di Hyperdrive, perché l’admin e le scritture necessitano di consistenza read-after-write. Ma le richieste pubbliche anonime con GET o HEAD possono tollerare una breve finestra di obsolescenza. Se quel compromesso è accettabile, esegui due configurazioni Hyperdrive sullo stesso database: una con cache disattivata (il binding primario) e una con cache attivata (cachedBinding). EmDash instrada quelle richieste pubbliche anonime attraverso il binding con cache e ogni altra richiesta attraverso il primario non in 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" });

Questo è il pattern a due configurazioni che Cloudflare documenta per la cache. EmDash decide quale binding usare per richiesta:

  • Letture anonime di percorsi del sito pubblico (GET/HEAD, nessuna sessione, non sotto /_emdash) → cachedBinding con cache, eccetto per una breve finestra dopo una pubblicazione di contenuto (predefinito 60 s; imposta preferUncachedAfterWriteMs sul tuo max_age Hyperdrive) quando EmDash preferisce il binding non in cache così una ricostruzione non può riempire di nuovo le cache edge/oggetto da risultati Hyperdrive ancora obsoleti.
  • Richieste autenticate (editor, autori) → binding non in cache.
  • Richieste di mutazione (POST, PUT, PATCH, DELETE, incluse quelle anonime) → binding non in cache.
  • Qualsiasi richiesta sotto /_emdash (admin, setup, auth, API interne), anche un GET anonimo → binding non in cache.
  • Migrazioni runtime e cold-start → sempre il binding primario.
  • Migrazioni gestite dal deployment → si connettono direttamente all’origine PostgreSQL usando migrationConnectionStringEnv; non usano mai nessuno dei binding Hyperdrive.

Opzionale: usare un ruolo in cache separato

Migrazioni, setup, richieste autenticate e scritture esplicite usano sempre il binding primario. Un ruolo separato per cachedBinding non necessita di proprietà dello schema né di CREATE, ma necessita di CONNECT, USAGE sullo schema e SELECT su ogni tabella usata dal sito pubblico.

Anche le richieste pubbliche anonime GET e HEAD possono registrare hit di redirect e 404. Per preservare quelle funzionalità, il ruolo in cache necessita inoltre di UPDATE su _emdash_redirects e di SELECT, INSERT, UPDATE e DELETE su _emdash_404_log. Plugin o codice applicativo che scrivono durante un GET o HEAD pubblico possono richiedere di più. Usa lo stesso ruolo per entrambi i binding a meno che tu non abbia testato il sito con un ruolo in cache ristretto.

Aggiungi il ruolo in cache dopo che EmDash ha completato le migrazioni iniziali. Gli esempi seguenti usano lo schema opzionale emdash; sostituisci lo schema attivo, come public. Crea il login e le impostazioni del database con il ruolo amministrativo del tuo provider:

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;

Poi connettiti come emdash_app, il proprietario dello schema e delle tabelle, per concedere l’accesso alle tabelle esistenti e future:

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;

Connettiti con entrambi i ruoli e verifica che riportino lo stesso current_database() e current_schema() prima di abilitare cachedBinding. Su uno schema condiviso, GRANT SELECT ON ALL TABLES espone anche tabelle non correlate. Concedi invece l’accesso a tabelle EmDash individuali e aggiorna quei grant quando vengono aggiunte collection o altri oggetti di schema.

Migrazioni principali

EmDash esegue le migrazioni principali automaticamente per impostazione predefinita per ogni dialetto supportato. Il build e sync di Astro emettono anche un .emdash/migrations.json validato e senza segreti, che emdash migrate può applicare prima del deployment. SQLite, libSQL, PostgreSQL, D1 e l’origine PostgreSQL diretta dietro Hyperdrive hanno executor di deployment.

Vedi Gestire le migrazioni del database principale per le credenziali di destinazione, la serializzazione CI, la policy runtime auto/check/manual e il ripristino da record sconosciuti o scritture D1 ambigue.

Per PostgreSQL, le migrazioni runtime passano dalla connessione configurata; le migrazioni runtime Hyperdrive usano sempre il suo binding primario. Le migrazioni Hyperdrive gestite dal deployment si connettono direttamente all’origine PostgreSQL. Le migrazioni principali possono creare tabelle, indici e funzioni, alterare o eliminare colonne e vincoli, e aggiornare righe esistenti. Un ruolo che può connettersi e modificare righe ma non possiede gli oggetti EmDash esistenti non è sufficiente. La procedura guidata di setup non può riparare i privilegi di database mancanti perché le migrazioni runtime vengono eseguite prima del setup.

Se il database è vuoto (nessuna collection) e la procedura guidata di setup non è stata completata, EmDash applica anche un file seed al primo avvio. Il seed viene letto da .emdash/seed.json, il percorso in package.json#emdash.seed o seed/seed.json — il primo trovato — e incorporato nel build in fase di compilazione. Se nessuno è presente, viene usato un seed predefinito integrato. Gli avvii successivi contro un database esistente lasciano intatto il suo contenuto.

Usare database separati per ambienti separati

Assegna a development, preview, staging e production ciascuno il proprio database. Un deployment di preview puntato alla produzione può eseguire migrazioni principali o comandi distruttivi del modello di contenuto contro dati live.

Per Cloudflare, definisci ogni binding D1 o Hyperdrive sotto l’ambiente Wrangler corrispondente e passa --env ai comandi Wrangler. Per Node.js, inietta un URL di database diverso in ogni ambiente runtime. Tieni le credenziali nei secret runtime, non in astro.config.mjs.