Cloudflare Workers→D1にORM drizzleを使ってmigration、接続する

## はじめに
前回までにCloudflare WorkersからD1に接続するまで実施しました。
アプリケーションはHonoで実行していたんですが、ORMないと色々不便だな😅と感じ、ORMを探していました。TypeScriptのORMというと、prismaが思い浮かぶのですが、D1とは相性が悪いみたいで採用を見送りました。ざんねん😢
よくよく調べてみるとdrizzleというORMが相性いいみたいで今回採用してみました！！

https://qiita.com/koji0705/items/5a454437019468d7a826


## Drizzleとは
TypeScriptのORMです。Prismaと比べて計量でシンプルさとパフォーマンスを重視したORMです。
サーバレス環境やコンパクトなプロジェクトに適しています！

| 特徴                | Prisma                               | Drizzle ORM                           |
|---------------------|--------------------------------------|---------------------------------------|
| **エコシステム**    | 非常にリッチ、フル機能               | シンプルで必要最小限                 |
| **型安全性**        | 自動生成された型で強力にサポート     | サポートあり（Prismaほど自動ではない）|
| **パフォーマンス**  | より多機能なため、やや重め           | 軽量で高速                           |
| **学習コスト**      | 中程度〜高め                         | 低め                                 |
| **サーバーレス対応**| Prisma Data Proxyで対応             | 非常に適している                     |
| **マイグレーション**| Prisma Migrateで強力にサポート      | 基本的なサポート                     |
| **ドキュメント**    | 非常に豊富                           | シンプルでコンパクト                 |

https://orm.drizzle.team/


## 0.準備(プロジェクト作成〜D1でDB作成まで)
今回の本題とは異なりますが、プロジェクト作成、D1にDBを作成します。
下記を参考に実施してみてください！！⭐️

<details><summary>プロジェクト作成〜D1でDB作成まで</summary>

```shell:プロジェクトを作成
~/develop$ npm create cloudflare@latest

> npx
> create-cloudflare


using create-cloudflare version 2.23.0

╭ Create an application with Cloudflare Step 1 of 3
│ 
├ In which directory do you want to create your application?
│ dir ./sample-drizzle
│
├ What would you like to start with?
│ category Framework Starter
│
╰ Which development framework do you want to use?
  ○ Analog
  ○ Angular
  ○ Astro
  ○ Docusaurus
  ○ Gatsby
  ● Hono
  ○ Next
  ○ Nuxt
  ○ Qwik
  ○ React
  ○ Remix
  ○ Solid
  ○ Svelte
  ○ Vue
```

D1でDBを作成する
![スクリーンショット 2024-08-12 11.30.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/5e72416e-4096-d75f-eb2b-91deeef18c04.png)
![スクリーンショット 2024-08-12 11.30.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/48618bba-7f34-2346-c4ee-7d5b07801a07.png)

</details>

## 1. migrationの実施
では実際にmigrationを実施するための作業をしていきましょう！！

### 1-1. 環境変数の設定
```toml:wrangler.toml
#:schema node_modules/wrangler/config-schema.json
name = "XXXX"  ## 今回作成したプロジェクト名を設定してください。
main = "src/index.ts"
compatibility_date = "2024-08-06"
account_id = "XXXXXX"　## アカウントIDを設定
workers_dev = true

[[d1_databases]]
binding = "D1"
database_name = "sample"　## DBの名称
database_id = "XXXXX"　## DBのid
```

### 1-2. スキーマ定義
今回はuserテーブルを作っておきます。

```shell:必要なライブラリをinstallする
npm install drizzle-orm
npm install drizzle-kit --save-dev
```

```ts:src/schema.ts
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";

// usersテーブルのスキーマを定義
export const users = sqliteTable("users", {
	id: integer("id").primaryKey().notNull(), // ユーザーID（主キー）
	username: text("username").notNull(), // ユーザー名（必須）
	email: text("email").notNull().unique(), // メールアドレス（必須、ユニーク）
	password: text("password").notNull(), // パスワード（必須）
	isActive: integer("is_active").default(1), // アクティブフラグ（デフォルトで1 = true）
	createdAt: text("created_at").default("CURRENT_TIMESTAMP"), // 作成日時
});
```

:::note warn
d1はsqliteの亜種のようです。そのため、bool値が使えないようなので注意⚠️
:::

### 1-3. migrationファイルの作成
スキーマからmigrationファイルを作成していきましょう！

