家計簿アプリを作る #2:Drizzle でスキーマ設計 + D1 マイグレーション
家計簿アプリ作成シリーズの第2回です。今回は Drizzle ORM でスキーマを定義し、Cloudflare D1 にマイグレーションを適用するところまでを記録します。ハマりポイントが多かったので詳しくまとめます。
Step 1:D1 データベースを作成する
npx wrangler d1 create kakeibo-app-db
実行すると、接続情報が表示されます。
[[d1_databases]]
binding = "DB"
database_name = "kakeibo-app-db"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
ハマりポイント①:wrangler d1 create はリモートに作成される
--local フラグをつけてローカル専用で作成しようとしましたが、このフラグは存在しませんでした。
# --local は存在しない
npx wrangler d1 create kakeibo-app-db --local # ← エラーになる
wrangler d1 create は常に Cloudflare アカウント上(リモート)に D1 データベースを作成するコマンドです。ローカル用の SQLite ファイルは wrangler dev や --local オプション付きの execute を実行したタイミングで自動生成されるため、ローカル専用の作成コマンドは不要です。
Step 2:wrangler.jsonc にバインディングを追記する
wrangler d1 create 実行後に表示された情報を wrangler.jsonc に追記します。--update-config フラグを使うと自動で追記してくれます。
npx wrangler d1 create kakeibo-app-db --update-config
手動で追記する場合は以下の形式です。
{
"name": "kakeibo-app",
"compatibility_date": "2026-06-01",
"d1_databases": [
{
"binding": "DB",
"database_name": "kakeibo-app-db",
"database_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
]
}
ハマりポイント②:name と compatibility_date が必要
wrangler d1 create の出力には d1_databases の情報しか含まれていません。wrangler.jsonc には name と compatibility_date も必須なので、忘れずに追記してください。
Step 3:Drizzle でスキーマを定義する
src/lib/server/db/schema.ts に収支テーブルを定義します。
import { int, text, sqliteTable } from 'drizzle-orm/sqlite-core';
export const transactions = sqliteTable('transactions', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
amount: int('amount').notNull(),
type: text('type').notNull(), // 'income' or 'expense'
category: text('category').notNull(),
memo: text('memo').notNull().default(''),
date: text('date').notNull()
});
| カラム | 型 | 説明 |
|---|---|---|
id |
text(UUID) | 主キー、自動生成 |
amount |
integer | 金額 |
type |
text | income(収入)または expense(支出) |
category |
text | カテゴリ |
memo |
text | メモ(省略時は空文字) |
date |
text | 日付 |
Step 4:マイグレーションファイルを生成する
npx drizzle-kit generate
スキーマの内容をもとに SQL ファイルが生成されます。
CREATE TABLE `transactions` (
`id` text PRIMARY KEY NOT NULL,
`amount` integer NOT NULL,
`type` text NOT NULL,
`category` text NOT NULL,
`memo` text DEFAULT '' NOT NULL,
`date` text NOT NULL
);
ハマりポイント③:出力先フォルダのズレ
drizzle-kit generate のデフォルト出力先は drizzle フォルダです。一方 wrangler d1 migrations apply が参照するデフォルトは migrations フォルダです。この2つが食い違っているため、何も設定しないとマイグレーション適用時にエラーになります。
✘ [ERROR] No migrations present at /Users/.../migrations.
drizzle.config.ts に out を明示して出力先を揃えるのが確実です。
export default defineConfig({
schema: './src/lib/server/db/schema.ts',
dialect: 'sqlite',
out: './migrations', // ← 明示する
dbCredentials: { url: process.env.DATABASE_URL },
});
Step 5:ローカル D1 にマイグレーションを適用する
npx wrangler d1 migrations apply kakeibo-app-db --local
--local フラグでローカルの SQLite に対して適用します。リモートの Cloudflare D1 には影響しません。
適用後、データが正しく入ったか確認します。
npx wrangler d1 execute DB --local --command="SELECT name FROM sqlite_master WHERE type='table'"
transactions テーブルが表示されれば完了です。
まとめ
| ポイント | 内容 |
|---|---|
wrangler d1 create |
常にリモートにDBを作成。--local フラグは存在しない |
wrangler.jsonc |
name と compatibility_date も必須 |
drizzle-kit generate のデフォルト出力先 |
drizzle フォルダ |
wrangler d1 migrations apply のデフォルト参照先 |
migrations フォルダ |
| ズレの解消 | drizzle.config.ts に out: './migrations' を明示する |
| ローカルへの適用 | --local フラグを付けて実行 |
次回は D1 への接続とデータ取得を実装します。