Choisir une base de données

Sur cette page

Choisissez un adaptateur de base de données pour chaque déploiement. La base de données stocke le modèle de contenu, les entrées, les utilisateurs, les paramètres et les données des plugins. Les fichiers média appartiennent à un backend de stockage séparé.

Aperçu

Base de donnéesUtilisationEnvironnement d’exécution
SQLiteUn processus Node.js dispose d’un disque persistantNode.js ou développement local
D1Le site fonctionne sur Cloudflare Workers et doit utiliser Cloudflare SQLCloudflare Workers
HyperdriveLe site fonctionne sur Workers et doit utiliser une source PostgreSQL existanteCloudflare Workers
PostgreSQLPlusieurs processus Node.js ont besoin d’une base de données partagéeNode.js
libSQLUn déploiement Node.js a besoin d’une base distante compatible SQLiteNode.js

D1 est le choix par défaut pour les modèles Cloudflare. SQLite est l’option Node.js la plus simple, mais elle nécessite un volume persistant en écriture et des sauvegardes opérationnelles de la base de données.

SQLite

SQLite utilise le pilote de base de données intégré de Node.js et constitue l’option la plus simple pour les déploiements Node.js.

import { sqlite } from "emdash/db";

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

Configuration

OptionTypeDescription
urlstringChemin de fichier avec préfixe file:

Chemin de fichier

L’url doit commencer par file: :

// Chemin relatif
database: sqlite({ url: "file:./data/emdash.db" });

// Chemin absolu
database: sqlite({ url: "file:/var/data/emdash.db" });

// Depuis une variable d'environnement
database: sqlite({ url: `file:${process.env.DATABASE_PATH}` });

Cloudflare D1

D1 est la base de données SQLite serverless de Cloudflare. Utilisez-la lors du déploiement sur Cloudflare Workers.

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

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

Configuration

OptionTypePar défautDescription
bindingstringNom du binding D1 depuis wrangler.jsonc
sessionstring"disabled"Mode de réplication de lecture (voir ci-dessous)
bookmarkCookiestring"__em_d1_bookmark"Nom du cookie pour les signets de session

Binding Wrangler

wrangler.jsonc

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

wrangler.toml

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

Wrangler peut provisionner une base D1 manquante à partir de ce binding lors du déploiement. Les migrations EmDash sont une étape séparée. Suivez Déployer sur Cloudflare pour l’ensemble complet des bindings et Gérer les migrations de base de données core pour le guide de migration.

Répliques de lecture

D1 prend en charge la réplication de lecture pour réduire la latence de lecture des sites distribués mondialement. Lorsqu’elle est activée, les requêtes de lecture sont acheminées vers des répliques proches au lieu de toujours accéder à la base de données primaire.

EmDash utilise l’API Sessions de D1 pour gérer cela de manière transparente. Activez-la avec l’option session :

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

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

Modes de session

ModeComportement
"disabled"Pas de sessions. Toutes les requêtes vont à la primaire. Par défaut.
"auto"Les requêtes anonymes lisent depuis la réplique la plus proche. Les utilisateurs authentifiés obtiennent la cohérence lecture-après-écriture via des cookies de signets.
"primary-first"Comme "auto", mais la première requête va toujours à la primaire. Pour les sites avec des écritures très fréquentes.

Fonctionnement

  • Les visiteurs anonymes obtiennent first-unconstrained — les lectures vont à la réplique la plus proche pour la latence la plus faible. Comme les utilisateurs anonymes n’écrivent jamais, ils n’ont pas besoin de garanties de cohérence.
  • Les utilisateurs authentifiés (éditeurs, auteurs) obtiennent des sessions basées sur des signets. Après une écriture, un cookie de signet garantit que la prochaine requête voit au moins cet état.
  • Les requêtes d’écriture (POST, PUT, DELETE) commencent toujours à la base de données primaire.
  • Les requêtes au moment du build (Astro content collections) contournent entièrement les sessions et utilisent directement la primaire.

libSQL

libSQL est un fork de SQLite qui prend en charge les connexions distantes. Utilisez-le lorsque vous avez besoin d’une base distante sans 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,
			}),
		}),
	],
});

Configuration

