空雲 Blog

Eye catchCloudflareWorkersとPrismaの開発環境からLocalDBにアクセスする

publication: 2024/01/13
update:2024/02/20

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対応のドメインからアクセスできるようにします。

https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-local-tunnel/

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などに書いて、対応されず何年も待っている気の長い人もいますが、自分で作ったほうが早いです。