データベース管理
このプロジェクトでは、PrismaとCloudflare D1を使用してデータベースを管理しています。
データベース構成
Cloudflare D1
- 本番環境: Cloudflare D1(SQLiteベースの分散データベース)
- 開発環境: ローカルSQLiteファイル(
dev.db)
Prisma
- ORM: Prisma
- アダプター:
@prisma/adapter-d1(本番環境) - スキーマ:
packages/db/prisma/schema.prisma
データベーススキーマ
スキーマファイル
スキーマファイルは packages/db/prisma/schema.prisma に定義されています。
generator client { provider = "prisma-client-js" previewFeatures = ["driverAdapters"]}
datasource db { provider = "mysql" url = env("DATABASE_URL")}
model Post { id String @id @default(uuid()) title String slug String @unique date DateTime description String? content String createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at")
@@map("posts")}Prisma Client の使用
Client の作成
import { PrismaD1 } from "@prisma/adapter-d1";import { PrismaClient } from "@prisma/client";
export function createPrismaClient(options: { d1?: D1Database; databaseUrl?: string;}): PrismaClient { const { d1, databaseUrl } = options;
// 本番環境: D1アダプターを使用 if (d1) { const adapter = new PrismaD1(d1); return new PrismaClient({ adapter }); }
// 開発環境: MySQLを使用 return new PrismaClient({ datasources: { db: { url: databaseUrl || process.env.DATABASE_URL, }, }, });}Client の使用
import { createPrismaClient } from "@portfolio/db";
export class D1PostRepository { private prisma: PrismaClient;
constructor(d1: D1Database) { this.prisma = createPrismaClient({ d1 }); }
async findById(id: string): Promise<Post | null> { return await this.prisma.post.findUnique({ where: { id }, }); }}マイグレーション
マイグレーションの作成
# スキーマを変更後、マイグレーションファイルを生成cd packages/dbbunx prisma migrate dev --name migration_name
# マイグレーションファイルが生成される# prisma/migrations/YYYYMMDDHHMMSS_migration_name/migration.sql開発環境でのマイグレーション
# スキーマをデータベースにプッシュ(開発環境のみ)cd packages/dbbun run push
# または、直接実行bunx dotenv-cli -e ../../.env -- prisma db push本番環境でのマイグレーション
# D1データベースにマイグレーションを適用cd packages/dbwrangler d1 migrations apply portfolio-db
# または、package.jsonのスクリプトを使用bun run migrateマイグレーションの確認
# マイグレーションの状態を確認bunx prisma migrate status
# 適用済みのマイグレーションを確認wrangler d1 migrations list portfolio-dbシードデータ
シードスクリプト
import { createPrismaClient } from "./client";
export async function seed(d1?: D1Database, databaseUrl?: string) { const prisma = createPrismaClient({ d1, databaseUrl });
// シードデータの投入 await prisma.post.createMany({ data: [ { title: "First Post", slug: "first-post", date: new Date(), content: "Content here", }, ], });}シードの実行
# 開発環境でシードを実行cd packages/dbbun run seed
# 本番環境でシードを実行(注意: 本番データを上書きする可能性あり)wrangler d1 execute portfolio-db --file=./seed.sqlデータベースの操作
Prisma Studio
Prisma Studioを使用してデータベースを視覚的に操作できます。
# Prisma Studioを起動cd packages/dbbun run dev
# ブラウザで http://localhost:5555 を開くD1データベースの操作
# D1データベースの一覧を表示wrangler d1 list
# SQLクエリを実行wrangler d1 execute portfolio-db --command "SELECT * FROM posts"
# SQLファイルを実行wrangler d1 execute portfolio-db --file=./query.sql
# ローカルD1データベースを使用(開発環境)wrangler d1 execute portfolio-db --local --command "SELECT * FROM posts"データベースのバックアップ
D1データベースのエクスポート
# データベースをエクスポートwrangler d1 export portfolio-db --output=./backup.sqlデータベースのインポート
# データベースをインポートwrangler d1 execute portfolio-db --file=./backup.sqlパフォーマンス最適化
インデックスの追加
model Post { id String @id @default(uuid()) slug String @unique // ユニークインデックス title String date DateTime
@@index([date]) // インデックスの追加 @@map("posts")}クエリの最適化
// ✅ Good: 必要なフィールドのみ取得const posts = await prisma.post.findMany({ select: { id: true, title: true, slug: true, },});
// ❌ Bad: すべてのフィールドを取得const posts = await prisma.post.findMany();ページネーション
// ページネーションを使用const posts = await prisma.post.findMany({ skip: (page - 1) * pageSize, take: pageSize, orderBy: { date: "desc", },});トラブルシューティング
マイグレーションエラー
# マイグレーションをリセット(開発環境のみ)bunx prisma migrate reset
# マイグレーションを再適用bunx prisma migrate deployPrisma Client の再生成
# Prisma Clientを再生成cd packages/dbbun run generate
# または、直接実行bunx prisma generateデータベース接続エラー
# データベースURLを確認echo $DATABASE_URL
# D1データベースの接続をテストwrangler d1 execute portfolio-db --command "SELECT 1"ベストプラクティス
1. スキーマの変更
- スキーマを変更したら、必ずマイグレーションを作成
- マイグレーションファイルは、リポジトリにコミット
- 本番環境への適用前に、開発環境でテスト
2. データの整合性
- 外部キー制約を使用してデータの整合性を保つ
- トランザクションを使用して複数の操作を原子性を保証
3. パフォーマンス
- インデックスを適切に設定
- N+1問題を避ける(
includeやselectを使用) - ページネーションを実装
4. セキュリティ
- SQLインジェクションを防ぐ(Prismaが自動的に処理)
- 環境変数でデータベースURLを管理
- 本番環境の認証情報を適切に管理