#### 1-3-1. マイグレーション先の設定ファイル
```ts:drizzle.config.ts
import type { Config } from "drizzle-kit";

export default {
  schema: "./src/schema.ts",  // スキーマ定義ファイル
  out: "./drizzle/migrations", // マイグレーションファイルの出力ディレクトリ
  dialect: "sqlite",          // SQLiteを使用
} satisfies Config;
```
:::note info
マイグレーション先の設定ファイルを作成します。
プロジェクトルート配下に作成してください！
:::

#### 1-3-2. マイグレーションファイルの生成
```shell:マイグレーションファイルの生成
~/develop/sample-drizzle (master)$ npx drizzle-kit generate
No config path provided, using default 'drizzle.config.ts'
Reading config file '/Users/kawamurakouji/develop/sample-drizzle/drizzle.config.ts'
1 tables
users 6 columns 1 indexes 0 fks

[✓] Your SQL migration file ➜ drizzle/migrations/0000_foamy_clint_barton.sql 🚀
```

:::note info
これでマイグレーションファイルが作成されました。
:::

#### 1-3-3. マイグレーションの実行
```shell:マイグレーションの実行
npx wrangler d1 execute <database-name> --file=./drizzle/migrations/<migration-file>.sql --remote
```

:::note warn
上記はサーバに直接migrationを流す方法です。`--remote`を外すだけで良いです。

```shell:ローカルDBへのマイグレーション
npx wrangler d1 execute <database-name> --file=./drizzle/migrations/<migration-file>.sql 
```
:::

#### 1-3-4. DBにTBLが作成されているかを確認する。

```shell:DBにテーブルが作られているか確認。
~/develop/sample-drizzle (master)$ npx wrangler d1 execute sample --command="SELECT name FROM sqlite_master WHERE type='table';" --remote

 ⛅️ wrangler 3.70.0
-------------------

🌀 Executing on remote database sample (be1170e4-6823-4cfb-ac73-7b295e08cfd6):
🌀 To execute on your local development database, remove the --remote flag from your wrangler command.
🚣 Executed 1 commands in 0.1765ms
┌────────┐
│ name   │
├────────┤
│ _cf_KV │
├────────┤
│ users  │
└────────┘
```

:::note info
問題なくできていそうですね。
:::


### 1-4. DBにデータをinsertする

#### 1-4-1. insert文を作成する

```sql:insert_data.sql
INSERT INTO users (username, email, password, is_active) 
VALUES 
('jane_doe', 'jane@example.com', 'anotherpassword', 1),
('alice', 'alice@example.com', 'password123', 1),
('bob', 'bob@example.com', 'mypassword', 1);
```

#### 1-4-2. insert文を実行する

```
npx wrangler d1 execute <database-name> --file=./insert_data.sql --remote
```

### 1-5. APIを作成する

```ts:src/index.ts
import { Hono } from "hono";
import { drizzle } from "drizzle-orm/d1"; // D1専用のドライバーを使用
import { users } from "./schema";
import type { D1Database } from "@cloudflare/workers-types";

type Bindings = {
	DB: D1Database;
};

const app = new Hono<{ Bindings: Bindings }>();

// 全てのユーザーを取得するエンドポイント
app.get("/users", async (c) => {
	try {
		const db = drizzle(c.env.DB); // D1専用ドライバーで初期化
		const allUsers = await db.select().from(users).execute(); // クエリの実行
		return c.json(allUsers);
	} catch (error) {
		return c.json(
			{ error: "Failed to retrieve users" },
			500,
		);
	}
});

export default app;
```

```shell:デプロイ実行
npm run deploy
```


### 1-6. APIを実行する
```
GET: ${URL}/users
[
  {
    "id": 1,
    "username": "jane_doe",
    "email": "jane@example.com",
    "password": "anotherpassword",
    "isActive": 1,
    "createdAt": "CURRENT_TIMESTAMP"
  },
  {
    "id": 2,
    "username": "alice",
    "email": "alice@example.com",
    "password": "password123",
    "isActive": 1,
    "createdAt": "CURRENT_TIMESTAMP"
  },
  {
    "id": 3,
    "username": "bob",
    "email": "bob@example.com",
    "password": "mypassword",
    "isActive": 1,
    "createdAt": "CURRENT_TIMESTAMP"
  }
]
```

取得できましたね。createdAtは想定外の表示になりましたが、これはご愛嬌ですね😅