ハンズオン

データベースのセットアップ

Postgres と Drizzle ORM を設定 — 認証、課金、ストレージ、タスク、各種アプリテーブルを支える中核です。

データベースは現代の SaaS の中心です。認証、課金、クレジット、ストレージ、利用状況の記録、さらにはデモ機能まで、すべてが DB を通ります。私たちはしばしば「CRUD エンジニア」と冗談を言いますが、作成・読み取り・更新・削除こそが日々の本質です。スキーマが大きくなるほど、DB を土台として扱うことで、アプリ全体の予測可能性が高まります。

本ガイドは DB を最優先に扱います。スキーマが機能にどう対応するか、Drizzle で型安全な CRUD をどう行うかを説明し、その後に接続・マイグレーション・検証の手順を示します。

なぜ DB が最初か

  • 事実のソース: 認証・課金・ファイル・タスクは DB に永続化され、サービス/API 層で結合されます。
  • スキーマ → 型 → UI: src/db/schema.ts の Drizzle モデルから安全な型が生成され、モデルやルートで共有されます。
  • CRUD は UI そのもの: 多くの画面は読み書き可能な一覧や詳細です。テーブル設計が良ければ UI 実装は速くなります。

本テンプレートの主要ドメイン(テーブル → 機能)

  • 認証: userssessionsaccountsverifications(Better Auth)。
  • 課金とクレジット: orderscredits が購入と台帳を管理。
  • ストレージ: files が S3/R2 のメタデータとライフサイクル(uploading → active → deleted)を保持。
  • 利用状況とタスク(AI): tasks が queued/running/completed を記録し、credits_trans_no で台帳にひも付け。
  • コンテンツ: posts
  • グロースとフィードバック: affiliatesfeedbacks
  • デモ: reservation_servicesreservations がスケジューリング/デポジットのパターンを提示。

コードにおける CRUD ファーストのパターン

  • モデルは src/models/* にあり、型付きヘルパーを提供:
    • src/models/file.ts: insertFilefindFileByUuidupdateFileByUuidlistFilesByUsersoftDeleteFile
    • src/models/task.ts: insertTaskfindTaskByUuidgetTasksByUserUuidupdateTaskStatus
  • サービスや API ルートはこれらのヘルパーを呼び出します。DB アクセスは集中管理しましょう。
  • 新機能の進め方: src/db/schema.ts でモデリング → pnpm drizzle-kit generatepnpm drizzle-kit migratesrc/models/<domain>.ts を追加 → src/services/* とルートから利用。

1. Drizzle ORM とは?

Drizzle ORM は TypeScript と Postgres をつなぐ型安全なレイヤーです。src/db/schema.ts に定義したスキーマが唯一の真実となり、ここから以下を行います。

  • drizzle-kit でマイグレーション SQL を生成
  • 生成したマイグレーションを Postgres に適用
  • アプリ全体で型を共有 (users.$inferSelect など)

SQL ファイルを手書きする必要はありません。スキーマを編集し、Drizzle にマイグレーション作成と適用を任せます。

2. データベースのシナリオを決める

状況に合わせて .env を設定してください。

シナリオ推奨内容
ローカル開発のみDocker で Postgres を起動 (docker run ... postgres:16) し、DATABASE_URLpostgres://postgres:postgres@localhost:5432/postgres に設定。
マネージド Postgres (Neon, Supabase, Railway など)プロバイダーが発行する接続文字列を使用し、マイグレーションを実行する IP を許可。
環境ごとに別 DBローカル用は .env、本番用はホスティング側のシークレットに設定。
既存 DB にテーブル追加Drizzle は src/db/schema.ts に定義したテーブルのみを変更します。名前の衝突がないか確認し、必要ならスキーマ (schema) を分けます。

メモ: Drizzle はデータベース自体を作成しません。マイグレーション前に空の DB を用意してください。

3. 環境変数を設定する

接続文字列と Better Auth のシークレットを定義します。.env.env.local.env.development を自動で読み込みます。

.env
DATABASE_URL="postgresql://user:password@host:5432/db?sslmode=require"
BETTER_AUTH_SECRET="$(openssl rand -base64 32)"
BETTER_AUTH_URL="http://localhost:3000"

値を変更したら pnpm dev を再起動して反映させてください。

4. スキーマ関連のファイルを把握する

  • src/db/schema.tsBetter Auth のテーブル(userssessionsaccountsverifications)とアプリ独自テーブル: orderscreditsfilestaskspostsaffiliatesfeedbacksreservation_servicesreservations
  • src/db/config.ts – drizzle-kit の設定 (スキーマパスと Postgres URL)。
  • src/lib/auth.ts – Better Auth のフィールドと Drizzle の列をマッピング。users の列名を変更した場合はここも更新します。

新しい列やテーブルを追加するときは schema.ts を編集し、必要であれば auth.ts のマッピングも更新します。読み取り性能のため、schema.ts で適切にインデックス(uniqueIndex/index)を定義することを推奨します。

5. コード変更後にマイグレーションを生成する

schema.ts を変更したら以下を実行します。

pnpm drizzle-kit generate --config src/db/config.ts

想定される出力:

Reading config file 'src/db/config.ts'
...
[✓] Your SQL migration file ➜ src/db/migrations/0002_add_user_flag.sql 🚀

生成されるファイル:

  • src/db/migrations/<timestamp>_*.sql
  • src/db/migrations/meta/_journal.json

どちらも Git にコミットします。

6. マイグレーションを Postgres に適用する

生成済み SQL をデータベースに流します。

pnpm drizzle-kit migrate --config src/db/config.ts

成功例:

Applying migrations from src/db/migrations
Migration 0002_add_user_flag.sql executed in 380 ms
All migrations applied!

認証エラーが出る場合は DATABASE_URL を確認してください。タイムアウトする場合は IP 制限やコンテナの起動をチェックします。

7. 結果を確認する

任意の Postgres クライアントでテーブルや列を確認します。psql の例:

\dt
SELECT * FROM sessions LIMIT 1;
SELECT email_verified FROM users LIMIT 1;

Better Auth のテーブルに加え、アプリの主要テーブルも見えるはずです。簡単な CRUD のスモークテストとして、モデルヘルパー経由で 1 行を insert して取得してみてください(例: POST /api/storage/uploads で最小の files を作成後、SELECT * FROM files LIMIT 1;)。

8. 認証フローを試す

マイグレーション適用後に pnpm dev を再起動し、/ja/signup (または任意のロケール) でユーザー登録をテストします。ホームへリダイレクトされ、userssessions にレコードが作成されます。

9. トラブルシューティング

  • Error: model "userss" not foundsrc/lib/auth.tsusers を参照しているか確認。古いサンプルから usePlural をコピーしていたら削除してください。
  • カラムが見つからないschema.ts を変えたのに generatemigrate を忘れている可能性があります。もう一度実行します。
  • 接続できない – DB が存在するか、認証情報が正しいか、ネットワーク設定 (SSL/許可 IP) を確認します。
  • ゼロからやり直したい – テーブルを手動で削除するか、drizzle-kit の SQL を利用してロールバックし、その後再度 generatemigrate を実施します。

常に「コードを変更 → マイグレーション生成 → 適用 → 検証」の流れを守れば、認証フローで 500 エラーが出るのを防げます。