🐙

Cloudflare D1でDrizzle ORMを使う

2025/01/09に公開

はじめに

Cloudflare D1でDrizzle ORMを利用するための手順をまとめました。以下の公式ドキュメントを参考に作業を進めます。

なお記事を投稿した時点でDrizzle公式ドキュメントに一部不備がありました。内容は適宜補足します。

環境

記事の投稿にあたり、以下の環境で動作確認しました。

  • node: 23.5.0
  • drizzle-orm: 0.38.3
  • drizzle-kit: 0.30.1

動作確認はmacOSで行いましたが、Node.jsが動作する環境であればWindowsやLinuxなどOSに依存せず動作するはずです。

Step 1: Workersプロジェクトを作成する

まずはWorkersプロジェクトを作成します。以下のコマンドを実行してください。

$ npm create cloudflare@latest

各項目は以下のように回答しました。

├ In which directory do you want to create your application?
│ dir ./drizzle-test
│
├ What would you like to start with?
│ category Hello World example
│
├ Which template would you like to use?
│ type Hello World Worker
│
├ Which language do you want to use?
│ lang TypeScript

デプロイは後で行います。「Do you want to deploy your application?」はひとまずNoで進めてください。

Step 2: D1データベースを作成する

続いてD1データベースを作成します。以下のコマンドを実行してください。

cd ./drizzle-test
$ npx wrangler d1 create my-database

次の手順で利用するため、実行結果をメモしてください。

実行結果の例
 ⛅️ wrangler 3.100.0
--------------------

✅ Successfully created DB 'my-database' in region WNAM
Created your new D1 database.

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Step 3: wrangler.tomlファイルを編集する

wrangler.tomlファイルを開き、末尾にD1データベースの項目を追加します。以下のように編集してください。

#:schema node_modules/wrangler/config-schema.json
name = "drizzle-test"
main = "src/index.ts"
compatibility_date = "2024-12-30"
compatibility_flags = ["nodejs_compat"]

# (省略)

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

database_id = "xxx..."はStep 3でメモした内容で置き換えてください。

Step 4: バインディングの型定義を生成する

以下のコマンドを実行してください。

$ npx wrangler types

以下のように、worker-configuration.d.tsファイルにD1データベースの型定義が追加されていれば成功です。

$ cat ./worker-configuration.d.ts
// Generated by Wrangler by running `wrangler types`

interface Env {
	DB: D1Database;
}

Step 5: Drizzleをインストールする

以下のコマンドを実行してください。

$ npm i drizzle-orm dotenv
$ npm i -D drizzle-kit tsx

Step 6: テーブルを定義する

src/dbディレクトリを作成し、以下の内容でsrc/db/schema.tsファイルを作成してください。

import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core';

export const usersTable = sqliteTable('users', {
	id: int().primaryKey({ autoIncrement: true }),
	name: text().notNull(),
	age: int().notNull(),
	email: text().notNull().unique(),
});
補足情報を表示

(補足)名前のマッピングについて

Drizzleはテーブル名やカラム名をTypeScriptのコードにマッピングする設定が可能です。ただし、DrizzleのGet startedに定義されている実装ではusersではなくusers_tableテーブルが作成されてしまいます。

