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
| Database | Use it when | Runtime |
|---|---|---|
| SQLite | Un processo Node.js ha un disco persistente | Node.js o sviluppo locale |
| D1 | Il sito gira su Cloudflare Workers e deve usare Cloudflare SQL | Cloudflare Workers |
| Hyperdrive | Il sito gira su Workers e deve usare un’origine PostgreSQL esistente | Cloudflare Workers |
| PostgreSQL | Più processi Node.js necessitano di un database condiviso | Node.js |
| libSQL | Un deployment Node.js necessita di un database remoto compatibile con SQLite | Node.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
| Option | Type | Description |
|---|---|---|
url | string | Percorso 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
| Option | Type | Default | Description |
|---|---|---|---|
binding | string | — | Nome del binding D1 da wrangler.jsonc |
session | string | "disabled" | Modalità di replica in lettura (vedi sotto) |
bookmarkCookie | string | "__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
| Mode | Behavior |
|---|---|
"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
| Option | Type | Description |
|---|---|---|
url | string | URL del database (libsql://... o file:...) |
authToken | string | Token di auth runtime per database remoti (opzionale in locale) |
migrationAuthTokenEnv | string | Nome 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,
});
| Option | Type | Description |
|---|---|---|
connectionString | string | URL di connessione PostgreSQL |
host | string | Host del database |
port | number | Porta del database |
database | string | Nome del database |
user | string | Utente del database |
password | string | Password del database |
ssl | boolean | Abilitare SSL |
pool.min | number | Connessioni minime del pool (predefinito 0) |
pool.max | number | Connessioni massime del pool (predefinito 10) |
pool.connectionTimeoutMillis | number | Attesa massima di connessione (predefinito pg: 0, nessun timeout) |
pool.idleTimeoutMillis | number | Durata del client idle (predefinito pg: 10.000 ms) |
migrationConnectionStringEnv | string | Nome 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:
CONNECTsul database;USAGEeCREATEsullo schema attivo;- proprietà di ogni tabella e funzione EmDash, direttamente o tramite appartenenza con
INHERITal ruolo proprietario; e SELECT,INSERT,UPDATEeDELETEsu 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.3installato 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
| Option | Type | Default | Description |
|---|---|---|---|
binding | string | "HYPERDRIVE" | Nome del binding Hyperdrive primario (cache disabilitata) |
cachedBinding | string | — | Binding opzionale con cache abilitata per letture anonime (vedi sotto) |
preferUncachedAfterWriteMs | number | 60000* | Dopo una pubblicazione di contenuto, preferire binding per questi ms sulle letture pubbliche anonime (allinea a max_age di Hyperdrive) |
migrationConnectionStringEnv | string | CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> | Variabile d’ambiente con l’URL diretto dell’origine PostgreSQL per emdash migrate |
max | number | 5 | Dimensione 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) →cachedBindingcon cache, eccetto per una breve finestra dopo una pubblicazione di contenuto (predefinito 60 s; impostapreferUncachedAfterWriteMssul tuomax_ageHyperdrive) quando EmDash preferisce ilbindingnon in cache così una ricostruzione non può riempire di nuovo le cache edge/oggetto da risultati Hyperdrive ancora obsoleti. - Richieste autenticate (editor, autori) →
bindingnon in cache. - Richieste di mutazione (
POST,PUT,PATCH,DELETE, incluse quelle anonime) →bindingnon in cache. - Qualsiasi richiesta sotto
/_emdash(admin, setup, auth, API interne), anche unGETanonimo →bindingnon in cache. - Migrazioni runtime e cold-start → sempre il
bindingprimario. - 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.