CloudflareWorkersとPrismaの開発環境からLocalDBにアクセスする
CloudflareWorkersとPrismaの制限
CloudflareWorkersはネイティブのバイナリが動作しないため、Prismaを使用する場合はEdge機能を使用する必要があります。PrismaのEdge機能はランタイムエンジンがリモート上に必要になり、Prisma Accelerateなどの外部サービスが必要になります。
ここで困るのがローカルで開発時しているときです。いちいち外部サービスと連携を取らないと開発が行えない問題が発生します。マイグレーションなどを自由に行うなら、ローカルにDBを設置したいところです。ところがCloudflareWorkersとPrismaの組み合わせではそれが出来ません。
不可能を可能にする
まずはローカルにPrisma Accelerateの機能をエミュレーションさせます。
https://www.npmjs.com/package/prisma-accelerate-local
これを使えば、ローカル環境でPrisma AccelerateのようなPrismaエンジンのリモート化ができるようになります。ところが単純にこれを使うには問題があって、PrismaからPrisma Accelerateへの接続はhttpsの必要があります。しかもオレオレ証明書は不可です。Node.jsなら回避方法はあるのですが、CloudflareWorkersのランタイムは回避方法はありません。
ということでprisma-accelerate-localを真っ当なhttpsで接続できるようにします。そのためにはcloudflaredを使って、ローカルポートをhttps対応のドメインからアクセスできるようにします。
cloudflaredを使ってprisma-accelerate-localをhttps化すれば良いのですが、また一つ問題が出ます。コマンド実行ごとにドメインが生成されます。このドメインを固定することは可能なのですが、固定にすると今度はチーム開発時に面倒なことになります。一時生成のドメインをうまく利用してやる必要があります。
https://www.npmjs.com/package/cloudflared-output-domain
これを使って、一時生成されたドメイン名を取得し、.dev.varsに出力して、CloudflareWorkersの環境変数として利用可能にします。ここまでやると、CloudflareWorkersとPrismaでローカルDBにアクセスできるようになります。
実際の設定方法
以下はサンプルリポジトリです。
https://github.com/SoraKumo001/cloudflare-workers-prisma
1{2"scripts": {3 "dev": "npm-run-all -p dev:*",4 "deploy": "wrangler deploy",5 "dev:worker": "cloudflared-output-domain --url http://127.0.0.1:55555 -- echo DATABASE_URL=prisma://{host}/?api_key=dummy > .dev.vars && wrangler dev",6 "dev:docker": "docker compose -f docker/docker-compose.yml up -d",7 "dev:proxy": "prisma-accelerate-local postgresql://postgres:password@localhost:15432/postgres -p 8000 -t",8 "dev:cloudflared": "cloudflared --metrics 127.0.0.1:55555 --url http://127.0.0.1:8000",9 "prisma": "npm-run-all -p prisma:*",10 "prisma:migrate": "prisma format && next-exec prisma migrate dev",11 "prisma:generate": "prisma generate --no-engine"12 }13}
開発用のコマンドは上記のようになります。
dev:proxy
PrismaAccelerateのエミュレータを起動
dev:cloudflared
PrismaAccelerateをhttpsの外部ドメインに転送
dev:worker
ドメインを.dev.varsに出力し開発コードを起動
dev
まとめて実行
src/index.ts
環境変数のDATABASE_URLを受け取ってPrismaClientを作成しローカルDBに接続します。
1import { PrismaClient } from '@prisma/client/edge';23export interface Env {4 DATABASE_URL: string;5}67export default {8 async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {9 const url = new URL(request.url);10 if (url.pathname !== '/') return new Response('Not found', { status: 404 });1112 const prisma = new PrismaClient({ datasourceUrl: env.DATABASE_URL });13 await prisma.post.create({ data: {} });14 const result = await prisma.post.findMany({ orderBy: { createdAt: 'desc' } });15 return new Response(JSON.stringify(result, undefined, ' '), {16 headers: { 'content-type': 'application/json' },17 });18 },19};
概略図
1 [postgres://localhost:15432] [http://localhost:8000] [https://xxxx.trycloudflare.com/]2PostgreSQL <-> prisma-accelerate-local <-> cloudflared <-> prisma/edge
まとめ
設定が面倒くさいですが、ローカルDBで開発が可能となりました。こういう問題をIssuesなどに書いて、対応されず何年も待っている気の長い人もいますが、自分で作ったほうが早いです。