各デプロイメントにつき 1 つのデータベースアダプターを選択します。データベースはコンテンツモデル、エントリ、ユーザー、設定、プラグインデータを保持します。メディアバイナリは別のストレージバックエンドに配置します。
概要
| データベース | 使用するケース | ランタイム |
|---|---|---|
| SQLite | 1 つの Node.js プロセスが永続ディスクを持つ場合 | Node.js またはローカル開発 |
| D1 | サイトが Cloudflare Workers で動作し Cloudflare SQL を使用する場合 | Cloudflare Workers |
| Hyperdrive | サイトが Workers で動作し既存の PostgreSQL オリジンを使用する必要がある場合 | Cloudflare Workers |
| PostgreSQL | 複数の Node.js プロセスが 1 つの共有データベースを必要とする場合 | Node.js |
| libSQL | Node.js デプロイメントがリモートの SQLite 互換データベースを必要とする場合 | Node.js |
D1 は Cloudflare テンプレートのデフォルトです。SQLite は最もシンプルな Node.js オプションですが、書き込み可能な永続ボリュームと運用データベースバックアップが必要です。
SQLite
SQLite は Node.js 組み込みのデータベースドライバーを使用し、Node.js デプロイメントで最もシンプルなオプションです。
import { sqlite } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
}),
],
});
設定
| オプション | 型 | 説明 |
|---|---|---|
url | string | file: プレフィックス付きファイルパス |
ファイルパス
url は file: で始まる必要があります:
// 相対パス
database: sqlite({ url: "file:./data/emdash.db" });
// 絶対パス
database: sqlite({ url: "file:/var/data/emdash.db" });
// 環境変数から
database: sqlite({ url: `file:${process.env.DATABASE_PATH}` });
Cloudflare D1
D1 は Cloudflare のサーバーレス SQLite データベースです。Cloudflare Workers にデプロイする場合に使用します。
import { d1 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: d1({ binding: "DB" }),
}),
],
});
設定
| オプション | 型 | デフォルト | 説明 |
|---|---|---|---|
binding | string | — | wrangler.jsonc の D1 バインディング名 |
session | string | "disabled" | 読み取りレプリケーションモード(下記参照) |
bookmarkCookie | string | "__em_d1_bookmark" | セッションブックマーク用 Cookie 名 |
Wrangler バインディング
wrangler.jsonc
{
"d1_databases": [
{
"binding": "DB",
"database_name": "emdash-db"
}
]
} wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "emdash-db" Wrangler はデプロイ中にこのバインディングから不足している D1 データベースをプロビジョニングできます。EmDash のマイグレーションは別のステップです。完全なバインディングセットは Cloudflare へのデプロイを、マイグレーションのランブックはコアデータベースマイグレーションの管理を参照してください。
読み取りレプリカ
D1 はグローバルに分散されたサイトの読み取りレイテンシーを低減するために読み取りレプリケーションをサポートしています。有効にすると、読み取りクエリは常にプライマリデータベースにアクセスする代わりに近くのレプリカにルーティングされます。
EmDash は D1 Sessions API を使用してこれを透過的に管理します。session オプションで有効にします:
import { d1 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: d1({
binding: "DB",
session: "auto",
}),
}),
],
});
セッションモード
| モード | 動作 |
|---|---|
"disabled" | セッションなし。すべてのクエリがプライマリに送信される。デフォルト。 |
"auto" | 匿名リクエストは最寄りのレプリカから読み取る。認証済みユーザーはブックマーク Cookie で読み取り後の書き込み一貫性を取得。 |
"primary-first" | "auto" と同様だが、最初のクエリは常にプライマリに送信される。書き込みが非常に頻繁なサイト向け。 |
仕組み
- 匿名の訪問者 は
first-unconstrainedを取得 — 最も低いレイテンシーのために最寄りのレプリカから読み取る。匿名ユーザーは書き込まないため、一貫性の保証は不要。 - 認証済みユーザー(編集者、著者)はブックマークベースのセッションを取得。書き込み後、ブックマーク Cookie が次のリクエストで少なくともその状態を確認できるようにする。
- 書き込みリクエスト(
POST、PUT、DELETE)は常にプライマリデータベースから開始。 - ビルド時クエリ(Astro Content Collections)はセッションを完全にバイパスし、直接プライマリを使用。
libSQL
libSQL はリモート接続をサポートする SQLite のフォークです。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,
}),
}),
],
});
設定
| オプション | 型 | 説明 |
|---|---|---|
url | string | データベース URL(libsql://... または file:...) |
authToken | string | リモートデータベース用ランタイム認証トークン(ローカルではオプション) |
migrationAuthTokenEnv | string | マイグレーショントークン変数名(デフォルト TURSO_AUTH_TOKEN) |
ローカル開発
開発中はローカルの libSQL ファイルを使用します:
database: libsql({ url: "file:./data.db" });
PostgreSQL
PostgreSQL はフルリレーショナルデータベースを必要とする Node.js デプロイメントでサポートされています。
import { postgres } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: postgres({
connectionString: process.env.DATABASE_URL,
}),
}),
],
});
設定
接続文字列または個別のパラメータで接続できます:
// 接続文字列
database: postgres({
connectionString: "postgres://user:password@localhost:5432/emdash",
});
// 個別パラメータ
database: postgres({
host: "localhost",
port: 5432,
database: "emdash",
user: "emdash",
password: process.env.DB_PASSWORD,
ssl: true,
});
| オプション | 型 | 説明 |
|---|---|---|
connectionString | string | PostgreSQL 接続 URL |
host | string | データベースホスト |
port | number | データベースポート |
database | string | データベース名 |
user | string | データベースユーザー |
password | string | データベースパスワード |
ssl | boolean | SSL を有効にする |
pool.min | number | プール最小接続数(デフォルト 0) |
pool.max | number | プール最大接続数(デフォルト 10) |
pool.connectionTimeoutMillis | number | 最大接続待ち時間(pg デフォルト:0、タイムアウトなし) |
pool.idleTimeoutMillis | number | アイドルクライアント寿命(pg デフォルト:10,000 ms) |
migrationConnectionStringEnv | string | マイグレーション接続文字列変数名(デフォルト DATABASE_URL) |
pool.connectionTimeoutMillis をゼロ以外の値に設定して、PostgreSQL に到達できない場合やプールされた接続が利用可能にならない場合のリクエスト待機時間を制限します。pool.idleTimeoutMillis を 0 に設定して、プールが閉じるまでアイドルクライアントを開いたままにします。どちらのオプションも省略すると pg のデフォルトが保持されます。
データベースロール要件
EmDash は独自の PostgreSQL テーブルを作成・更新します。コアマイグレーションはシステムテーブルとコレクションテーブルを作成・変更し、コンテンツタイプは ec_* テーブルを作成し、フィールドの追加や削除はそのコレクションテーブルを変更します。設定された PostgreSQL ロールには、初期設定時だけでなく、サイトのライフタイム全体にわたってスキーマ権限が必要です。
EmDash 用に 1 つの正規ロールを使用します。必要なのは:
- データベースへの
CONNECT; - アクティブスキーマへの
USAGEとCREATE; - すべての EmDash テーブルと関数の所有権(直接、または所有ロールへの
INHERIT付きメンバーシップ経由);および - それらのテーブルへの
SELECT、INSERT、UPDATE、DELETE。
スーパーユーザーである必要はなく、CREATEDB や CREATEROLE の権限も、拡張の作成も不要です。PostgreSQL はテーブルの ALTER や DROP 権限を提供しません:これらの操作はオブジェクト所有者とその権限を継承するロールに属します。別のロールにテーブルの ALL を付与しても、そのロールは所有者にはなりません。EmDash は SET ROLE を実行しないため、継承なしで設定されたメンバーシップでは不十分です。
ほとんどのインストールではデータベースの既存のスキーマ(通常は public)を使用できます。データベースが EmDash 専用の場合、これが最もシンプルなオプションです。以下の例では、emdash_app は EmDash の接続文字列のログインロールです。既存のプロバイダーロールを使用するか、専用のログインを作成してください。管理接続でアクセスを付与し、データベース、スキーマ、ロール名を置き換えてください:
GRANT CONNECT ON DATABASE app TO emdash_app;
GRANT USAGE, CREATE ON SCHEMA public TO emdash_app;
これらの権限付与によりロールは新しいオブジェクトを作成できます。既存のテーブルの所有者は変更しません。既存のサイトで所有者が混在している場合は PostgreSQL 所有権修復ランブックを使用してください。
EmDash は PostgreSQL のアクティブな current_schema() を使用します。スキーマの作成や search_path の設定は行わないため、デプロイ前に接続を確認してください:
SELECT
current_database(),
session_user,
current_user,
current_schema(),
current_setting('search_path');
オプション:専用スキーマの使用
EmDash が別のアプリケーションとデータベースを共有する場合や、そのオブジェクトを public から分離したい場合は、専用スキーマを使用します。これはオプションであり、EmDash の初回セットアップ前に設定するのが最も簡単です。EmDash 専用のデータベースでは別のスキーマは不要です。
正規ロール emdash_app がすでに存在すると仮定して、管理接続でそのスキーマを作成・選択します:
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;
これは public からの既存のインストールの移動や、混在する所有権の修復は行いません。既存のサイトは現在のスキーマを維持し、代わりに PostgreSQL 所有権修復ランブックを使用してください。
接続プーリング
アダプターは pg.Pool を使用します。デプロイメントに合わせてプールサイズを調整してください:
database: postgres({
connectionString: process.env.DATABASE_URL,
pool: { min: 2, max: 20 },
});
Hyperdrive
hyperdrive() アダプターを使用して、既存の PostgreSQL — または Postgres 互換(例:PlanetScale Postgres)— データベースを使用して Cloudflare Workers で EmDash を実行します。Hyperdrive は Cloudflare のネットワーク上で接続をプールし高速化します。EmDash の PostgreSQL ダイアレクトがクエリを実行します。
import { hyperdrive, r2 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: hyperdrive({ binding: "HYPERDRIVE" }),
storage: r2({ binding: "MEDIA" }),
}),
],
});
要件
- サイトに
pg >= 8.16.3がインストール済み(pnpm add pg) compatibility_flags: ["nodejs_compat"]compatibility_date >= "2024-09-23"
セットアップ
まず PostgreSQL ロールを準備します。次にそのロールの接続文字列で Hyperdrive 設定を作成し、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>" 設定
| オプション | 型 | デフォルト | 説明 |
|---|---|---|---|
binding | string | "HYPERDRIVE" | プライマリ(キャッシュ無効)Hyperdrive バインディング名 |
cachedBinding | string | — | 匿名読み取り用のオプションのキャッシュ有効バインディング(下記参照) |
preferUncachedAfterWriteMs | number | 60000* | コンテンツ公開後、匿名パブリック読み取りでこの時間(ms)binding を優先(Hyperdrive の max_age に合わせる) |
migrationConnectionStringEnv | string | CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> | emdash migrate 用の直接 PostgreSQL オリジン URL を含む環境変数 |
max | number | 5 | Hyperdrive への Worker 内接続プールの最大サイズ |
*デフォルト 60000 は cachedBinding が設定されている場合のみ適用。それ以外では無視。
キャッシュからの匿名読み取り提供
デフォルトでは Hyperdrive キャッシュを完全に無効にします。管理パネルと書き込みには読み取り後書き込みの一貫性が必要だからです。しかし、GET または HEAD の匿名パブリックリクエストは短い陳腐化ウィンドウを許容できます。このトレードオフが許容できる場合、同じデータベース上で 2 つの Hyperdrive 設定を実行します:キャッシュ無効のもの(プライマリ binding)とキャッシュ有効のもの(cachedBinding)。EmDash はこれらの匿名パブリックリクエストをキャッシュ有効バインディング経由で、その他のリクエストをキャッシュなしのプライマリ経由でルーティングします。
# プライマリ — キャッシュ OFF(管理、認証済みリクエスト、書き込み、マイグレーション用)
wrangler hyperdrive create emdash-db \
--connection-string "postgres://user:password@host/db?sslmode=verify-full" \
--caching-disabled
# キャッシュ有効 — 同じデータベースロールと接続文字列、キャッシュ ON
wrangler hyperdrive create emdash-db-cached \
--connection-string "postgres://user:password@host/db?sslmode=verify-full"
{
"hyperdrive": [
{ "binding": "HYPERDRIVE", "id": "<キャッシュ無効 ID>" },
{ "binding": "HYPERDRIVE_CACHED", "id": "<キャッシュ有効 ID>" }
]
}
database: hyperdrive({ binding: "HYPERDRIVE", cachedBinding: "HYPERDRIVE_CACHED" });
これは Cloudflare がキャッシュ用に文書化している2 設定パターンです。EmDash はリクエストごとにどのバインディングを使用するか決定します:
- パブリックパスの匿名読み取り(
GET/HEAD、セッションなし、/_emdash配下でない)→ キャッシュ有効cachedBinding。ただしコンテンツ公開後の短いウィンドウ(デフォルト 60s;preferUncachedAfterWriteMsを Hyperdrive のmax_ageに設定)では EmDash はキャッシュなしbindingを優先し、リビルドがまだ陳腐化した Hyperdrive 結果からエッジ/オブジェクトキャッシュを再シードしないようにする。 - 認証済みリクエスト(エディター、著者)→ キャッシュなし
binding。 - ミューテーションリクエスト(
POST、PUT、PATCH、DELETE、匿名含む)→ キャッシュなしbinding。 /_emdash配下のすべてのリクエスト(管理、セットアップ、認証、内部 API)、匿名GET含む → キャッシュなしbinding。- ランタイムマイグレーションとコールドスタート → 常にプライマリ
binding。 - デプロイ管理マイグレーション →
migrationConnectionStringEnvを使用して PostgreSQL オリジンに直接接続。Hyperdrive バインディングは使用しない。
オプション:別のキャッシュ用ロールの使用
マイグレーション、セットアップ、認証済みリクエスト、明示的な書き込みリクエストは常にプライマリ binding を使用します。cachedBinding の別のロールにはスキーマ所有権や CREATE は不要ですが、CONNECT、スキーマ USAGE、パブリックサイトで使用されるすべてのテーブルへの SELECT が必要です。
匿名パブリック GET および HEAD リクエストはリダイレクトヒットと 404 も記録できます。これらの機能を保持するには、キャッシュ用ロールに _emdash_redirects への UPDATE と _emdash_404_log への SELECT、INSERT、UPDATE、DELETE も必要です。パブリック GET または HEAD 中に書き込むプラグインやアプリケーションコードはさらに多くが必要な場合があります。制限されたキャッシュ用ロールでサイトをテストしていない限り、両方のバインディングに同じロールを使用してください。
EmDash が初期マイグレーションを完了した後にキャッシュ用ロールを追加します。以下の例ではオプションの emdash スキーマを使用しています。アクティブスキーマ(public など)に置き換えてください。プロバイダーの管理ロールでログインとデータベース設定を作成します:
CREATE ROLE emdash_cached LOGIN PASSWORD 'シークレットに置き換え';
GRANT CONNECT ON DATABASE app TO emdash_cached;
ALTER ROLE emdash_cached IN DATABASE app SET search_path = emdash;
次にスキーマとテーブルの所有者である emdash_app として接続し、既存および将来のテーブルへのアクセスを付与します:
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;
両方のロールで接続し、cachedBinding を有効にする前に同じ current_database() と current_schema() を報告することを確認します。共有スキーマでは GRANT SELECT ON ALL TABLES は無関係なテーブルも公開します。代わりに個別の EmDash テーブルへのアクセスを付与し、コレクションやその他のスキーマオブジェクトが追加されたときにそれらの権限を更新してください。
コアマイグレーション
EmDash はサポートされているすべてのダイアレクトに対してデフォルトで自動的にコアマイグレーションを実行します。Astro ビルドと同期は検証済みでシークレットを含まない .emdash/migrations.json も出力し、emdash migrate がデプロイ前に適用できます。SQLite、libSQL、PostgreSQL、D1、Hyperdrive の背後にある直接 PostgreSQL オリジンにはデプロイエグゼキュータがあります。
ターゲット資格情報、CI シリアライゼーション、auto/check/manual ランタイムポリシー、不明なレコードや曖昧な D1 書き込みからの復旧については、コアデータベースマイグレーションの管理を参照してください。
PostgreSQL の場合、ランタイムマイグレーションは設定された接続を通じて実行されます。Hyperdrive ランタイムマイグレーションは常にプライマリバインディングを使用します。デプロイ管理の Hyperdrive マイグレーションは PostgreSQL オリジンに直接接続します。コアマイグレーションはテーブル、インデックス、関数の作成、カラムや制約の変更・削除、既存行の更新を行えます。接続して行を変更できるが既存の EmDash オブジェクトを所有していないロールでは不十分です。セットアップウィザードは不足しているデータベース権限を修復できません。ランタイムマイグレーションはセットアップの前に実行されるためです。
データベースが空(コレクションなし)でセットアップウィザードが完了していない場合、EmDash は初回起動時にシードファイルも適用します。シードは .emdash/seed.json、package.json#emdash.seed のパス、または seed/seed.json のいずれか最初に見つかったものから読み取られ、コンパイル時にビルドにインライン化されます。いずれも存在しない場合、組み込みのデフォルトシードが使用されます。既存のデータベースに対する以降の起動ではそのコンテンツはそのままにされます。
環境ごとに別のデータベースを使用する
開発、プレビュー、ステージング、本番にそれぞれ独自のデータベースを用意してください。本番を指すプレビューデプロイメントは、ライブデータに対してコアマイグレーションや破壊的なコンテンツモデルコマンドを実行する可能性があります。
Cloudflare の場合、対応する Wrangler 環境の下に各 D1 または Hyperdrive バインディングを定義し、Wrangler コマンドに --env を渡します。Node.js の場合、各ランタイム環境に異なるデータベース URL を注入します。資格情報は astro.config.mjs ではなく、ランタイムシークレットに保持してください。