Scegli un adattatore di database per ogni distribuzione. Il database contiene il modello di contenuto, le voci, gli utenti, le impostazioni e i dati dei plugin. I file multimediali appartengono a un backend di archiviazione separato.
Panoramica
| Database | Quando usarlo | 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 | Una distribuzione Node.js necessita di un database remoto compatibile con SQLite | Node.js |
D1 è il 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 le distribuzioni 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 file con prefisso file: |
Percorso file
L’url deve iniziare con file::
// Percorso relativo
database: sqlite({ url: "file:./data/emdash.db" });
// Percorso assoluto
database: sqlite({ url: "file:/var/data/emdash.db" });
// Da variabile d'ambiente
database: sqlite({ url: `file:${process.env.DATABASE_PATH}` });
Cloudflare D1
D1 è il database SQLite serverless di Cloudflare. Usalo quando distribuisci 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 da wrangler.jsonc |
session | string | "disabled" | Modalità di replicazione 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ò provisionare un database D1 mancante da questo binding durante la distribuzione. Le migrazioni EmDash sono un passaggio separato. Segui Distribuire su Cloudflare per l’insieme completo dei binding e Gestire le migrazioni core del database per il runbook delle migrazioni.
Repliche di lettura
D1 supporta la replicazione di lettura per ridurre la latenza di lettura per siti distribuiti globalmente. Quando abilitata, le query di lettura vengono instradate verso repliche vicine invece di accedere sempre al database primario.
EmDash usa l’API Sessions di D1 per gestire questo 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à 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 coerenza lettura-dopo-scrittura tramite cookie segnalibro. |
"primary-first" | Come "auto", ma la prima query va sempre al primario. Usa 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 coerenza. - 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 (Astro content collections) bypassano completamente le sessioni e usano direttamente il primario.
libSQL
libSQL è un fork di SQLite che supporta connessioni remote. Usalo quando hai bisogno di 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 runtime per database remoti (opzionale in locale) |
migrationAuthTokenEnv | string | Nome 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 le distribuzioni 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 stringa di connessione o parametri individuali:
// Stringa di connessione
database: postgres({
connectionString: "postgres://user:password@localhost:5432/emdash",
});
// Parametri individuali
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 | 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 client inattivo (predefinito pg: 10.000 ms) |
migrationConnectionStringEnv | string | Nome variabile stringa di connessione migrazione (predefinito DATABASE_URL) |
Imposta pool.connectionTimeoutMillis a un valore diverso da zero per limitare quanto una richiesta attende quando PostgreSQL non è raggiungibile o nessuna connessione del pool diventa disponibile. Imposta pool.idleTimeoutMillis a 0 per mantenere i client inattivi aperti fino alla chiusura del pool. Omettere entrambe le opzioni preserva il valore predefinito di pg.
Requisiti del ruolo database
EmDash crea e aggiorna le proprie tabelle PostgreSQL. Le migrazioni core creano e modificano tabelle di sistema e di collezioni, i tipi di contenuto creano tabelle ec_*, e l’aggiunta o rimozione di un campo modifica la tabella della sua collezione. Il ruolo PostgreSQL configurato necessita quindi di autorità sullo schema per l’intera durata 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 superutente, avere CREATEDB o CREATEROLE, né creare estensioni. PostgreSQL non fornisce un grant ALTER o DROP sulle tabelle: queste operazioni appartengono al proprietario dell’oggetto e ai ruoli che ne ereditano i privilegi. Concedere ALL su una tabella a un ruolo diverso non rende quel ruolo 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. Questa è 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 fornitore esistente o crea un login dedicato. Concedi l’accesso con una connessione amministrativa, sostituendo i nomi del tuo 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 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 della distribuzione:
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 i suoi oggetti isolati da public. Questo è opzionale ed è più facile da configurare prima della prima configurazione di EmDash. Un database dedicato a EmDash non ha bisogno 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 dovrebbero mantenere il loro schema attuale e usare il runbook di riparazione proprietà PostgreSQL al suo posto.
Pool di connessioni
L’adattatore usa pg.Pool. Regola la dimensione del pool in base alla tua distribuzione:
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 — o compatibile con Postgres (es. PlanetScale Postgres) — esistente. Hyperdrive raggruppa e accelera la connessione sulla rete 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 iniziale
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": "<il-tuo-id-hyperdrive>"
}
]
} wrangler.toml
[[hyperdrive]]
binding = "HYPERDRIVE"
id = "<il-tuo-id-hyperdrive>" Configurazione
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
binding | string | "HYPERDRIVE" | Nome del binding Hyperdrive primario (cache disabilitato) |
cachedBinding | string | — | Binding opzionale con cache abilitato per letture anonime (vedi sotto) |
preferUncachedAfterWriteMs | number | 60000* | Dopo una pubblicazione, preferire binding per questi ms su letture pubbliche anonime (allineare al max_age di Hyperdrive) |
migrationConnectionStringEnv | string | CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> | Variabile d’ambiente contenente l’URL diretto dell’origine PostgreSQL per emdash migrate |
max | number | 5 | Dimensione massima del pool di connessioni in-Worker verso Hyperdrive |
*Il predefinito 60000 si applica solo quando cachedBinding è impostato; ignorato altrimenti.
Servire letture anonime dalla cache
Per impostazione predefinita si disabilita completamente il cache di Hyperdrive, perché l’admin e le scritture necessitano di coerenza lettura-dopo-scrittura. Ma le richieste pubbliche anonime con GET o HEAD possono tollerare una breve finestra di obsolescenza. Se questo compromesso è accettabile, esegui due configurazioni Hyperdrive sullo stesso database: una con cache disabilitato (il binding primario) e una con cache abilitato (cachedBinding). EmDash instrada quelle richieste pubbliche anonime attraverso il binding con cache e ogni altra richiesta attraverso il primario senza cache.
# Primario — cache DISABILITATO (per admin, richieste autenticate, scritture, migrazioni)
wrangler hyperdrive create emdash-db \
--connection-string "postgres://user:password@host/db?sslmode=verify-full" \
--caching-disabled
# Con cache — STESSO ruolo e stringa di connessione, cache ABILITATO
wrangler hyperdrive create emdash-db-cached \
--connection-string "postgres://user:password@host/db?sslmode=verify-full"
{
"hyperdrive": [
{ "binding": "HYPERDRIVE", "id": "<id-cache-disabilitato>" },
{ "binding": "HYPERDRIVE_CACHED", "id": "<id-cache-abilitato>" }
]
}
database: hyperdrive({ binding: "HYPERDRIVE", cachedBinding: "HYPERDRIVE_CACHED" });
Questo è il pattern a due configurazioni che Cloudflare documenta per il cache. EmDash decide quale binding usare per richiesta:
- Letture anonime di percorsi pubblici (
GET/HEAD, nessuna sessione, non sotto/_emdash) →cachedBindingcon cache, eccetto per una breve finestra dopo una pubblicazione (predefinito 60s; impostapreferUncachedAfterWriteMsal tuomax_agedi Hyperdrive) quando EmDash preferisce ilbindingsenza cache affinché una ricostruzione non possa riempire 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, auth, API interne), anche unGETanonimo →bindingsenza cache. - Migrazioni runtime e avvio a freddo → sempre il
bindingprimario. - Migrazioni gestite dalla distribuzione → si connettono direttamente all’origine PostgreSQL usando
migrationConnectionStringEnv; non usano mai nessun binding Hyperdrive.
Opzionale: usare un ruolo con 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 necessita di proprietà dello schema o CREATE, ma necessita di CONNECT, USAGE dello schema e SELECT su ogni tabella usata dal sito pubblico.
Le richieste pubbliche anonime GET e HEAD possono anche registrare hit di redirect e 404. Per preservare queste funzionalità, il ruolo con cache necessita inoltre di UPDATE su _emdash_redirects e 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 con cache ristretto.
Aggiungi il ruolo con cache dopo che EmDash ha completato le sue migrazioni iniziali. Gli esempi seguenti usano lo schema opzionale emdash; sostituisci il tuo schema attivo, come public. Crea il login e le impostazioni del database con il ruolo amministrativo del tuo fornitore:
CREATE ROLE emdash_cached LOGIN PASSWORD 'sostituire-con-un-segreto';
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 accesso a 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 singole tabelle EmDash e aggiorna quei grant quando vengono aggiunte collezioni o altri oggetti dello schema.
Migrazioni core
EmDash esegue le migrazioni core automaticamente per impostazione predefinita per ogni dialetto supportato. Astro build e sync emettono anche un .emdash/migrations.json validato e senza segreti, che emdash migrate può applicare prima della distribuzione. SQLite, libSQL, PostgreSQL, D1 e l’origine PostgreSQL diretta dietro Hyperdrive hanno esecutori di distribuzione.
Vedi Gestire le migrazioni core del database per le credenziali di destinazione, la serializzazione CI, la policy runtime auto/check/manual e il recupero da record sconosciuti o scritture D1 ambigue.
Per PostgreSQL, le migrazioni runtime passano attraverso la connessione configurata; le migrazioni runtime di Hyperdrive usano sempre il binding primario. Le migrazioni Hyperdrive gestite dalla distribuzione si connettono direttamente all’origine PostgreSQL. Le migrazioni core 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 runtime vengono eseguite prima della configurazione.
Se il database è vuoto (nessuna collezione) e la procedura guidata di configurazione non è stata completata, EmDash applica anche un file seed al primo avvio. Il seed viene letto da .emdash/seed.json, dal percorso in package.json#emdash.seed, o da 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 il suo contenuto invariato.
Usare database separati per ambienti separati
Dai a sviluppo, anteprima, staging e produzione il proprio database. Una distribuzione di anteprima che punta alla produzione può eseguire migrazioni core o comandi distruttivi del modello di contenuto contro dati in produzione.
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 di esecuzione. Mantieni le credenziali nei segreti di runtime, non in astro.config.mjs.