📀

【Cloudflare】Workers から Prisma で D1 を操作する

2024/10/20に公開

Cloudflare Workers から D1 を操作する際に Prisma を使ってみたので、その手順をまとめます。

Worker セットアップ

C3 で Worker を作ります。

$ npm create cloudflare@latest

D1 セットアップ

Wrangler CLI で D1 Database を作ります。

$ npx wrangler d1 create example_database

wrangler.toml に D1 の設定を追加し、Worker に D1 をバインドします。
これにより Worker から D1 にアクセスできるようになります。
各プロパティの詳細は公式ドキュメントを参照してください。

https://developers.cloudflare.com/workers/wrangler/configuration/#d1-databases

<snip>

[[d1_databases]]
binding = "DB"
database_name = "example_database"
database_id = "xxx"

Prisma セットアップ

Prisma CLI、Driver をインストールします。

$ npm install @prisma/client @prisma/adapter-d1
$ npm install --save-dev prisma

Prisma スキーマ作成

./prisma/schema.prisma にスキーマを定義します。

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["driverAdapters"]
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
}

マイグレーション

マイグレーションファイルの作成とマイグレーションの適用は Wrangler CLI で行います。
SQL の生成は Prisma CLI の migrate diff コマンドを使います。このコマンドはモデルとデータベースの差分を出力してくれるものです。オプション指定によりマイグレーション用 SQL の生成が可能です。
なお、上記は Prisma の公式ドキュメントに記載されている手順です。

https://www.prisma.io/docs/orm/overview/databases/cloudflare-d1#migration-workflows

Wrangler CLI でマイグレーションファイルを作成します。

$ npx wrangler d1 migrations create example_database create_user_table

Prisma CLI で SQL を生成し、マイグレーションファイルに書き出します。

$ npx prisma migrate diff \
    --from-empty --to-schema-datamodel ./prisma/schema.prisma \
    --script --output migrations/0001_create_user_table.sql
-- CreateTable
CREATE TABLE "User" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "email" TEXT NOT NULL,
    "name" TEXT
);

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

マイグレーションを適用し、テーブルを作成します。
--remote オプションを指定しない場合は、ローカルのデータベースに適用されます。

$ npx wrangler d1 migrations apply example_database --remote

Prisma 操作

Prisma を使って D1 を操作してみます。

User モデルを操作できるように、Prisma Client を生成しておきます。

$ npx prisma generate

レコード追加

/insert にリクエストが来た場合に、create メソッドを使ってレコードを追加する実装をしてみます。

import { PrismaD1 } from '@prisma/adapter-d1';
import { PrismaClient } from '@prisma/client';

interface Env {
  DB: D1Database;
}

async function insert(env: Env): Promise<Response> {
  const adapter = new PrismaD1(env.DB);
  const prisma = new PrismaClient({ adapter });

  await prisma.user.create({
    data: {
      name: 'Alice',
      email: 'test@example.com',
    },
  });

  return new Response('Inserted', { status: 200 });
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (new URL(request.url).pathname === '/insert') {
      return insert(env);
    }

    return new Response('Not Found', { status: 404 });
  },
};

Worker をデプロイし、/insert にリクエストを送ると、レコードが追加されます。

$ npx wrangler deploy
$ curl ${Worker のエンドポイント}/insert
Inserted

レコード取得

/select にリクエストが来た場合に、findMany メソッドを使ってレコードを取得する実装をしてみます。

import { PrismaD1 } from '@prisma/adapter-d1';
import { PrismaClient } from '@prisma/client';

interface Env {
  DB: D1Database;
}

async function insert(env: Env): Promise<Response> {
  const adapter = new PrismaD1(env.DB);
  const prisma = new PrismaClient({ adapter });

  await prisma.user.create({
    data: {
      name: 'Alice',
      email: 'test@example.com',
    },
  });

  return new Response('Inserted', { status: 200 });
}

async function select(env: Env): Promise<Response> {
  const adapter = new PrismaD1(env.DB);
  const prisma = new PrismaClient({ adapter });

  const users = await prisma.user.findMany();

  return new Response(JSON.stringify(users), { status: 200 });
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (new URL(request.url).pathname === '/insert') {
      return insert(env);
    } else if (new URL(request.url).pathname === '/select') {
      return select(env);
    }

    return new Response('Not Found', { status: 404 });
  },
};
$ npx wrangler deploy
$ curl ${Worker のエンドポイント}/select
[{"id":1,"email":"test@example.com","name":"Alice"}]

所感

Prisma の API で D1 を扱えるのは嬉しいですね。
しかし @prisma/adapter-d1 は 2024/10/19 現在プレビュー版であるので、本番環境での利用には検討が必要かもしれないです。GA 版リリースを待ちましょう。

Discussion