Cloudflare Workers から TiDB Serverless に接続する (Prisma 版)

2023/11/26に公開

https://zenn.dev/kameoncloud/articles/99d3ed9d5ce4fd
前回の記事ではCloudflare WorkersからTiDB Serverlessに接続する手順を試してみました。
今回は前回の記事でも少し触れた古い手順であるPrisma版を試していきます。古いとはいえPrismaは実績のある、またWorkersやTiDBでは提供していない機能も持っているため、引き続きPrismaを普段から利用されている方はこちらを好むだろうからです。

Prisma とは

chatGPT先生によるとPrisamとは以下の通りです。

Prismaは、オープンソースの次世代ORMで、データベースを簡単に管理および操作できます¹. Prismaは、必須のデータベーススキーマ移行ツールであるPrisma Migrate、自動生成されたタイプセーフなクエリビルダーであるPrisma Client、データベース内のデータ用のビジュアルエディターであるPrisma Studioの3つの主要部分で構成されています。

Prismaは、Node.jsエコシステムで作業しているバックエンドまたはフルスタック開発者にとってはるかに使いやすいことがわかりました。

説明の中に出てくるORMは以下の通り出です。

オブジェクト関係マッピング (ORM) は、オブジェクト指向プログラミングと関係データベースの互換性を向上させるために設計されたプログラミング技術です。ORMは、プログラミング言語のクラスとデータベースのテーブルをマッピングすることから始めます。これにより、エンジニアは直接 SQL クエリの作成することなく、ORMを通じてデータベースと相互作用できます。ORMには、オブジェクト関係マッピング自体の機能を提供するだけでなく、データベーススキーマ移行ツールやタイプセーフなクエリビルダーなどのツールも含まれます。ORMは、バックエンドまたはフルスタック開発者にとってはるかに使いやすいことがわかりました。

要はPrismaを用いることでSQLを直接記載することなくデータベースを操作できるようになります。勿論SQLが書けないから用いる、ということが推奨されないのは言うまでもありませんが、複数データソースを同時に用いる場合の抽象化レイヤーとして有効であり、Cloudflare Workers から TiDB Serverless を呼び出す際に引き続きPrismaを用いるケースもあるのではないか、というわけです。

さっそくやってみる

1. Wrangler のセットアップ

以下の記事でHello World!まで一度流しておきます。
https://zenn.dev/kameoncloud/articles/1fac9762aab4ec

wrangler init prisma-tidb-cloudflare

以下の内容を参考にプロジェクトをinitします。単純にすべての入力にデフォルトで回答しているだけです。

Delegating to locally-installed wrangler@3.10.0 over global wrangler@2.14.0...
Run `npx wrangler init prisma-tidb-cloudflare` to use the local version directly.

 ⛅️ wrangler 3.10.0 (update available 3.17.1)
-------------------------------------------------------
Using npm as package manager.
▲ [WARNING] The `init` command is no longer supported. Please use `npm create cloudflare@2 -- prisma-tidb-cloudflare` instead.

  The `init` command will be removed in a future version.


Running `npm create cloudflare@2 -- prisma-tidb-cloudflare`...

using create-cloudflare version 2.0.9

╭ Create an application with Cloudflare Step 1 of 3
│
├ Where do you want to create your application?
│ dir prisma-tidb-cloudflare
│
├ What type of application do you want to create?
│ type "Hello World" script
│
├ Do you want to use TypeScript?
│ typescript yes
│
├ Copying files from "simple" template
│
├ Do you want to use git?
│ git yes
│
╰ Application created

╭ Installing dependencies Step 2 of 3
│
├ Installing dependencies
│ installed via `npm install`
│ 
├ Committing new files
│ git initial commit
│ 
╰ Dependencies Installed 

╭ Deploy with Cloudflare Step 3 of 3
│ 
├ Do you want to deploy your application?
│ yes deploying via `npm run deploy`

Deployが完了したらHello Worldが出力されていることをブラウザから確認します。

2.Prismaのセットアップ

cd prisma-tidb-cloudflare
npx prisma init
✔ Your Prisma schema was created at prisma/schema.prisma
  You can now open it in your favorite editor.

Next steps:
1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started
2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql, sqlite, sqlserver, mongodb or cockroachdb.
3. Run npx prisma db pull to turn your database schema into a Prisma schema.
4. Run npx prisma generate to generate the Prisma Client. You can then start querying your database.        