// 修正前
export const usersTable = sqliteTable("users_table", {

// 修正後
export const usersTable = sqliteTable("users", {

Step 7: drizzle.config.tsファイルを作成する

プロジェクトのルートディレクトリにdrizzle.config.tsファイルを作成します。以下の内容でファイルを作成してください。

import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
	out: './drizzle',
	schema: './src/db/schema.ts',
	dialect: 'sqlite',
	driver: 'd1-http',
	dbCredentials: {
		accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
		databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
		token: process.env.CLOUDFLARE_D1_TOKEN!,
	},
});
補足情報を表示

(補足)driverプロパティについて

公式ドキュメントでは空欄になっていますが間違いです。

// 修正前
driver: '',

// 修正後
driver: 'd1-http',

なお空欄のままdrizzle-kit migrateを実行するとバリデーションエラーが発生してコマンドの実行が中断します。

Step 8: 環境変数を設定する

プロジェクトのルートディレクトリに.envファイルを作成します。以下3つの環境変数を設定してください。

CLOUDFLARE_ACCOUNT_ID='CloudflareのアカウントID'
CLOUDFLARE_DATABASE_ID='wrangler.tomlファイルに設定したdatabase_idと同じ値'
CLOUDFLARE_D1_TOKEN='Cloudflareダッシュボードで発行したAPIトークン'
補足情報を表示

(補足)アカウントIDとAPIトークンの取得方法

CloudflareのアカウントIDはCloudflareのダッシュボードに表示されています。ダッシュボードにログイン→Workers & Pagesに移動→Account IDのコピーボタンをクリックすると取得できます。

APIトークンについては、Cloudflareのダッシュボードにログイン→Account Manage→Account API Tokens→Create Tokenから作成できます。

トークン作成画面に移動したら、Custom tokenのGet startedボタンをクリックします。項目は以下のように設定します。

  • Resources: Accountを選択
  • Permissions: D1を選択
  • Permissions levels: Editを選択

トークンの名前や有効期間などは適当で構いません。入力が完了したらContinue to summaryボタンをクリックしてください。

権限として「D1:Edit」と表示されていることを確認したら、Create Tokenボタンをクリックしてください。D1データベースの操作権限のあるトークンが発行されます。

Step 9: マイグレーションファイルを生成する

以下のコマンドを実行してマイグレーションファイルを生成します。

$ npx drizzle-kit generate

以下のような結果が表示されたら成功です。

No config path provided, using default 'drizzle.config.ts'
Reading config file '/private/tmp/drizzle-test/drizzle.config.ts'
1 tables
users 4 columns 1 indexes 0 fks

[✓] Your SQL migration file ➜ drizzle/0000_daily_hardball.sql 🚀

Step 10: マイグレーションを適用する

以下のコマンドを実行してマイグレーションを適用します。

$ npx drizzle-kit migrate

結果は以下の2行しか表示されません。成功したか不安になりますが、エラーメッセージが表示されなければ成功です。

No config path provided, using default 'drizzle.config.ts'
Reading config file '/private/tmp/drizzle-test/drizzle.config.ts'
補足情報を表示

(補足)drizzle-kit pushコマンドについて

DrizzleのGet startedではdrizle-kit pushコマンドを実行してマイグレーションを適用する方法が示されています。しかし、このコマンドは失敗します。

$ npx drizzle-kit push
No config path provided, using default 'drizzle.config.ts'
Reading config file '/private/tmp/drizzle-test/drizzle.config.ts'
[✓] Pulling schema from database...
Error: 7500: not authorized: SQLITE_AUTH

なおエラーが発生するのはpushサブコマンドのみです。すでにGitHubにIssueが登録されているため、いずれ修正されるはずです。

push / pullサブコマンドは既存のデータベースからスキーマを読み取りコードを生成する場合に利用します。ただし、別のツールでマイグレーションに利用する.sqlファイルを生成し、migrateサブコマンドを実行することでも同様の結果が得られます。

Step 11: レコードの読み書き処理を実装する

GETリクエストでレコードを取得、POSTリクエストでレコードを挿入する処理を実装しましょう。src/index.tsを以下の内容で置き換えてください。

import { drizzle } from 'drizzle-orm/d1';
import { usersTable } from './db/schema';

export default {
	async fetch(request, env, ctx): Promise<Response> {
		switch (request.method) {
			case 'POST': {
				const db = drizzle(env.DB);
				const record: typeof usersTable.$inferInsert = {
					name: 'John',
					age: 23,
					email: crypto.randomUUID() + '@example.com',
				};

				await db.insert(usersTable).values(record);

				return new Response(null, { status: 201 });
			}
			case 'GET': {
				const db = drizzle(env.DB);
				const result = await db.select().from(usersTable).all();

				return Response.json({ count: result.length, values: result });
			}
			default: {
				return new Response(null, { status: 405 });
			}
		}
	},
} satisfies ExportedHandler<Env>;

Step 12: 動作確認

それでは動作確認してみましょう。まずはWorkerをデプロイします。以下のコマンドを実行してください。

$ npx wrangler deploy
バインディングが正しく設定されているか確認するには?

D1データベースのバインディングが正しく認識されているか確認しましょう。以下のように「db: my-database ...」がコマンドの実行結果に含まれていれば正常です。

 ⛅️ wrangler 3.100.0
--------------------

Total Upload: 188.87 KiB / gzip: 38.52 KiB
Worker Startup Time: 13 ms
Your worker has access to the following bindings:
- D1 Databases:
  - DB: my-database (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
Uploaded drizzle-test (2.96 sec)
Deployed drizzle-test triggers (0.66 sec)
  https://drizzle-test.<your_name>.workers.dev
Current Version ID: e27fe9b1-b79a-487b-a6c0-44356d180b1b

まずはGETリクエストを試しましょう。以下のコマンドを実行してください。

※URLの<your_name>は各自の値に読み替えてください。

$ curl https://drizzle-test.<your_name>.workers.dev

この時点でテーブルは空なので以下のレスポンスが返されます。

{"count":0,"values":[]}

次はレコードを作成してみましょう。以下のようにPOSTリクエストを送信してください。

$ curl -i -X POST https://drizzle-test.<your_name>.workers.dev

リクエストが成功すると201 Createdレスポンスが返されます。

HTTP/2 201
date: Thu, 09 Jan 2025 04:58:22 GMT
content-length: 0
# 以下省略

これでテーブルに1つのレコードが挿入されました。改めてGETリクエストを送信して確認してみましょう。

$ curl https://drizzle-test.<your_name>.workers.dev

以下のような結果が得られます。

{"count":1,"values":[{"id":1,"name":"John","age":23,"email":"d73c45b2-ebd2-46e6-9afe-c5fb60204c9d@example.com"}]}%

作業は以上で完了です。Drizzle ORMをCloudflare D1データベースに対して利用できることが確認できました。

Discussion