空雲 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

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';
2
3export interface Env {
4 DATABASE_URL: string;
5}
6
7export 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 });
11
12 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などに書いて、対応されず何年も待っている気の長い人もいますが、自分で作ったほうが早いです。