More information in our documentation:
https://pris.ly/d/getting-started

Prismaの管理画面でNew Projetを押します。
https://console.prisma.io/

適当な名前を入れてCreate Projectを押します。

WorkersからPrismaを使うためにはmysql://...経由での接続は行えず、Prisma Accelerate経由のprisma://...のみがサポートされています。このため、Accelerateをアクティベートします。Enable Accelerateを押します。
![](https://storage.googleapis.com/zenn-user-upload/4508028898d3-20231126.png

DBへの接続文字列を入力します。

mysql://xxxxuserid.root:xxxxpassword@gateway01.eu-central-1.prod.aws.tidbcloud.com:4000/bookshop?sslaccept=strict

bookshopはWorkersからTiDB Serverlessへの接続に利用していたデータベースです。必ず先の手順を終わらせておいてください。
https://zenn.dev/kameoncloud/articles/99d3ed9d5ce4fd
また?sslaccept=strictは先の手順では使っていませんでしたが絶対必須なパラメータです。
Enable Accelerateを押します。
次にGenerate API Keyを押します。

出力される文字列をcopyしておきます。

DATABASE_URL="prisma://accelerate.prisma-data.net/?api_key=eyJhbGci<snip>"

Wrangler環境のコンソールに戻ります。prisma/@risma.schemaを以下で置き換えます。

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider  = "mysql"
  url       = "prisma://accelerate.prisma-data.net/?api_key=eyJh<snip>"
  directUrl = "mysql://xxxxuserid.root:xxxxpassword@gateway01.eu-central-1.prod.aws.tidbcloud.com:4000/bookshop?sslaccept=strict"
}

次にnpx prisma db pullを実行します。
以下の出力が出ます。

Prisma schema loaded from prisma\schema.prisma
Environment variables loaded from .env
Datasource "db": MySQL database "bookshop" at "gateway01.eu-central-1.prod.aws.tidbcloud.com:4000"

✔ Introspected 1 model and wrote it into prisma\schema.prisma in 8.53s

*** WARNING ***

The following models were ignored as they do not have a valid unique identifier or id. This is currently not supported by Prisma Client:
  - "users"

Run npx prisma generate to generate Prisma Client.

また'prisma.schema'は以下のように書き換わります。

<snip>
/// The underlying table does not contain a valid unique identifier and can therefore currently not be handled by Prisma Client.
model users {
  id       BigInt?
  nickname String?  @db.VarChar(100)
  balance  Decimal? @db.Decimal(15, 2)

  @@ignore
}

つまり'bookshop.users'テーブルはPrisma Clientが利用できる形態になっていないようです。残念!ではTiDB 側の'Chat2Query'でもう一つテーブルを作ってしまいましょう。

CREATE TABLE `bookshop`.`users2` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `nickname` varchar(100),
  `balance` decimal(15,2),
  PRIMARY KEY (`id`)
);
select * from bookshop.users2;

もう一度npx prisma db pullを実行した後npx prisma generateを実行すると'prisma.schema`が書き換わります。

model users2 {
  id       Int      @id @default(autoincrement())
  nickname String?  @db.VarChar(100)
  balance  Decimal? @db.Decimal(15, 2)
}

3.Workersのコード

これでWorkersからPrisma Accelerate 経由で TiDB Serverlessへ接続できる準備が整いました。
'workers.ts'を以下に置換します。

workers.ts
import { PrismaClient } from '@prisma/client/edge';

addEventListener('fetch', (event) => {
	event.respondWith(handleEvent(event))
})

export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
		const prisma = new PrismaClient();
		try {
			const users2 = await prisma.users2.create({
				data: {
					nickname: 'Alice',
					balance: 50,
				}
			});

		} finally {
			await prisma.$disconnect();
		}
		return new Response("test")
	}
}

wrangler deployを実行した後ブラウザで読み込むとtestと出力されます。
select * from bookshop.users2;をTiDB の管理者画面で実行するとAliceが出てきます。

PrismaのAccelerate画面では以下の通りProxyとして実行されたSQLの統計が出てきます。

TiDB CloudのDiagnosis画面ではいろいろとPrisma Accelerate経由で実行されているSQLを確認することが可能です。

Discussion