デプロイごとに、データベースアダプターを 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 で最も簡単な選択肢ですが、書き込み可能な永続ボリュームが 1 つ必要で、運用としてのデータベースバックアップも必要です。
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: で始まる必要があります。
// 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 は、SQLite データベースをwrite-ahead logging(WAL)モードで開きます。サイトの実行中、SQLite は emdash.db-wal や emdash.db-shm のような 2 つの追加ファイルをデータベースの隣に保持します。これらを作成するには、プロセスがデータベースのディレクトリへの書き込み権限を持っている必要があります。
-wal ファイルには、まだメインのデータベースファイルに反映されていないコミット済みの変更が含まれていることがあります。.db ファイルだけをコピーするのではなく、SQLite のバックアップコマンドでバックアップしてください。SQLite のバックアップと復旧を参照してください。
WAL には共有メモリが必要なため、データベースは NFS や SMB のようなネットワークファイルシステムではなく、ローカルディスクまたはブロックボリュームに置いてください。
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 により read-your-writes の一貫性を得ます。 |
"primary-first" | "auto" と同様ですが、最初のクエリは常にプライマリに送られます。書き込みが非常に頻繁なサイトで使用します。 |
仕組み
- 匿名の訪問者には
first-unconstrainedが適用されます。読み取りは最も近いレプリカに送られ、レイテンシが最小になります。匿名ユーザーは書き込みを行わないため、一貫性の保証は必要ありません。 - 認証済みユーザー(エディター、作成者)には、ブックマークベースのセッションが適用されます。書き込み後は、ブックマーク Cookie によって、次のリクエストが少なくともその状態を確認できるようになります。
- 書き込みリクエスト(
POST、PUT、DELETE)は、常にプライマリデータベースから開始されます。 - ビルド時のクエリ(Astro のコンテンツコレクション)は、セッションを完全に迂回し、プライマリを直接使用します。
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,
}),
}),
],
});
設定
接続文字列、または個別のパラメーターで接続できます。
// 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,
});
| オプション | 型 | 説明 |
|---|---|---|
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 を 0 以外の値に設定すると、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(または PlanetScale Postgres のような 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 | Worker 内から Hyperdrive への接続プールの最大サイズ |
*デフォルトの 60000 は cachedBinding を設定した場合にのみ適用され、それ以外では無視されます。
キャッシュから匿名の読み取りを提供する
デフォルトでは Hyperdrive のキャッシュを完全に無効にします。管理画面と書き込みには、書き込み後の読み取りの一貫性が必要だからです。しかし、GET または HEAD を使う匿名の公開リクエストは、短い古さの許容時間を受け入れられます。このトレードオフを受け入れられる場合は、同じデータベースに対して 2 つの Hyperdrive 構成を実行します。1 つはキャッシュ無効(プライマリの binding)、もう 1 つはキャッシュ有効(cachedBinding)です。EmDash は、それらの匿名の公開リクエストをキャッシュ有効なバインディング経由にし、それ以外のすべてのリクエストをキャッシュなしのプライマリ経由にします。
# 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" });
これは、Cloudflare がキャッシュについてドキュメント化している2 構成パターンです。EmDash は、リクエストごとにどのバインディングを使うかを決定します。
- 公開サイトのパスへの匿名の読み取り(
GET/HEAD、セッションなし、/_emdash配下ではない)→ キャッシュ有効なcachedBinding。ただし、コンテンツの公開後の短い期間(デフォルトは 60 秒。preferUncachedAfterWriteMsを Hyperdrive のmax_ageに設定します)は、再ビルドによって、まだ古い Hyperdrive の結果からエッジやオブジェクトのキャッシュが再び満たされることがないよう、EmDash はキャッシュなしのbindingを優先します。 - 認証済みリクエスト(エディター、作成者)→ キャッシュなしの
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 'replace-with-a-secret';
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 の場合は、各 D1 または Hyperdrive のバインディングを対応する Wrangler の環境の下に定義し、Wrangler コマンドに --env を渡します。Node.js の場合は、各ランタイム環境に異なるデータベース URL を注入します。認証情報は、astro.config.mjs ではなく、ランタイムのシークレットに保管してください。