Drizzle ORM 実戦ガイド:Prismaより90%軽量なTypeScript ORMの選択

先週、プロジェクトをVercelにデプロイし、トップページを開いたら3秒も待たされました。3秒です! ユーザーならとっくに離脱しています。
Vercelのバンドル分析を開き、14MBも占めているPrisma Clientのパッケージを見つめながら、私は人生を疑い始めました。ただの単純なユーザー検索なのに、なぜこんなに大量のコードを読み込む必要があるのでしょうか? さらに悪いことに、Lambdaのコールドスタート時間が狂うほど長かったのです——Prismaを使っていない関数は600msで立ち上がるのに、Prismaを使った関数は2.5秒もかかっていました。
「でもPrismaは便利じゃないか?」とあなたは言うかもしれません。確かに、私もそう思っていました。型安全性、自動マイグレーション、Prisma Studioという可視化ツール、必要なものはすべて揃っています。しかし問題は——**「重すぎる」**ことです。
もしあなたも、サーバーレス環境でのコールドスタートの遅さ、バンドルサイズの肥大化、複雑なクエリの制御不能(結局raw SQLを書く羽目になる)といった問題に直面しているなら、この記事はあなたのためのものです。今日紹介する Drizzle ORM は、コアパッケージがわずか7.4kbで、Prismaより90%以上軽量でありながら、同等の型安全性を提供します。
この記事では、Next.js + Drizzleのゼロからの設定、SQLライクなAPIの使い方、そしてDrizzleとPrismaの徹底的な性能比較を行います。最も重要なこととして、どのようなシナリオでDrizzleを選び、どのようなシナリオではまだPrismaが優れているのかをお伝えします。
なぜDrizzleが必要なのか? 既存ORMの痛点
Prismaの3大痛点
正直なところ、Prismaは多くの場面で優れた選択肢です。しかし長く使っていると、避けて通れない問題が出てきます。
痛点1:バンドルサイズの制御不能
Prisma v5の生成済みClientは14MBに達することがあります。これがどういうことかというと、Next.jsプロジェクト全体が2-3MBかもしれないのに、Prismaだけでその半分近くを占める可能性があるということです。Prisma 7ではRustバイナリを削除して1MBまで最適化されましたが、古いバージョンを使っていたり、バンドルサイズに敏感なプロジェクトでは大きな問題です。
私たちのチームにはCloudflare Workersにデプロイするリアルタイムチャットアプリがあり、バンドルサイズの上限は1MBでした。Prismaでは完全にオーバーしていたため、最終的に別のソリューションに乗り換えるしかありませんでした。
痛点2:サーバーレスでの遅いコールドスタート
GitHubにはIssue #10724という数年来の議論があります。PrismaのLambdaコールドスタートが遅すぎる問題です。データは明白です:
- ORMなしの関数:~600ms
- Prisma v5使用の関数:~2.5s
- Prisma v7使用の関数:~1.5s(改善されたが、まだ遅い)
この背後にある理由は、Prismaが起動時に巨大なDMMF(Data Model Meta Format)文字列を解析する必要があるためです。中規模のスキーマでもこの文字列は600万文字以上になります。コールドスタートのたびにこれを解析するのですから、遅くて当然です。
Cal.com(オープンソースのカレンダー予約ツール)もこの問題について技術ブログを書き、コールドスタート時間をどう最適化したか共有しています。結論として、問題は確実に存在し、様々なワークアラウンドで緩和するしかありませんでした。
痛点3:SQL制御力の不足
Prismaの設計哲学は「SQLの抽象化」であり、Prisma ClientのDSLを使ってクエリを書かせようとします。大抵は便利ですが、複雑なクエリになると厄介です。
例えば、多層JOIN + サブクエリ + 条件付き集計のような複雑なクエリを書こうとすると、PrismaのAPIでは表現できないことがあります。結局 prisma.$queryRaw を使って直接SQLを書くことになります。
それなら、最初からSQLライクなAPIを使えばいいのでは? それがDrizzleの発想です。
開発者のリアルな需要
開発者が本当に求めているものをまとめると:
- 型安全性、しかし性能は犠牲にしない:TypeScriptの型推論は欲しいが、ORMのせいでプロジェクトが遅くなるのは困る
- SQLを直接使う、新しいDSLは覚えない:SQL自体で十分だ、Prisma独自のクエリ構文を覚えさせないでくれ
- サーバーレスフレンドリー:2025年にもなって、Vercel、Cloudflare Workers、AWS Lambdaを使わない手はない
- コンパイル速度:大規模プロジェクトではPrismaの型推論がTypeScriptのコンパイルを遅くする、これも隠れたコストだ
Drizzleはまさにこれらの需要に応えるために生まれました。
Drizzle ORMとは? 核心機能の解析
Drizzleの設計哲学
Drizzleのスローガンは “If you know SQL, you know Drizzle”(SQLを知っていれば、Drizzleもわかる)。強気ですが、确实にその通りです。
"If you know SQL, you know Drizzle."
従来のORMはSQLを隠蔽しようとしますが、Drizzleはその逆を行きます——SQLを抽象化せず、TypeScript APIを可能な限りSQL構文に近づけるのです。書くコードはSQLのように見えますが、完全な型ヒントがあります。
例を見れば一目瞭然です:
// Drizzle クエリ
await db
.select()
.from(posts)
.leftJoin(comments, eq(posts.id, comments.postId))
.where(eq(posts.id, 10))
// 生成される SQL
SELECT * FROM posts
LEFT JOIN comments ON posts.id = comments.post_id
WHERE posts.id = 10見ての通り、コード構造はSQLとほぼ同じです。SQLがわかる人なら、一目で使い方がわかります。
核心機能
1. 極限の軽量化
コアパッケージ drizzle-orm はわずか 7.4kb(min+gzip)で、ランタイム依存ゼロです。比較してみましょう:
- Drizzle:~7.4kb
- TypeORM:~300kb
- Prisma v7:~1MB
- Prisma v5:~14MB
これは桁違いの差です。
2. TypeScriptファースト、Client生成不要
Prismaは prisma generate を実行してClientを生成する必要がありますが、Drizzleは不要です。
スキーマを定義すれば、TypeScriptが直接型を推論します。IntelliSenseの自動補完、型チェック、エラー提示、すべて揃っています。コンパイル時に問題を発見でき、実行時まで待つ必要はありません。
3. SQLライクAPI、学習コストほぼゼロ
SQLが書ければ、Drizzleの習得に10分もかかりません。
// SELECT クエリ
db.select().from(users).where(eq(users.id, 1))
// INSERT 挿入
db.insert(users).values({ name: 'John', email: 'john@example.com' })
// UPDATE 更新
db.update(users).set({ name: 'Jane' }).where(eq(users.id, 1))
// DELETE 削除
db.delete(users).where(eq(users.id, 1))SQLを知っている人がこのコードを見れば、ドキュメントを調べる必要すらありません。
4. 性能劣化なし
Drizzleにはランタイムの抽象化層がありません。書いたクエリは直接SQLに変換され、中間プロセスはありません。
Prismaが起動時にDMMFを解析し内部状態を維持するのと対照的に、Drizzleは純粋なクエリビルダーです。隠れたコストも、予期せぬオーバーヘッドもありません。
5. サーバーレス対応
Drizzleはすべての主要なサーバーレス環境をサポートしています:
- Vercel Edge Functions
- Cloudflare Workers
- AWS Lambda
- Deno Deploy
- Bun
さらにサーバーレスデータベースドライバもネイティブサポート:
- Neon Serverless
- PlanetScale
- Turso(SQLite on the edge)
- Supabase
Neon + DrizzleでVercel Edgeにデプロイした私たちのプロジェクトでは、コールドスタート時間が2.5秒から700msに短縮されました。これが実際の効果です。
適用シナリオ
Drizzleは万能ではありませんが、以下のシナリオでは特に適しています:
1. サーバーレスアプリケーション
LambdaやEdge Functionsのような環境で動かすなら、Drizzleの軽さと高速なコールドスタートは必須要件です。
2. 性能重視のシナリオ
リアルタイムアプリ、金融システム、データ分析プラットフォーム——クエリ遅延に敏感なあらゆるシナリオで、Drizzleのゼロ抽象化設計は実質的な性能向上をもたらします。
3. 複雑なSQL制御が必要なプロジェクト
大量の複雑なクエリがあり、手動でSQL最適化が必要な場合、DrizzleのSQLライクAPIはPrismaのDSLより遥かに使いやすいです。
4. バンドルサイズに敏感なフロントエンドプロジェクト
一部のフルスタックフレームワーク(SolidStart、Qwikなど)はORMコードをクライアントにバンドルすることがあります。この時、Drizzleの7.4kbは巨大な利点になります。
逆に、チームがSQLに不慣れで、プロトタイプを爆速開発したい、あるいはPrismaのエコシステム(Prisma Studio、Prisma Migrate、Prisma Pulse)を好むなら、Prismaの方がまだ良い選択かもしれません。
Next.js + Drizzle 実戦設定
理論はこれくらいにして、実践に入りましょう。Next.js 15 + Drizzle + PostgreSQLのプロジェクトをゼロから設定する方法を解説します。
環境準備
まずNext.jsプロジェクトを作成:
npx create-next-app@latest my-drizzle-app
cd my-drizzle-appDrizzle関連の依存関係をインストール:
npm install drizzle-orm drizzle-kit
npm install @neondatabase/serverless # Neonデータベースなら
# または
npm install postgres # 従来のPostgreSQLならここでは Neon を推奨します。Drizzleと相性抜群のサーバーレスPostgreSQLです。無料アカウントを作り、データベースを作成して接続文字列を取得してください。
データベースSchemaの定義
db/schema.ts を作成し、テーブル構造を定義します:
import { pgTable, serial, text, timestamp, integer } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
// ユーザーテーブル
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').defaultNow(),
});
// 記事テーブル
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content'),
authorId: integer('author_id').references(() => users.id),
createdAt: timestamp('created_at').defaultNow(),
});
// 関係定義(一対多)
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));見てください、ただのTypeScriptコードです。Prismaのような特殊なスキーマファイルも、Client生成も不要です。
データベース接続の設定
db/index.ts を作成:
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
import * as schema from './schema';
// 環境変数からデータベース接続を読み込む
const sql = neon(process.env.DATABASE_URL!);
// Drizzleインスタンスを作成
export const db = drizzle(sql, { schema });これだけです。.env.local に以下を追加:
DATABASE_URL=postgres://user:pass@your-neon-host.neon.tech/dbnameDrizzle Kit(マイグレーションツール)の設定
drizzle.config.ts を作成:
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './db/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});マイグレーションファイルを生成:
npx drizzle-kit generatedrizzle/ ディレクトリにSQLマイグレーションファイルが生成されます。内容を確認し、問題なければ実行します:
npx drizzle-kit pushこれでデータベーステーブルが作成されました。
最終プロジェクト構造
my-drizzle-app/
├── app/ # Next.js アプリ
│ ├── page.tsx
│ └── actions.ts # Server Actions
├── db/
│ ├── schema.ts # テーブル定義と関係
│ └── index.ts # データベース接続
├── drizzle/
│ └── migrations/ # マイグレーションファイル(自動生成)
├── drizzle.config.ts # Drizzle Kit 設定
├── .env.local # 環境変数
└── package.json設定プロセス全体で5分もかかりません。複雑なPrisma Schema構文も、長い prisma generate もなく、純粋なTypeScriptコードだけです。
Drizzle SQLライクAPI 実戦
設定が完了したので、Drizzleでのクエリの書き方を見てみましょう。最もよく使う操作と、Next.js Server Actionsでの実際の使い方を紹介します。
基礎CRUD操作
データ検索(SELECT)
import { db } from '@/db';
import { users, posts } from '@/db/schema';
import { eq, like, and, or, desc } from 'drizzle-orm';
// 全ユーザー検索
const allUsers = await db.select().from(users);
// 単一ユーザー検索
const user = await db.select().from(users).where(eq(users.id, 1));
// あいまい検索
const result = await db.select().from(users).where(like(users.name, '%John%'));
// 複合条件
const admins = await db
.select()
.from(users)
.where(
and(
eq(users.role, 'admin'),
gt(users.createdAt, new Date('2024-01-01'))
)
);
// ソートと制限
const latestPosts = await db
.select()
.from(posts)
.orderBy(desc(posts.createdAt))
.limit(10);SQLのロジックそのままで、関数呼び出しに変わっただけですね。
データ挿入(INSERT)
// 単体挿入
await db.insert(users).values({
name: 'John Doe',
email: 'john@example.com',
});
// 複数挿入
await db.insert(users).values([
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
]);
// 挿入したデータを返す
const [newUser] = await db
.insert(users)
.values({ name: 'Charlie', email: 'charlie@example.com' })
.returning();
console.log(newUser.id); // 自動生成されたIDデータ更新(UPDATE)
// 単体更新
await db
.update(users)
.set({ name: 'Jane Doe' })
.where(eq(users.id, 1));
// 一括更新
await db
.update(posts)
.set({ published: true })
.where(eq(posts.authorId, 1));
// 更新後のデータを返す
const [updatedUser] = await db
.update(users)
.set({ name: 'Updated Name' })
.where(eq(users.id, 1))
.returning();データ削除(DELETE)
// 単体削除
await db.delete(users).where(eq(users.id, 1));
// 一括削除
await db.delete(posts).where(eq(posts.published, false));
// 削除したデータを返す
const deleted = await db
.delete(users)
.where(eq(users.id, 1))
.returning();高度なクエリ
JOIN 操作
// LEFT JOIN:ユーザーとその記事を検索
const usersWithPosts = await db
.select({
userId: users.id,
userName: users.name,
postId: posts.id,
postTitle: posts.title,
})
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId));
// INNER JOIN:記事のあるユーザーのみ検索
const activeAuthors = await db
.select()
.from(users)
.innerJoin(posts, eq(users.id, posts.authorId));サブクエリ
// 記事数が5以上のユーザーを検索
const sq = db
.select({ authorId: posts.authorId, count: count() })
.from(posts)
.groupBy(posts.authorId)
.having(gt(count(), 5))
.as('sq');
const prolificAuthors = await db
.select()
.from(users)
.innerJoin(sq, eq(users.id, sq.authorId));集計関数
import { count, sum, avg } from 'drizzle-orm';
// ユーザー総数カウント
const [{ total }] = await db
.select({ total: count() })
.from(users);
// 作者ごとの記事数カウント
const postCounts = await db
.select({
authorId: posts.authorId,
count: count(),
})
.from(posts)
.groupBy(posts.authorId);Next.js Server Actions での使用
app/actions.ts を作成:
'use server';
import { db } from '@/db';
import { users } from '@/db/schema';
import { eq } from 'drizzle-orm';
import { revalidatePath } from 'next/cache';
// ユーザー作成
export async function createUser(formData: FormData) {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
try {
await db.insert(users).values({ name, email });
revalidatePath('/users');
return { success: true };
} catch (error) {
return { success: false, error: 'ユーザー作成失敗' };
}
}
// 全ユーザー取得
export async function getUsers() {
return await db.select().from(users);
}
// ユーザー削除
export async function deleteUser(id: number) {
try {
await db.delete(users).where(eq(users.id, id));
revalidatePath('/users');
return { success: true };
} catch (error) {
return { success: false, error: 'ユーザー削除失敗' };
}
}ページでの使用:
// app/users/page.tsx
import { getUsers } from '../actions';
export default async function UsersPage() {
const users = await getUsers();
return (
<div>
<h1>ユーザーリスト</h1>
<ul>
{users.map(user => (
<li key={user.id}>
{user.name} - {user.email}
</li>
))}
</ul>
</div>
);
}TypeScript 型安全性
これこそがDrizzleの真骨頂——完全な型推論です。
// 戻り値の型を自動推論
const users = await db.select().from(users);
// 型:{ id: number; name: string; email: string; createdAt: Date }[]
// カスタム戻り値フィールド、型も自動推論
const result = await db
.select({
id: users.id,
name: users.name,
})
.from(users);
// 型:{ id: number; name: string }[]
// エラーはコンパイル時に捕捉される
await db.select().from(users).where(eq(users.id, '1'));
// ❌ TypeScript エラー:型 'string' を型 'number' に割り当てることはできませんIntelliSenseは利用可能なすべてのフィールド、関数、条件演算子を提示してくれます。コードを書くのはゲーム感覚で、ドットを押せばすべてが出てきます。
APIを覚える必要も、ドキュメントを調べる必要もありません。TypeScriptコンパイラが最高のドキュメントです。
Drizzle vs Prisma 徹底比較
Drizzleの良い点ばかり話しましたが、客観的に全面比較してみましょう。自分のプロジェクトにどちらが適しているか判断するためです。
性能比較
| 比較項目 | Drizzle | Prisma v5 | Prisma v7 | 備考 |
|---|---|---|---|---|
| Bundleサイズ | ~7.4kb | ~14MB | ~1MB | Drizzleが最軽量 |
| コールドスタート | ~600ms | ~2.5s | ~1.5s | サーバーレス環境 |
| ランタイム依存 | 0個 | Rustバイナリ | 0個 | DrizzleとPrisma v7は依存なし |
| メモリ使用量 | ~5MB | ~80MB | ~30MB(推定) | 実際の実行時メモリ |
| 型チェック速度 | 高速 | 普通 | 普通 | Drizzleの推論がよりシンプル |
リアルな事例データ
私たちのチームのプロジェクトで、Prisma v5からDrizzleへ移行した後:
- 初回リクエスト時間:3s → 700ms(76%高速化)
- 本番バンドルサイズ:18MB → 4MB(78%削減)
- Lambdaコールドスタート:2.4s → 650ms(73%高速化)
これらの数字はテスト環境のものではなく、実際の本番環境のモニタリングデータです。
開発体験比較
Schema定義方式
// Drizzle(TypeScriptコード)
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});
// Prisma(専用DSL)
model User {
id Int @id @default(autoincrement())
name String
}Drizzleの利点:TypeScriptそのものなのでIDEサポートが良く、TypeScriptの全機能(条件付き型、ジェネリクス等)が使える
Prismaの利点:Prisma Schemaはより簡潔で、可読性が高い
クエリAPIスタイル
// Drizzle(SQLライク)
await db
.select()
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId))
.where(gt(posts.views, 1000));
// Prisma(チェーン式API)
await prisma.user.findMany({
include: {
posts: {
where: { views: { gt: 1000 } },
},
},
});Drizzleの利点:SQLに近く、複雑なクエリを表現しやすい
Prismaの利点:SQLに不慣れな開発者に優しく、ネストしたクエリの意味が明確
機能比較
Drizzleの強み
- 究極の軽量化:コア7.4kb、バンドルサイズに敏感なプロジェクトに必須
- サーバーレスネイティブ:コールドスタートが速く、Edge環境に適応
- SQL制御力が強い:複雑なクエリや性能最適化が柔軟
- Client生成不要:スキーマを変えたらすぐ使える、
prisma generateは不要 - Tree-shakable:使用したコードのみバンドルされる
Prismaの強み
- エコシステムが成熟:2021年リリース、コミュニティが大きく、チュートリアルが多く、ハマりどころが少ない
- 開発ツールが充実:Prisma Studio(DB管理)、Prisma Migrate(自動移行)、Prisma Pulse(リアルタイム購読)
- 関係クエリがスマート:N+1問題を自動処理、ネストクエリが直感的
- 初心者に優しい:SQLを深く理解する必要がなく、DSLの学習曲線が緩やか
- エラー提示が詳細:ランタイムエラー情報が親切
選定アドバイス
Drizzleを選ぶべきシーン
✅ サーバーレス/エッジ環境(Vercel、Cloudflare Workers、Deno Deploy)
✅ 性能重視のシーン(リアルタイムアプリ、金融システム、高同時API)
✅ バンドルサイズ制限あり(< 1MB制限環境)
✅ チームのSQL基礎があり、クエリを直接制御したい
✅ 複雑なSQL最適化が必要なプロジェクト
Prismaを選ぶべきシーン
✅ チームがSQLに不慣れで、急速に立ち上げたい
✅ 開発体験とツールチェーンの完全性を重視
✅ 可視化データベース管理が必要(Prisma Studio)
✅ 複雑なリレーションクエリとデータモデリング
✅ プロジェクトがサーバーレス環境でなく、バンドルサイズに敏感でない
私の個人的アドバイス
もし新規プロジェクトをやるなら、私はこう選びます:
- 個人/スタートアップ:Drizzle(性能が良く、コストが低く、極限まで最適化できる)
- 企業/チーム開発:チーム背景による。SQLが強ければDrizzle、弱ければPrisma
- サーバーレスファースト:迷わずDrizzle
- 従来型サーバーデプロイ:どちらでもOK、Prismaのツールチェーンは魅力的
ハイブリッド案もあります:コアの性能重視モジュールはDrizzle、管理画面はPrisma。両者は共存可能で衝突しません。
移行ガイドとベストプラクティス
PrismaからDrizzleへの移行
すでにPrismaプロジェクトがあり、Drizzleを試したい場合、段階的に移行できます。
ステップ1:Schema変換
// Prisma Schema
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
createdAt DateTime @default(now())
}
// Drizzle Schemaへ変換
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').defaultNow(),
});ステップ2:クエリ書き換え
// Prisma クエリ
const user = await prisma.user.findUnique({
where: { id: 1 },
include: { posts: true },
});
// Drizzle クエリ
const [user] = await db
.select()
.from(users)
.where(eq(users.id, 1))
.leftJoin(posts, eq(users.id, posts.authorId));ステップ3:段階的置換
PrismaとDrizzleを共存させられます:
// 性能重視のクエリはDrizzle
import { db } from '@/db/drizzle';
const latestPosts = await db
.select()
.from(posts)
.orderBy(desc(posts.createdAt))
.limit(50);
// 複雑なリレーションクエリは暫定的にPrisma
import { prisma } from '@/db/prisma';
const userWithRelations = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: { include: { comments: { include: { author: true } } } },
},
});少しずつ置き換え、リスクを下げましょう。
Drizzle ベストプラクティス
1. コネクションプール管理
サーバーレス環境ではコネクションプールに特に注意:
import { drizzle } from 'drizzle-orm/neon-http';
import { neon, neonConfig } from '@neondatabase/serverless';
// プール設定
neonConfig.fetchConnectionCache = true;
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });2. プリコンパイルクエリ
高頻度のクエリはプリコンパイルして性能アップ:
import { db } from '@/db';
import { users } from '@/db/schema';
import { eq } from 'drizzle-orm';
// クエリのプリコンパイル
const getUserById = db
.select()
.from(users)
.where(eq(users.id, placeholder('id')))
.prepare('get_user_by_id');
// 使用
const user = await getUserById.execute({ id: 1 });3. トランザクション処理
await db.transaction(async (tx) => {
// ユーザー作成
const [user] = await tx
.insert(users)
.values({ name: 'John', email: 'john@example.com' })
.returning();
// 関連する記事を作成
await tx.insert(posts).values({
title: 'First Post',
authorId: user.id,
});
// いずれかが失敗すれば、トランザクション全体がロールバック
});4. 型の再利用
推論された型をエクスポートしてフロントエンドで使用:
// db/schema.ts
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
});
// 推論型をエクスポート
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
// フロントエンドで使用
import type { User } from '@/db/schema';
function UserCard({ user }: { user: User }) {
return <div>{user.name}</div>;
}5. エラーハンドリング
import { db } from '@/db';
import { users } from '@/db/schema';
try {
await db.insert(users).values({
name: 'John',
email: 'duplicate@example.com',
});
} catch (error) {
// PostgreSQL エラーコード
if (error.code === '23505') {
console.error('メールアドレスが既に存在します');
} else {
console.error('データベースエラー', error);
}
}結論
長くなりましたが、まとめましょう。
Drizzle ORMはTypeScript ORM界の一服の清涼剤です——SQLを隠蔽せず、SQLを受け入れます。7.4kbのコア、ランタイム依存ゼロ、ネイティブSQLに近い性能で、**「軽量化は妥協ではなく、より良い選択である」**ことを証明しました。
Prismaの完全な代替品ではありません。Prismaには成熟したエコシステム、完璧なツールチェーン、初心者に優しい体験があります。しかし、もしあなたのプロジェクトが:
- サーバーレス環境で動く
- 性能とバンドルサイズに敏感
- 複雑なSQL制御が必要
- チームにSQLの基礎がある
ならば、Drizzleはより適切な選択かもしれません。
私たちのチームの実際のデータがすべてを物語っています:PrismaからDrizzleへ移行後、コールドスタートは73%高速化、バンドルは78%削減、初回リクエストは3秒から700msに短縮。これは微調整ではなく、質的な飛躍です。
もしPrismaの遅いコールドスタートや巨大なバンドルに頭を悩ませているなら、Drizzleを試してみてください。学習コストは低いです——SQLがわかれば、10分で使いこなせます。
最後に、役立つリンクを貼っておきます:
試してみてください。私と同じように、もう戻れなくなるかもしれません。
Next.js + Drizzle ORM 完全設定・移行ガイド
Drizzle ORMのゼロからの設定、環境準備、Schema定義、DB接続、クエリ使用、そしてPrismaからの移行までの完全ステップ
⏱️ Estimated time: 2 hr
- 1
Step1: ステップ1:環境準備と依存インストール
Next.jsプロジェクト作成:
```bash
npx create-next-app@latest my-drizzle-app
cd my-drizzle-app
```
Drizzle関連依存のインストール:
```bash
npm install drizzle-orm drizzle-kit
npm install @neondatabase/serverless # Neonデータベースなら
# または
npm install postgres # 従来のPostgreSQLなら
```
Neon推奨(serverless PostgreSQL):
• 無料アカウント登録:https://neon.tech
• データベース作成、接続文字列取得
• Drizzleと相性抜群、Edge環境対応
.env.local に追加:
```
DATABASE_URL=postgres://user:pass@your-neon-host.neon.tech/dbname
``` - 2
Step2: ステップ2:データベースSchema定義
db/schema.ts を作成し、テーブル構造を定義:
```typescript
import { pgTable, serial, text, timestamp, integer } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
// ユーザーテーブル
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').defaultNow(),
});
// 記事テーブル
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content'),
authorId: integer('author_id').references(() => users.id),
createdAt: timestamp('created_at').defaultNow(),
});
// 関係定義(一対多)
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
```
ポイント:
• TypeScriptコードそのもの、Prismaのような特殊ファイルなし
• Client生成不要、型は自動推論
• TypeScriptの全機能利用可能 - 3
Step3: ステップ3:DB接続とDrizzle Kit設定
db/index.ts 作成:
```typescript
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
import * as schema from './schema';
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
```
drizzle.config.ts 作成:
```typescript
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './db/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
```
マイグレーション実行:
```bash
npx drizzle-kit generate
npx drizzle-kit push
```
設定完了まで5分以内。 - 4
Step4: ステップ4:Drizzle SQLライクAPIでのクエリ
基礎CRUD:
検索(SELECT):
```typescript
const user = await db.select().from(users).where(eq(users.id, 1));
```
挿入(INSERT):
```typescript
await db.insert(users).values({ name: 'John', email: 'john@example.com' });
```
高度なクエリ(JOIN):
```typescript
const usersWithPosts = await db
.select()
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId));
```
ポイント:
• SQL構文に近い構造
• 完全な型推論とIntelliSense
• コンパイル時エラー検出 - 5
Step5: ステップ5:Next.js Server Actionsでの使用
app/actions.ts:
```typescript
'use server';
import { db } from '@/db';
import { users } from '@/db/schema';
export async function createUser(formData: FormData) {
// ...
await db.insert(users).values({ name, email });
revalidatePath('/users');
}
``` - 6
Step6: ステップ6:Prismaからの移行(任意)
段階的移行が可能:
1. Schema変換:Prisma model → Drizzle pgTable
2. クエリ書き換え:prisma.findMany → db.select
3. 共存運用:性能重要な箇所はDrizzle、複雑な箇所はPrismaと使い分け、徐々に置換
FAQ
Drizzle ORMとPrismaの違いは?いつDrizzleを選ぶべき?
性能:
• Drizzle:7.4kb、コールドスタート600ms、依存なし
• Prisma:v5は14MB/2.5s、v7は1MB/1.5s
開発体験:
• Drizzle:SQLライク、Client生成不要
• Prisma:専用DSL、Client生成必要
Drizzleを選ぶべき時:
✅ サーバーレス/エッジ環境
✅ 性能・バンドルサイズ重視
✅ SQL制御が必要
✅ チームにSQL基礎がある
Prismaを選ぶべき時:
✅ SQL不慣れなチーム
✅ ツールチェーン重視(Studio等)
✅ サーバーレスでない
Drizzle ORMのSQLライクAPIとは具体的に?
SQL:
SELECT * FROM posts WHERE id = 10
Drizzle:
await db.select().from(posts).where(eq(posts.id, 10))
特徴:
• SQLを知っていれば学習コストほぼゼロ
• 完全な型推論と自動補完
• Prismaの独自DSLを覚える必要がない
Drizzle ORMのサーバーレスでの性能優位性は?
• 初回リクエスト:3s → 700ms(76%高速化)
• バンドルサイズ:18MB → 4MB(78%削減)
• Lambdaコールドスタート:2.4s → 650ms(73%高速化)
理由:
• 軽量コア(7.4kb)
• 起動時の巨大なスキーマ解析がない
• メモリ使用量が少ない
Prismaからの移行は複雑ですか?
1. Schema変換(Model → pgTable)
2. クエリ書き換え(findMany → select)
3. 漸進的置換(共存させて徐々に移行)
というステップで、リスクを抑えて移行できます。
Drizzle ORMはどのDBと環境をサポートしていますか?
• PostgreSQL(Neon推奨)
• MySQL
• SQLite(Turso等)
• SQL Server
環境:
• Vercel Edge Functions
• Cloudflare Workers
• AWS Lambda
• Deno Deploy
• Bun
すべてネイティブサポートです。
型安全性はどうですか?
Client生成なしでTypeScriptが直接型を推論します。
• 戻り値の型も自動推論
• IntelliSenseで全フィールド補完
• コンパイル時にエラー検出
大規模プロジェクトでもコンパイル速度を落としません。
学習コストは?
新しいDSLを覚える必要がないため、Prismaより学習コストは低いです(SQL基礎がある場合)。
9 min read · 公開日: 2025年12月20日 · 更新日: 2026年1月22日
関連記事
AdSense代替案徹底比較:Mediavine、Ezoic、アフィリエイト完全ガイド(2026年版)

AdSense代替案徹底比較:Mediavine、Ezoic、アフィリエイト完全ガイド(2026年版)
AMPページへのAdSense設置完全ガイド:モバイル広告収益を48%アップさせる秘訣

AMPページへのAdSense設置完全ガイド:モバイル広告収益を48%アップさせる秘訣
WordPress AdSense最適化ガイド:プラグイン選びと設定の極意(2026年版)


コメント
GitHubアカウントでログインしてコメントできます