Cloudflare の nodejs_compat_v2 を有効にして prisma から pg を使う
nodejs_compat_v2
nodejs_compat_v2 は Cloudflare Workers/Pages を扱う場合に、Node.js の機能と互換性を持たせることができます。nodejs_compat よりも対応する幅が増えました。compatibility_date の 2024-09-23 以降からは、v2 の機能がnodejs_compatに統合される予定です。
ちなみに nodejs_compat は機能がランタイム側にあるため、利用してもデプロイ時のサイズに影響を与えないのですが、nodejs_compat_v2は デプロイ前にPolyfillが働くらしく、サイズ増加を覚悟する必要があります。
さっそく pg を使ってみる
Node.js の互換性が上がったということは、 Polyfill を追加で指定せずに pg を使うことができるかもしれません。早速 wrangler 上で Prisma を動作させてみました。
@prisma/adapter-pg + pg
1 [ERROR] TypeError: http://this.stream.once is not a function`
ビルドは通るものの、実行時にエラーになりました。
@prisma/adapter-pg-worker + @prisma/adapter-pg-worker
1X [ERROR] TypeError: Illegal invocation: function called with incorrect `this` reference.
こちらはCloudflare用のパッケージなので、本来動くはずなのですが、 nodejs_compat_v2 によって pg が誤動作するため、逆に動かなくなりました。
動かす
ここで残念ながら動きませんでしたで終わらせるのはあまりにアホなので、pg を nodejs_compat_v2 に対応させます。ということで、問題点を調べ修正を加え、パッチを作りました。
https://www.npmjs.com/package/pg-compat
これをpgと一緒にインストールすると誤動作の原因のコードを自動修正します、これで pg が nodejs_compat_v2 で動作するようになります。
サンプル
Remix + Prisma + PostgreSQL のサンプルを作りました。nodejs_compat_v2を有効にして動作させています。Prisma は @prisma/adapter-pg + pgの組み合わせで動作させています。これによって Node.js で動く開発環境と、Build 後の Wrangler 上の環境で、パッケージを切り替えずに動作させられます。
https://github.com/SoraKumo001/remix-nodejs_compat_v2
wrangler.toml
1compatibility_date = "2024-08-21"2compatibility_flags = ["nodejs_compat_v2"]
app/routes/_index.tsx
1import { LoaderFunctionArgs } from "@remix-run/cloudflare";2import { useLoaderData } from "@remix-run/react";3// @prisma/xxx-worker is not used4import pg from "pg";5import { PrismaPg } from "@prisma/adapter-pg";6import { PrismaClient } from "@prisma/client";78export default function Index() {9 const values = useLoaderData<string[]>();10 return (11 <div>12 {values.map((v) => (13 <div key={v}>{v}</div>14 ))}15 </div>16 );17}1819export async function loader({20 context,21}: LoaderFunctionArgs): Promise<string[]> {22 const url = new URL(context.cloudflare.env.DATABASE_URL);23 const schema = url.searchParams.get("schema") ?? undefined;24 const pool = new pg.Pool({25 connectionString: context.cloudflare.env.DATABASE_URL,26 });27 const adapter = new PrismaPg(pool, { schema });28 const prisma = new PrismaClient({ adapter });29 await prisma.test.create({ data: {} });30 return prisma.test.findMany().then((r) => r.map(({ id }) => id));31}
まとめ
とりあえずパッチを作って動くようにはしましたが、いずれ pg のバージョンアップでパッチが不要になることでしょう。