Scegli un adattatore di database per ogni deploy. Il database contiene il modello di contenuto, le voci, gli utenti, le impostazioni e i dati dei plugin. I binari dei media vanno in un backend di archiviazione separato.
Panoramica
| Database | Usalo quando | 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 origin PostgreSQL esistente | Cloudflare Workers |
| PostgreSQL | Più processi Node.js hanno bisogno di un database condiviso | Node.js |
| libSQL | Un deploy Node.js ha bisogno di un database remoto compatibile con SQLite | Node.js |
D1 è l’impostazione predefinita dei template Cloudflare. SQLite è l’opzione più semplice per Node.js, 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 deploy Node.js.
import { sqlite } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
}),
],
});
Configurazione
| Opzione | Tipo | Descrizione |
|---|---|---|
url | string | Percorso del file con il 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 ha bisogno dell’accesso in scrittura alla directory del database per crearli.
Il file -wal può contenere modifiche confermate che non sono ancora nel file principale del database. Esegui il backup con il comando di backup di SQLite invece di copiare solo il file .db. Consulta Backup e ripristino SQLite.
WAL richiede memoria condivisa, quindi tieni il database su un disco locale o su un volume a blocchi e non su un file system di rete come NFS o SMB.
Cloudflare D1
D1 è il database SQLite serverless di Cloudflare. Usalo quando fai il deploy su Cloudflare Workers.
import { d1 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: d1({ binding: "DB" }),
}),
],
});
Configurazione
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
binding | string | — | Nome del binding D1 in wrangler.jsonc |
session | string | "disabled" | Modalità di replica in lettura (vedi sotto) |
bookmarkCookie | string | "__em_d1_bookmark" | Nome del cookie per i segnalibri 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ò effettuare il provisioning di un database D1 mancante a partire da questo binding durante il deploy. Le migrazioni di EmDash sono un passaggio separato. Segui Distribuire su Cloudflare per l’insieme completo dei binding e Gestire le migrazioni del database core per il runbook delle migrazioni.
Repliche in lettura
D1 supporta la replica in lettura per ridurre la latenza di lettura dei siti distribuiti globalmente. Quando è abilitata, le query di lettura vengono instradate verso repliche vicine invece di raggiungere sempre il database primario.
EmDash usa la D1 Sessions API per gestirlo in modo trasparente. Abilitalo con l’opzione session:
import { d1 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: d1({
binding: "DB",
session: "auto",
}),
}),
],
});
Modalità di sessione
| Modalità | Comportamento |
|---|---|
"disabled" | Nessuna sessione. Tutte le query vanno al primario. Predefinito. |
"auto" | Le richieste anonime leggono dalla replica più vicina. Gli utenti autenticati ottengono la consistenza read-your-writes tramite cookie segnalibro. |
"primary-first" | Come "auto", ma la prima query va sempre al primario. Da usare per siti con scritture molto frequenti. |
Come funziona
- I 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 hanno bisogno di garanzie di consistenza. - Gli utenti autenticati (editor, autori) ottengono sessioni basate su segnalibri. Dopo una scrittura, un cookie segnalibro garantisce che la richiesta successiva veda almeno quello stato.
- Le richieste di scrittura (
POST,PUT,DELETE) iniziano sempre dal database primario. - Le query in fase di build (collezioni di contenuti Astro) aggirano completamente le sessioni e usano direttamente il primario.
libSQL
libSQL è un fork di SQLite che supporta le 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
| Opzione | Tipo | Descrizione |
|---|---|---|
url | string | URL del database (libsql://... o file:...) |
authToken | string | Token di autenticazione a runtime per i database remoti (facoltativo 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 deploy Node.js che richiedono 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 stringa di connessione o con parametri singoli:
// 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,
});
| Opzione | Tipo | Descrizione |
|---|---|---|
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 | Abilita SSL |
pool.min | number | Connessioni minime del pool (predefinito 0) |
pool.max | number | Connessioni massime del pool (predefinito 10) |
pool.connectionTimeoutMillis | number | Attesa massima della connessione (predefinito di pg: 0, nessun timeout) |
pool.idleTimeoutMillis | number | Durata dei client inattivi (predefinito di pg: 10.000 ms) |
migrationConnectionStringEnv | string | Nome della variabile della stringa di connessione per le migrazioni (predefinito DATABASE_URL) |
Imposta pool.connectionTimeoutMillis su un valore diverso da zero per limitare per quanto tempo una richiesta attende quando PostgreSQL non è raggiungibile o nessuna connessione del pool diventa disponibile. Imposta pool.idleTimeoutMillis su 0 per mantenere aperti i client inattivi finché il pool non viene chiuso. Omettere una delle due opzioni mantiene il valore predefinito di pg.
Requisiti del ruolo del database
EmDash crea e aggiorna le proprie tabelle PostgreSQL. Le migrazioni del core creano e modificano le tabelle di sistema e delle collezioni, i tipi di contenuto creano tabelle ec_*, e l’aggiunta o la rimozione di un campo modifica la tabella della sua collezione. Il ruolo PostgreSQL configurato ha quindi bisogno dell’autorità sullo schema per tutta la vita del sito, non solo durante la configurazione iniziale.
Usa un unico ruolo canonico per EmDash. Ha bisogno di:
CONNECTsul database;USAGEeCREATEsullo schema attivo;- la proprietà di ogni tabella e funzione di EmDash, direttamente o tramite l’appartenenza con
INHERITal ruolo proprietario; e SELECT,INSERT,UPDATEeDELETEsu quelle tabelle.
Non deve essere un superutente, avere CREATEDB o CREATEROLE, né creare estensioni. PostgreSQL non fornisce un privilegio ALTER o DROP sulle tabelle: quelle operazioni appartengono al proprietario dell’oggetto e ai ruoli che ereditano i suoi privilegi. Concedere ALL su una tabella a un altro ruolo non rende quel ruolo un proprietario. EmDash non esegue SET ROLE, quindi un’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 stringa di connessione di EmDash; usa un ruolo esistente del tuo provider o crea un login dedicato. Concedigli l’accesso con una connessione amministrativa, sostituendo i nomi del tuo database, dello schema e del ruolo:
GRANT CONNECT ON DATABASE app TO emdash_app;
GRANT USAGE, CREATE ON SCHEMA public TO emdash_app;
Queste concessioni 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 né imposta search_path, quindi verifica la connessione prima del deploy:
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 che i suoi oggetti siano isolati da public. È facoltativo ed è più semplice da configurare prima della prima configurazione di EmDash. Un database dedicato a EmDash non ha bisogno di uno schema separato.
Supponendo 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 una proprietà mista. I siti esistenti dovrebbero mantenere il proprio schema attuale 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 deploy:
database: postgres({
connectionString: process.env.DATABASE_URL,
pool: { min: 2, max: 20 },
});
Hyperdrive
Usa l’adattatore hyperdrive() per eseguire EmDash su Cloudflare Workers con un database PostgreSQL esistente — o compatibile con Postgres (ad esempio PlanetScale Postgres). Hyperdrive raggruppa 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
Prepara prima il ruolo PostgreSQL. Poi crea la configurazione Hyperdrive con la stringa di connessione di quel ruolo e aggiungi il binding alla tua 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
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
binding | string | "HYPERDRIVE" | Nome del binding Hyperdrive primario (cache disabilitata) |
cachedBinding | string | — | Binding facoltativo con cache abilitata per le letture anonime (vedi sotto) |
preferUncachedAfterWriteMs | number | 60000* | Dopo la pubblicazione di un contenuto, preferisci binding per questi ms nelle letture pubbliche anonime (allinealo al max_age di Hyperdrive) |
migrationConnectionStringEnv | string | CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> | Variabile d’ambiente che contiene l’URL diretto dell’origin PostgreSQL per emdash migrate |
max | number | 5 | Dimensione massima del pool di connessioni verso Hyperdrive nel Worker |
*Il valore predefinito 60000 si applica solo quando cachedBinding è impostato; altrimenti viene ignorato.
Servire letture anonime dalla cache
Per impostazione predefinita si disabilita completamente la cache di Hyperdrive, perché l’admin e le scritture richiedono la consistenza read-after-write. Ma le richieste pubbliche anonime che usano GET o HEAD possono tollerare una breve finestra di obsolescenza. Se questo compromesso è accettabile, esegui due configurazioni Hyperdrive sullo stesso database: una con la cache disattivata (il binding primario) e una con la cache attivata (cachedBinding). EmDash instrada quelle richieste pubbliche anonime attraverso il binding con cache e ogni altra richiesta attraverso il primario senza 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 ogni richiesta:
- Letture anonime di percorsi del sito pubblico (
GET/HEAD, nessuna sessione, non sotto/_emdash) →cachedBindingcon cache abilitata, tranne per una breve finestra dopo la pubblicazione di un contenuto (60 s per impostazione predefinita; impostapreferUncachedAfterWriteMssulmax_agedel tuo Hyperdrive) in cui EmDash preferisce ilbindingsenza cache, così una ricostruzione non può rialimentare le cache edge/oggetto con risultati Hyperdrive ancora obsoleti. - Richieste autenticate (editor, autori) →
bindingsenza cache. - Richieste di mutazione (
POST,PUT,PATCH,DELETE, incluse quelle anonime) →bindingsenza cache. - Qualsiasi richiesta sotto
/_emdash(admin, configurazione, autenticazione, API interne), anche unGETanonimo →bindingsenza cache. - Migrazioni a runtime e cold start → sempre il
bindingprimario. - Migrazioni gestite dal deploy → si connettono direttamente all’origin PostgreSQL usando
migrationConnectionStringEnv; non usano mai nessuno dei due binding Hyperdrive.
Opzionale: usare un ruolo in cache separato
Le migrazioni, la configurazione, le richieste autenticate e le richieste di scrittura esplicite usano sempre il binding primario. Un ruolo separato per cachedBinding non ha bisogno della proprietà dello schema né di CREATE, ma ha bisogno di CONNECT, USAGE sullo schema e SELECT su ogni tabella usata dal sito pubblico.
Le richieste pubbliche anonime GET e HEAD possono anche registrare gli accessi ai reindirizzamenti e i 404. Per conservare queste funzionalità, il ruolo in cache ha inoltre bisogno di UPDATE su _emdash_redirects e di SELECT, INSERT, UPDATE e DELETE su _emdash_404_log. I plugin o il 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 sue migrazioni iniziali. Gli esempi seguenti usano lo schema facoltativo emdash; sostituiscilo con il tuo schema attivo, ad esempio 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 gli stessi 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 alle singole tabelle di EmDash e aggiorna quelle concessioni quando vengono aggiunte collezioni o altri oggetti dello schema.
Migrazioni principali
EmDash esegue per impostazione predefinita le migrazioni principali in modo automatico per ogni dialetto supportato. La build e la sincronizzazione di Astro producono anche un .emdash/migrations.json validato e privo di segreti, che emdash migrate può applicare prima del deploy. SQLite, libSQL, PostgreSQL, D1 e l’origin PostgreSQL diretto dietro Hyperdrive dispongono di esecutori di deploy.
Consulta Gestire le migrazioni del database core per le credenziali della destinazione, la serializzazione in CI, la policy di runtime auto/check/manual e il ripristino da record sconosciuti o scritture D1 ambigue.
Per PostgreSQL, le migrazioni a runtime passano attraverso la connessione configurata; le migrazioni a runtime di Hyperdrive usano sempre il suo binding primario. Le migrazioni Hyperdrive gestite dal deploy si connettono direttamente all’origin PostgreSQL. Le migrazioni principali possono creare tabelle, indici e funzioni, modificare 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 configurazione non può riparare i privilegi del database mancanti, perché le migrazioni a runtime vengono eseguite prima della configurazione.
Finché la procedura guidata di configurazione non è stata completata, EmDash applica anche una sola volta, alla prima richiesta, lo schema e la struttura di un file seed. I contenuti di esempio del seed vengono applicati solo quando un amministratore li sceglie nella procedura guidata di configurazione. Il seed viene letto da .emdash/seed.json, dal percorso in package.json#emdash.seed oppure da seed/seed.json — quello trovato per primo — e incorporato nella build in fase di compilazione. Se nessuno è presente, viene usato un seed predefinito integrato. Gli avvii successivi su un database esistente lasciano intatto il suo contenuto.
Usare database separati per ambienti separati
Dai a sviluppo, preview, staging e produzione il loro database. Un deploy di preview che punta alla produzione può eseguire migrazioni principali o comandi distruttivi del modello di contenuto su 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 ciascun ambiente di runtime. Conserva le credenziali nei segreti di runtime, non in astro.config.mjs.