SvelteKit × Cloudflare D1にDrizzle ORMを導入した話
Drizzle ORMはTypeScript向けの軽量ORMです。platform.env.DB.prepare('SELECT ...') と書いていた生SQLを、型安全なTypeScriptのコードに置き換えられます。今回は既存のCloudflare D1テーブルに対してDrizzleを導入しました。
Drizzleのメリット
| 生SQL | Drizzle | |
|---|---|---|
| 型安全 | ❌ 手動で型を書く必要がある | ✅ スキーマから自動生成 |
| タイポ | ❌ 実行時エラー | ✅ コンパイル時エラー |
| リレーション | 手動でJOINを書く | .with() で簡潔に書ける |
| マイグレーション | 手動でSQLを書く | drizzle-kit で自動生成 |
セットアップ
1. インストール
npm install drizzle-orm
npm install -D drizzle-kit
2. スキーマを定義
src/lib/db/schema.ts を作成します。既存のテーブル構造に合わせて定義します。
import { int, text, sqliteTable } from 'drizzle-orm/sqlite-core';
export const posts = sqliteTable('posts', {
id: int('id').primaryKey({ autoIncrement: true }),
title: text('title').notNull(),
body: text('body').notNull(),
});
export const users = sqliteTable('users', {
id: text('id').primaryKey(),
githubId: int('github_id').notNull(),
name: text('name').notNull(),
});
export const sessions = sqliteTable('sessions', {
id: text('id').primaryKey(),
userId: text('user_id').notNull(),
expiresAt: int('expires_at').notNull(),
});
3. Drizzleクライアントのヘルパーを作成
src/lib/db/index.ts を作成します。
import { drizzle } from 'drizzle-orm/d1';
import * as schema from './schema';
export function createDb(d1: D1Database) {
return drizzle(d1, { schema });
}
4. drizzle.config.ts を作成
/// <reference types="node" />
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/lib/db/schema.ts',
out: './migrations',
dialect: 'sqlite',
driver: 'd1-http',
dbCredentials: {
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
token: process.env.CLOUDFLARE_D1_TOKEN!,
},
});
ポイント:/// <reference types="node" />
tsconfig.json に @cloudflare/workers-types が指定されているため、そのままでは process が認識されません。トリプルスラッシュディレクティブをファイル先頭に追加することで、そのファイルだけNode.jsの型を有効にできます。
SvelteKitで使う
クエリの書き方
import { createDb } from '$lib/db';
import { posts } from '$lib/db/schema';
import { eq } from 'drizzle-orm';
const db = createDb(platform!.env.DB);
// SELECT(全件)
const allPosts = await db.select().from(posts);
// SELECT(特定カラムのみ)
const titles = await db.select({ id: posts.id, title: posts.title }).from(posts);
// SELECT(条件付き)
const post = await db.select().from(posts).where(eq(posts.id, 1));
// INSERT
await db.insert(posts).values({ title: 'タイトル', body: '本文' });
// UPDATE
await db.update(posts).set({ title: '新しいタイトル' }).where(eq(posts.id, 1));
// DELETE
await db.delete(posts).where(eq(posts.id, 1));
APIルートへの組み込み例
// src/routes/api/posts/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { createDb } from '$lib/db';
import { posts } from '$lib/db/schema';
export const GET: RequestHandler = async ({ platform }) => {
const db = createDb(platform!.env.DB);
const allPosts = await db.select({ id: posts.id, title: posts.title }).from(posts);
return json(allPosts);
};
export const POST: RequestHandler = async ({ request, platform }) => {
const body = await request.json() as { title: string; body: string };
const db = createDb(platform!.env.DB);
await db.insert(posts).values({
title: body.title,
body: body.body,
});
return json({ success: true }, { status: 201 });
};
今回やったこととやっていないこと
今回は既存のD1テーブルに対してDrizzleを導入しました。テーブルの再作成やマイグレーションは行っていません。
| やったこと | やっていないこと |
|---|---|
| スキーマ定義(既存テーブルに合わせて) | drizzle-kit でのマイグレーション生成 |
| Drizzleクライアントの作成 | テーブルの新規作成 |
| 生SQL → Drizzleクエリへの書き換え | リレーションの定義 |
Drizzleの本来の使い方はスキーマを定義してから drizzle-kit generate でマイグレーションSQLを生成し、テーブルを作成するところから始まります。既存テーブルがある場合は今回のようにスキーマを合わせて定義することでDrizzleを導入できます。
まとめ
drizzle-orm+drizzle-kitをインストールしてスキーマを定義するだけで導入できるcreateDb(platform!.env.DB)でCloudflare D1に接続したDrizzleインスタンスを作れるselect/insert/update/deleteが型安全に書けるdrizzle.config.tsでprocessを使う場合は/// <reference types="node" />が必要