Tips & Tricks

Guide pratique d'utilisation de Drizzle ORM avec Claude Code

Découvrez guide pratique d'utilisation de Drizzle ORM avec Claude Code. Conseils pratiques et exemples de code inclus.

Drizzle ORM とは

Drizzle ORMはTypeScriptファーストの軽量ORMです。SQLに近い記法で型安全なクエリを書けるのが特徴です。Claude Codeと組み合わせれば、効率的にデータベース層を構築できます。

スキーマ定義

// db/schema.ts
import {
  pgTable,
  text,
  timestamp,
  boolean,
  integer,
  varchar,
  index,
} from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";

export const users = pgTable("users", {
  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
  email: varchar("email", { length: 255 }).notNull().unique(),
  name: varchar("name", { length: 100 }).notNull(),
  avatar: text("avatar"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull(),
}, (table) => ({
  emailIdx: index("email_idx").on(table.email),
}));

export const posts = pgTable("posts", {
  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
  title: varchar("title", { length: 255 }).notNull(),
  content: text("content").notNull(),
  published: boolean("published").default(false).notNull(),
  authorId: text("author_id").notNull().references(() => users.id),
  viewCount: integer("view_count").default(0).notNull(),
  publishedAt: timestamp("published_at"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  authorIdx: index("author_idx").on(table.authorId),
  publishedIdx: index("published_idx").on(table.published, table.publishedAt),
}));

export const comments = pgTable("comments", {
  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
  content: text("content").notNull(),
  authorId: text("author_id").notNull().references(() => users.id),
  postId: text("post_id").notNull().references(() => posts.id, { onDelete: "cascade" }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
});

リレーション定義

export const usersRelations = relations(users, ({ many, one }) => ({
  posts: many(posts),
  comments: many(comments),
}));

export const postsRelations = relations(posts, ({ one, many }) => ({
  author: one(users, {
    fields: [posts.authorId],
    references: [users.id],
  }),
  comments: many(comments),
}));

export const commentsRelations = relations(comments, ({ one }) => ({
  author: one(users, {
    fields: [comments.authorId],
    references: [users.id],
  }),
  post: one(posts, {
    fields: [comments.postId],
    references: [posts.id],
  }),
}));

クエリ操作

import { drizzle } from "drizzle-orm/node-postgres";
import { eq, and, like, desc, sql, count } from "drizzle-orm";
import * as schema from "./schema";

const db = drizzle(pool, { schema });

// 挿入
async function createPost(data: {
  title: string;
  content: string;
  authorId: string;
}) {
  const [post] = await db
    .insert(posts)
    .values(data)
    .returning();

  return post;
}

// 検索(ページネーション付き)
async function getPosts(params: {
  page?: number;
  perPage?: number;
  search?: string;
}) {
  const { page = 1, perPage = 20, search } = params;

  const conditions = [eq(posts.published, true)];
  if (search) {
    conditions.push(like(posts.title, `%${search}%`));
  }

  const [data, [{ total }]] = await Promise.all([
    db
      .select({
        id: posts.id,
        title: posts.title,
        publishedAt: posts.publishedAt,
        authorName: users.name,
        commentCount: count(comments.id),
      })
      .from(posts)
      .leftJoin(users, eq(posts.authorId, users.id))
      .leftJoin(comments, eq(posts.id, comments.postId))
      .where(and(...conditions))
      .groupBy(posts.id, users.name)
      .orderBy(desc(posts.publishedAt))
      .limit(perPage)
      .offset((page - 1) * perPage),

    db
      .select({ total: count() })
      .from(posts)
      .where(and(...conditions)),
  ]);

  return { data, total, page, perPage };
}

// 更新
async function updatePost(id: string, data: Partial<typeof posts.$inferInsert>) {
  const [updated] = await db
    .update(posts)
    .set({ ...data, updatedAt: new Date() })
    .where(eq(posts.id, id))
    .returning();

  return updated;
}

Relational Queries API

// Prismaライクなクエリ
async function getPostWithRelations(id: string) {
  return db.query.posts.findFirst({
    where: eq(posts.id, id),
    with: {
      author: {
        columns: { id: true, name: true, avatar: true },
      },
      comments: {
        with: {
          author: {
            columns: { id: true, name: true },
          },
        },
        orderBy: [desc(comments.createdAt)],
        limit: 10,
      },
    },
  });
}

マイグレーション

// drizzle.config.ts
import type { Config } from "drizzle-kit";

export default {
  schema: "./db/schema.ts",
  out: "./drizzle",
  dialect: "postgresql",
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
} satisfies Config;
# マイグレーション生成
npx drizzle-kit generate

# マイグレーション適用
npx drizzle-kit migrate

# Drizzle Studio(GUI)起動
npx drizzle-kit studio

Prisma との比較

特性DrizzlePrisma
バンドルサイズ軽量やや大きい
クエリ記法SQL寄り独自API
エッジ対応優秀制限あり
スキーマ定義TypeScript独自DSL
マイグレーションSQL生成自動管理

Claude Codeでの活用

Drizzle ORMの実装をClaude Codeに依頼する例です。Prismaとの比較はPrisma ORM完全ガイド、データベース連携についてはSupabase統合開発も参照してください。

Drizzle ORMでデータベース層を構築して。
- PostgreSQLのスキーマ定義
- リレーション付きのCRUDクエリ
- ページネーションと検索
- マイグレーション設定

Drizzle ORMの詳細はDrizzle ORM公式ドキュメントを参照してください。Claude Codeの活用法は公式ドキュメントで確認できます。

Summary

Drizzle ORMはSQLに近い記法と軽量さが魅力のORMです。Claude Codeを使えば、型安全なクエリとスキーマ設計を効率的に実装できます。エッジ環境での利用にも適しています。

#Claude Code #Drizzle ORM #database #TypeScript #SQL