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
{
"scripts": {
"dev": "npm-run-all -p dev:*",
"deploy": "wrangler deploy",
"dev:worker": "cloudflared-output-domain --url http://127.0.0.1:55555 -- echo DATABASE_URL=prisma://{host}/?api_key=dummy > .dev.vars && wrangler dev",
"dev:docker": "docker compose -f docker/docker-compose.yml up -d",
"dev:proxy": "prisma-accelerate-local postgresql://postgres:password@localhost:15432/postgres -p 8000 -t",
"dev:cloudflared": "cloudflared --metrics 127.0.0.1:55555 --url http://127.0.0.1:8000",
"prisma": "npm-run-all -p prisma:*",
"prisma:migrate": "prisma format && next-exec prisma migrate dev",
"prisma:generate": "prisma generate --no-engine"
}
}開発用のコマンドは上記のようになります。
dev:proxy
PrismaAccelerateのエミュレータを起動
dev:cloudflared
PrismaAccelerateをhttpsの外部ドメインに転送
dev:worker
ドメインを.dev.varsに出力し開発コードを起動
dev
まとめて実行
src/index.ts
環境変数のDATABASE_URLを受け取ってPrismaClientを作成しローカルDBに接続します。
import { PrismaClient } from '@prisma/client/edge';
export interface Env {
DATABASE_URL: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
if (url.pathname !== '/') return new Response('Not found', { status: 404 });
const prisma = new PrismaClient({ datasourceUrl: env.DATABASE_URL });
await prisma.post.create({ data: {} });
const result = await prisma.post.findMany({ orderBy: { createdAt: 'desc' } });
return new Response(JSON.stringify(result, undefined, ' '), {
headers: { 'content-type': 'application/json' },
});
},
};概略図
[postgres://localhost:15432] [http://localhost:8000] [https://xxxx.trycloudflare.com/]
PostgreSQL <-> prisma-accelerate-local <-> cloudflared <-> prisma/edgeまとめ
設定が面倒くさいですが、ローカルDBで開発が可能となりました。こういう問題をIssuesなどに書いて、対応されず何年も待っている気の長い人もいますが、自分で作ったほうが早いです。