OptionTypeDescription
urlstringURL de la base de données (libsql://... ou file:...)
authTokenstringToken d’authentification à l’exécution pour les bases distantes (optionnel en local)
migrationAuthTokenEnvstringNom de variable du token de migration (par défaut TURSO_AUTH_TOKEN)

Développement local

Utilisez un fichier libSQL local pendant le développement :

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

PostgreSQL

PostgreSQL est pris en charge pour les déploiements Node.js nécessitant une base de données relationnelle complète.

import { postgres } from "emdash/db";

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

Configuration

Vous pouvez vous connecter avec une chaîne de connexion ou des paramètres individuels :

// Chaîne de connexion
database: postgres({
	connectionString: "postgres://user:password@localhost:5432/emdash",
});

// Paramètres individuels
database: postgres({
	host: "localhost",
	port: 5432,
	database: "emdash",
	user: "emdash",
	password: process.env.DB_PASSWORD,
	ssl: true,
});
OptionTypeDescription
connectionStringstringURL de connexion PostgreSQL
hoststringHôte de la base de données
portnumberPort de la base de données
databasestringNom de la base de données
userstringUtilisateur de la base de données
passwordstringMot de passe de la base de données
sslbooleanActiver SSL
pool.minnumberConnexions minimum du pool (par défaut 0)
pool.maxnumberConnexions maximum du pool (par défaut 10)
pool.connectionTimeoutMillisnumberAttente maximale de connexion (par défaut pg : 0, pas de timeout)
pool.idleTimeoutMillisnumberDurée de vie du client inactif (par défaut pg : 10 000 ms)
migrationConnectionStringEnvstringNom de variable de chaîne de connexion de migration (par défaut DATABASE_URL)

Définissez pool.connectionTimeoutMillis à une valeur non nulle pour limiter le temps d’attente d’une requête lorsque PostgreSQL est inaccessible ou qu’aucune connexion du pool n’est disponible. Définissez pool.idleTimeoutMillis à 0 pour maintenir les clients inactifs ouverts jusqu’à la fermeture du pool. Omettre l’une ou l’autre option préserve la valeur par défaut de pg.

Exigences du rôle de base de données

EmDash crée et met à jour ses propres tables PostgreSQL. Les migrations core créent et modifient les tables système et de collections, les types de contenu créent des tables ec_*, et l’ajout ou la suppression d’un champ modifie la table de sa collection. Le rôle PostgreSQL configuré a donc besoin d’une autorité sur le schéma pendant toute la durée de vie du site, pas seulement lors de la configuration initiale.

Utilisez un rôle canonique pour EmDash. Il a besoin de :

  • CONNECT sur la base de données ;
  • USAGE et CREATE sur le schéma actif ;
  • la propriété de chaque table et fonction EmDash, soit directement, soit par l’adhésion avec INHERIT au rôle propriétaire ; et
  • SELECT, INSERT, UPDATE et DELETE sur ces tables.

Il n’a pas besoin d’être superutilisateur, d’avoir CREATEDB ou CREATEROLE, ni de créer des extensions. PostgreSQL ne fournit pas de droit ALTER ou DROP sur les tables : ces opérations appartiennent au propriétaire de l’objet et aux rôles qui héritent de ses privilèges. Accorder ALL sur une table à un rôle différent ne fait pas de ce rôle un propriétaire. EmDash n’exécute pas SET ROLE, donc l’adhésion configurée sans héritage n’est pas suffisante.

La plupart des installations peuvent utiliser le schéma existant de la base de données, généralement public. C’est l’option la plus simple lorsque la base de données est dédiée à EmDash. Dans les exemples ci-dessous, emdash_app est le rôle de connexion dans la chaîne de connexion d’EmDash ; utilisez un rôle de fournisseur existant ou créez un identifiant dédié. Accordez l’accès avec une connexion administrative, en substituant les noms de votre base de données, schéma et rôle :

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

Ces droits permettent au rôle de créer de nouveaux objets. Ils ne changent pas le propriétaire des tables existantes ; utilisez le guide de réparation de propriété PostgreSQL lorsqu’un site existant a des propriétaires mixtes.

EmDash utilise le current_schema() actif de PostgreSQL. Il ne crée pas de schéma et ne définit pas search_path, vérifiez donc la connexion avant le déploiement :

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

Optionnel : utiliser un schéma dédié

Utilisez un schéma dédié lorsqu’EmDash partage une base de données avec une autre application ou lorsque vous souhaitez isoler ses objets de public. C’est optionnel et plus facile à configurer avant la première installation d’EmDash. Une base de données dédiée à EmDash n’a pas besoin d’un schéma séparé.

En supposant que le rôle canonique emdash_app existe déjà, créez et sélectionnez son schéma avec une connexion administrative :

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;

Cela ne déplace pas une installation existante de public et ne répare pas la propriété mixte. Les sites existants doivent conserver leur schéma actuel et utiliser le guide de réparation de propriété PostgreSQL à la place.

Pool de connexions

L’adaptateur utilise pg.Pool. Ajustez la taille du pool en fonction de votre déploiement :

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

Hyperdrive

Utilisez l’adaptateur hyperdrive() pour exécuter EmDash sur Cloudflare Workers avec une base PostgreSQL — ou compatible Postgres (par ex. PlanetScale Postgres) — existante. Hyperdrive regroupe et accélère la connexion sur le réseau Cloudflare ; le dialecte PostgreSQL d’EmDash exécute les requêtes.

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

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

Prérequis

  • pg >= 8.16.3 installé dans votre site (pnpm add pg)
  • compatibility_flags: ["nodejs_compat"]
  • compatibility_date >= "2024-09-23"

Installation

Préparez d’abord le rôle PostgreSQL. Puis créez la configuration Hyperdrive avec la chaîne de connexion de ce rôle et ajoutez le binding à votre configuration Wrangler :

wrangler hyperdrive create emdash-db \
  --connection-string "postgres://user:password@host/db?sslmode=verify-full" \
  --caching-disabled

wrangler.jsonc

{
  "hyperdrive": [
    {
      "binding": "HYPERDRIVE",
      "id": "<votre-id-hyperdrive>"
    }
  ]
}

wrangler.toml

[[hyperdrive]]
binding = "HYPERDRIVE"
id = "<votre-id-hyperdrive>"

Configuration

OptionTypePar défautDescription
bindingstring"HYPERDRIVE"Nom du binding primaire Hyperdrive (cache désactivé)
cachedBindingstringBinding optionnel avec cache activé pour les lectures anonymes (voir ci-dessous)
preferUncachedAfterWriteMsnumber60000*Après une publication de contenu, préférer binding pendant cette durée (ms) sur les lectures publiques anonymes (à aligner sur le max_age d’Hyperdrive)
migrationConnectionStringEnvstringCLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING>Variable d’environnement contenant l’URL directe de la source PostgreSQL pour emdash migrate
maxnumber5Taille maximale du pool de connexions in-Worker vers Hyperdrive

*La valeur par défaut 60000 ne s’applique que lorsque cachedBinding est défini ; ignorée sinon.

Servir les lectures anonymes depuis le cache

Par défaut, vous désactivez complètement le cache d’Hyperdrive, car l’admin et les écritures ont besoin de la cohérence lecture-après-écriture. Mais les requêtes publiques anonymes avec GET ou HEAD peuvent tolérer une courte fenêtre d’obsolescence. Si ce compromis est acceptable, exécutez deux configurations Hyperdrive sur la même base de données : une avec le cache désactivé (le binding primaire) et une avec le cache activé (cachedBinding). EmDash route ces requêtes publiques anonymes via le binding avec cache activé et toute autre requête via le primaire sans cache.

# Primaire — cache DÉSACTIVÉ (pour admin, requêtes authentifiées, écritures, migrations)
wrangler hyperdrive create emdash-db \
  --connection-string "postgres://user:password@host/db?sslmode=verify-full" \
  --caching-disabled

# Avec cache — MÊME rôle et chaîne de connexion, cache ACTIVÉ
wrangler hyperdrive create emdash-db-cached \
  --connection-string "postgres://user:password@host/db?sslmode=verify-full"
{
	"hyperdrive": [
		{ "binding": "HYPERDRIVE", "id": "<id-cache-désactivé>" },
		{ "binding": "HYPERDRIVE_CACHED", "id": "<id-cache-activé>" }
	]
}
database: hyperdrive({ binding: "HYPERDRIVE", cachedBinding: "HYPERDRIVE_CACHED" });

C’est le modèle à deux configurations que Cloudflare documente pour le cache. EmDash décide quel binding utiliser par requête :

  • Lectures anonymes de chemins publics (GET/HEAD, pas de session, pas sous /_emdash) → cachedBinding avec cache activé, sauf pendant une courte fenêtre après une publication de contenu (par défaut 60s ; définissez preferUncachedAfterWriteMs à votre max_age Hyperdrive) lorsqu’EmDash préfère le binding sans cache pour qu’une reconstruction ne puisse pas remplir les caches edge/objet avec des résultats Hyperdrive encore obsolètes.
  • Requêtes authentifiées (éditeurs, auteurs) → binding sans cache.
  • Requêtes de mutation (POST, PUT, PATCH, DELETE, y compris anonymes) → binding sans cache.
  • Toute requête sous /_emdash (admin, configuration, auth, APIs internes), même un GET anonyme → binding sans cache.
  • Migrations à l’exécution et démarrage à froid → toujours le binding primaire.
  • Migrations gérées par le déploiement → se connectent directement à la source PostgreSQL via migrationConnectionStringEnv ; elles n’utilisent jamais aucun binding Hyperdrive.

Optionnel : utiliser un rôle avec cache séparé

Les migrations, la configuration, les requêtes authentifiées et les requêtes d’écriture explicites utilisent toujours le binding primaire. Un rôle séparé pour cachedBinding n’a pas besoin de la propriété du schéma ni de CREATE, mais il a besoin de CONNECT, USAGE du schéma et SELECT sur chaque table utilisée par le site public.

Les requêtes publiques anonymes GET et HEAD peuvent aussi enregistrer les accès aux redirections et les 404. Pour préserver ces fonctionnalités, le rôle avec cache a également besoin de UPDATE sur _emdash_redirects et SELECT, INSERT, UPDATE et DELETE sur _emdash_404_log. Les plugins ou le code applicatif qui écrivent pendant un GET ou HEAD public peuvent nécessiter plus. Utilisez le même rôle pour les deux bindings à moins d’avoir testé le site avec un rôle avec cache restreint.

Ajoutez le rôle avec cache après qu’EmDash ait terminé ses migrations initiales. Les exemples ci-dessous utilisent le schéma optionnel emdash ; substituez votre schéma actif, tel que public. Créez l’identifiant et les paramètres de base de données avec le rôle administratif de votre fournisseur :

CREATE ROLE emdash_cached LOGIN PASSWORD 'remplacer-par-un-secret';
GRANT CONNECT ON DATABASE app TO emdash_cached;
ALTER ROLE emdash_cached IN DATABASE app SET search_path = emdash;

Puis connectez-vous en tant que emdash_app, le propriétaire du schéma et des tables, pour accorder l’accès aux tables existantes et futures :

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;

Connectez-vous avec les deux rôles et vérifiez qu’ils rapportent le même current_database() et current_schema() avant d’activer cachedBinding. Sur un schéma partagé, GRANT SELECT ON ALL TABLES expose aussi les tables non liées. Accordez plutôt l’accès aux tables EmDash individuelles et mettez à jour ces droits lorsque des collections ou d’autres objets de schéma sont ajoutés.

Migrations core

EmDash exécute les migrations core automatiquement par défaut pour chaque dialecte supporté. Astro build et sync émettent aussi un .emdash/migrations.json validé et sans secrets, que emdash migrate peut appliquer avant le déploiement. SQLite, libSQL, PostgreSQL, D1 et la source PostgreSQL directe derrière Hyperdrive ont des exécuteurs de déploiement.

Voir Gérer les migrations de base de données core pour les identifiants de cible, la sérialisation CI, la politique d’exécution auto/check/manual et la récupération après des enregistrements inconnus ou des écritures D1 ambiguës.

Pour PostgreSQL, les migrations à l’exécution passent par la connexion configurée ; les migrations Hyperdrive à l’exécution utilisent toujours le binding primaire. Les migrations Hyperdrive gérées par le déploiement se connectent directement à la source PostgreSQL. Les migrations core peuvent créer des tables, des index et des fonctions, modifier ou supprimer des colonnes et des contraintes, et mettre à jour des lignes existantes. Un rôle qui peut se connecter et modifier des lignes mais qui ne possède pas les objets EmDash existants n’est pas suffisant. L’assistant de configuration ne peut pas réparer les privilèges de base de données manquants car les migrations à l’exécution s’exécutent avant la configuration.

Si la base de données est vide (pas de collections) et que l’assistant de configuration n’a pas été complété, EmDash applique aussi un fichier seed au premier démarrage. Le seed est lu depuis .emdash/seed.json, le chemin dans package.json#emdash.seed, ou seed/seed.json — le premier trouvé — et intégré dans le build à la compilation. Si aucun n’est présent, un seed par défaut intégré est utilisé. Les démarrages suivants contre une base de données existante ne modifient pas son contenu.

Utiliser des bases de données séparées pour des environnements séparés

Donnez au développement, à la prévisualisation, au staging et à la production leur propre base de données. Un déploiement de prévisualisation pointant vers la production peut exécuter des migrations core ou des commandes destructives de modèle de contenu contre des données en direct.

Pour Cloudflare, définissez chaque binding D1 ou Hyperdrive sous l’environnement Wrangler correspondant et passez --env aux commandes Wrangler. Pour Node.js, injectez une URL de base de données différente dans chaque environnement d’exécution. Gardez les identifiants dans les secrets d’exécution, pas dans astro.config.mjs.