Deployment
このガイドは pnpm dev から、デプロイされた Worker + D1 + R2 バックエンドまで導きます。Quick Start に従っており、それを出荷したいことを前提としています。
- Workers が有効な Cloudflare アカウント
wranglerCLI 3+ がインストール済み(pnpm add -g wrangler)wrangler login完了済み- ローカルに Node.js 20+
wrangler.jsonc
Section titled “wrangler.jsonc”{ "$schema": "node_modules/wrangler/config-schema.json", "name": "my-todo-app", "main": "./worker.ts", "compatibility_date": "2026-01-01", "compatibility_flags": ["nodejs_compat"],
"d1_databases": [ { "binding": "DB", "database_name": "my-todo-app-prod", "database_id": "<from wrangler d1 create output>" } ],
"r2_buckets": [ { "binding": "BUCKET", "bucket_name": "my-todo-app-blobs" } ],
"durable_objects": { "bindings": [ { "name": "COORDINATOR", "class_name": "SyncCoordinator" } ] },
"migrations": [ { "tag": "v1", "new_sqlite_classes": ["SyncCoordinator"] } ],
"observability": { "enabled": true }}d1_databases.DB— plasma が書き込む SQLite DB。r2_buckets.BUCKET—file()column のバイトが置かれる場所。durable_objects.COORDINATOR— リアルタイムの WebSocket poke ファンアウト用。ポーリングのみで満足なら任意。
D1 と R2 の作成
Section titled “D1 と R2 の作成”wrangler d1 create my-todo-app-prod# 出力された database_id を wrangler.jsonc に貼り付ける
wrangler r2 bucket create my-todo-app-blobsWorker のエントリポイント
Section titled “Worker のエントリポイント”import { createSyncHandler, ensureSchema, fromD1, gcOrphanedBlobs, r2Storage, runMigrations, SyncCoordinator,} from "@sh1n4ps/plasma-server"import { schema, mutators, SCHEMA_VERSION } from "./shared/schema"
export { SyncCoordinator } // wrangler が DO クラスを見つけられるよう re-export
interface Env { DB: D1Database BUCKET: R2Bucket COORDINATOR: DurableObjectNamespace}
export default { async fetch(req: Request, env: Env, ctx: ExecutionContext) { const executor = fromD1(env.DB)
// 初回リクエストの DDL。CREATE TABLE IF NOT EXISTS は no-op なので、 // table が存在すれば安価。 await ensureSchema({ schema, executor })
const handler = createSyncHandler({ schema, mutators, executor, schemaVersion: SCHEMA_VERSION,
auth: async (req) => { const token = req.headers.get("authorization")?.replace("Bearer ", "") if (!token) return { ok: false, reason: "no token" } const user = await verifyJWT(token) if (!user) return { ok: false, reason: "bad token" } return { ok: true, clientGroupID: user.id, clientID: req.headers.get("x-client") ?? crypto.randomUUID(), ctx: { userId: user.id }, } },
blobs: { default: r2Storage({ bucket: env.BUCKET }), }, })
return handler(req) },
async scheduled(_event, env, ctx) { // orphaned な blob を毎週 GC。cron トリガーはダッシュボードで設定。 const executor = fromD1(env.DB) ctx.waitUntil( gcOrphanedBlobs({ executor, storage: r2Storage({ bucket: env.BUCKET }), minOrphanAgeMs: 7 * 24 * 3600 * 1000, limit: 500, }), ) },}デプロイ時の migration
Section titled “デプロイ時の migration”migration は自動ではありません。2 つのパターンがあります:
パターン A — 1 回限りの管理ルート
Section titled “パターン A — 1 回限りの管理ルート”// worker.ts の fetch ディスパッチに追加if (url.pathname === "/admin/migrate") { if (req.headers.get("x-admin-token") !== env.ADMIN_TOKEN) { return new Response("forbidden", { status: 403 }) } const result = await runMigrations({ schema, executor: fromD1(env.DB) }) return Response.json(result)}デプロイスクリプトからトリガーします:
wrangler deploycurl -X POST -H "x-admin-token: $ADMIN_TOKEN" https://my-app.workers.dev/admin/migrateパターン B — wrangler 経由のデプロイ前 migration
Section titled “パターン B — wrangler 経由のデプロイ前 migration”生 SQL として表現できる schema 変更には wrangler d1 migrations を使えます — ただし schema.ts との同期を保つのは自己責任です。plasma の runMigrations が canonical なパスです。
wrangler deployWorker は my-todo-app.<your-subdomain>.workers.dev にデプロイされます。フロントエンドの endpoint をそこに向けます:
const plasma = createPlasmaClient({ ..., endpoint: "https://my-todo-app.<your-subdomain>.workers.dev/sync",})カスタムドメインには、Cloudflare ダッシュボードでルートを追加し、endpoint を更新してください。
{ "vars": { "PUBLIC_KEY": "..." }}シークレット(ADMIN_TOKEN、JWT 署名キーなど):
wrangler secret put ADMIN_TOKENwrangler secret put JWT_SIGNING_KEYworker.ts でアクセス:
interface Env { DB: D1Database BUCKET: R2Bucket COORDINATOR: DurableObjectNamespace PUBLIC_KEY: string // vars から ADMIN_TOKEN: string // secrets から JWT_SIGNING_KEY: string // secrets から}Observability
Section titled “Observability”wrangler.jsonc の "observability": { "enabled": true } は Workers observability ダッシュボードを有効にします。plasma の sync ハンドラは Log Streams に現れる構造化エラーを発行します:
push-http/pull-http/network/schema-mismatch/rebase-replay/blob-upload-failed— クライアント側のonErrorハンドラをconsole.errorに接続していれば、そこからblob-read-auth-cap-hit— blob 読み取り auth チェック中にreadAuthMaxRefsに達したときに現れる
Workers が提供する以上のメトリクスには、Analytics Engine を使って毎分の push / pull 呼び出しをカウントし、ダッシュボードには Grafana Cloud を使ってください。
フロントエンドのデプロイ
Section titled “フロントエンドのデプロイ”plasma はフロントエンドのホストを指定しません。Quick Start は Vite を使います; それを以下にデプロイできます:
- Cloudflare Workers Static Assets(同一ドメインのデプロイに推奨 —
/syncに CORS がない) - 別ドメインにフロントエンドを置き、Worker に CORS を有効にした Vercel または Netlify
- フロントエンドを
dist/にビルドして wrangler をそこに向ける 同じ Worker のassetsバインディング
ローカル開発
Section titled “ローカル開発”wrangler dev は Miniflare でエミュレートされた D1 と R2 に対して Worker をローカルで実行します:
wrangler dev --local開発中はフロントエンドの endpoint を http://localhost:8787/sync に向けられます。
プロダクションチェックリスト
Section titled “プロダクションチェックリスト”-
wrangler.jsoncに D1、R2、DO バインディングが正しく命名されている -
SCHEMA_VERSIONがクライアントとサーバーのエクスポート間で一致している -
auth()が単に{ok: true}を返すのではなく、実際のトークンを検証している - クライアントの
endpointが HTTPS で、デプロイされた Worker またはそのカスタムドメインを指している -
runMigrationsがデプロイ時に実行される -
file()を使うならgcOrphanedBlobsのscheduledハンドラがある - フロントエンドのビルドが同一ドメイン(
/sync)にあるか、Worker に CORS が設定されている -
wrangler.jsoncで observability が有効
次に読むべきもの
Section titled “次に読むべきもの”- Migrations — デプロイ時に実行される DDL-diff ランタイム
- Files and blobs — R2 バケットのサイジングとライフサイクル
- Auth and permissions — プロダクションの
auth()ハンドラの書き方 - Multi-region — 単一 D1 を超えたスケーリング