家計簿アプリを作る #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"
    }
  ]
}

ハマりポイント②:namecompatibility_date が必要

wrangler d1 create の出力には d1_databases の情報しか含まれていません。wrangler.jsonc には namecompatibility_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.tsout を明示して出力先を揃えるのが確実です。

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 namecompatibility_date も必須
drizzle-kit generate のデフォルト出力先 drizzle フォルダ
wrangler d1 migrations apply のデフォルト参照先 migrations フォルダ
ズレの解消 drizzle.config.tsout: './migrations' を明示する
ローカルへの適用 --local フラグを付けて実行

次回は D1 への接続とデータ取得を実装します。

← トップページに戻る