空雲 Blog

Eye catchPrisma Accelerate の Self Host で Cloudflare + Remix + PostgreSQL

publication: 2024/10/14
update:2024/10/16

Prisma Accelerate の Self Hosting の必要性

Prisma Client でクエリを作ると、Prisma Engine を介して、各 DB に対応した SQL に変換されます。Prisma Engine は Rust で記述されており、環境ごとにバイナリが用意されています。しかし Cloudflare Workers/Pages や Vercel Edge Functions、Deno などではネイティブのバイナリが使用できないため、WebAssembly 版の Prisma Engine が提供されています。ただ、WebAssembly 版はサイズが大きく、900KB 程度あります。Cloudflare Workers/Pages の無料版や Vercel Edge Functions では 1MB というサイズ制限があるため、その他のコードと合わせてデプロイすることが難しいです。

この状況を解決するために、Prisma 公式サービスで Prisma Accelerate が提供されています。Prisma Accelerate は Prisma Client から Prisma Engine を切り離してリモートで提供するサービスです。この機能を利用すると、Prisma Client で作成されたクエリはリモートサーバで実行されるため、ローカルにエンジンを必要としなくなります。こうすることで、Prisma Engine のサイズを気にすることなく、Prisma Client を利用できます。

https://www.prisma.io/pricing

上記の通り、無料でも一ヶ月 6 万件のクエリまで無料で利用できます。それを超えると 100 万クエリあたり$18 となります。アクセスが増えた時に結構な出費となるため、自前で Prisma Accelerate に相当する機能を運用したくなります。ただ、公式で Self Hosting の提供はされていないため、自前でなんとかする必要があります。

Self Hosting の方法

Self Hosting 用パッケージ

こちらに Prisma Accelerate を Self Hosting するパッケージを用意しました。

https://www.npmjs.com/package/prisma-accelerate-local

Cloudflare と Deno Deploy でサンプル

サンプルは PostgreSQL を使用していますが、他の DB でも同様の方法で利用できます。以下のリポジトリを Deploy すれば、Prisma Accelerate を Self Hosting できます。あとは、発行されたアドレスに対してリクエストを送るだけで、Prisma Accelerate を利用できます。

Deno Deploy での Self Hosting

https://github.com/SoraKumo001/prisma-accelerate-deno

無料枠で 100 万/月 リクエストまで利用できます。コードサイズに制限がないため、Prisma Engine のサイズを気にする必要がありません。

Cloudflare Workers での Self Hosting

https://github.com/SoraKumo001/prisma-accelerate-workers

無料枠で 10 万/日 リクエストまで利用できます。無料プランでは圧縮時のコードサイズが 1MB を超えるとエラーが発生するため、Prisma Engine のサイズに注意が必要です。

Self Hosting のコード解説

基本的な動作は prisma-accelerate-local が行うため、リモートアクセス時に必要となる secret の設定や、Prisma Engine を動作させるために必要な WASM ファイルの読み込みや Adapter の作成を行います。

1import { Pool } from "pg";
2import { PrismaPg } from "@prisma/adapter-pg";
3import { createFetcher } from "prisma-accelerate-local/workers";
4import WASM from "@prisma/client/runtime/query_engine_bg.postgresql.wasm";
5
6export type Env = {
7 SECRET: string;
8};
9
10export default {
11 fetch: createFetcher({
12 runtime: () =>
13 require(`@prisma/client/runtime/query_engine_bg.postgresql.js`),
14 secret: (env: Env) => env.SECRET,
15 queryEngineWasmModule: WASM,
16 adapter: (datasourceUrl: string) => {
17 const url = new URL(datasourceUrl);
18 const schema = url.searchParams.get("schema") ?? undefined;
19 const pool = new Pool({
20 connectionString: url.toString() ?? undefined,
21 });
22 return new PrismaPg(pool, {
23 schema,
24 });
25 },
26 }),
27};

使い方

API Key の作成

無料で使える PostgreSQL のサービス Supabase を使う場合の例です。速度的に 6543 の pooler の方を使うことをおすすめします。

1npx prisma-accelerate-local -s secret -m postgres://postgres.xxxxxxxx:xxxxxxx@aws-0-ap-northeast-1.pooler.supabase.com:6543/postgres

すると以下のような Key が生成されます。

1eyJhbGciOiJIUzI1NiJ9.eyJkYXRhc291cmNlVXJsIjoicG9zdGdyZXM6Ly9wb3N0Z3Jlcy54eHh4eHh4eDp4eHh4eHh4QGF3cy0wLWFwLW5vcnRoZWFzdC0xLnBvb2xlci5zdXBhYmFzZS5jb206NjU0My9wb3N0Z3JlcyIsImlhdCI6MTcyODgyNTc3NywiaXNzIjoicHJpc21hLWFjY2VsZXJhdGUifQ.Hyn0W8aBbTJ77BhqAuNkJeHEohXaLM7K0AxUtppcz8A

この Key を使って、Prisma の接続先を設定します。

  • .env

1DATABASE_URL="prisma://
2xxxxxx.xxxxx.workers.dev?api_key=eyJhbGciOiJIUzI1NiJ9.eyJkYXRhc291cmNlVXJsIjoicG9zdGdyZXM6Ly9wb3N0Z3Jlcy54eHh4eHh4eDp4eHh4eHh4QGF3cy0wLWFwLW5vcnRoZWFzdC0xLnBvb2xlci5zdXBhYmFzZS5jb206NjU0My9wb3N0Z3JlcyIsImlhdCI6MTcyODgyNTc3NywiaXNzIjoicHJpc21hLWFjY2VsZXJhdGUifQ.Hyn0W8aBbTJ77BhqAuNkJeHEohXaLM7K0AxUtppcz8A"

Remix での利用

https://github.com/SoraKumo001/cloudflare-remix-accelerate

上記のリポジトリで、Remix で Prisma Accelerate を利用する方法を解説します。

環境変数

サンプルとして Supabase の接続情報を設定します。

  • .env

prisma migrate devを実行するのに必要な情報です。

1DIRECT_DATABASE_URL=postgres://postgres.xxxx:xxxxx@aws-0-ap-northeast-1.pooler.supabase.com:5432/postgres?schema=public

  • .dev.var

実行時に必要な情報です。Self Hosting した Prisma Accelerate のアドレスと API Key を設定します。

1DATABASE_URL=prisma://xxxx.xxxx.workers.dev?api_key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Prisma Client の利用

  • app/routes/_index.tsx

@prisma/client/edgeから PrismaClient をインポートします。ちなみに PrismaClient のインスタンスは、リクエストを超えて使い回すことは出来ないので、毎回新しいインスタンスを作成する必要があります。詳細はこちらを参照してください。

1import { PrismaClient } from "@prisma/client/edge";
2import { LoaderFunctionArgs } from "@remix-run/cloudflare";
3import { useLoaderData } from "@remix-run/react";
4
5export default function Index() {
6 const values = useLoaderData<string[]>();
7 return (
8 <div>
9 {values.map((v) => (
10 <div key={v}>{v}</div>
11 ))}
12 </div>
13 );
14}
15
16export async function loader({
17 context,
18}: LoaderFunctionArgs): Promise<string[]> {
19 const prisma = new PrismaClient({
20 datasourceUrl: context.cloudflare.env.DATABASE_URL,
21 });
22 await prisma.test.create({ data: {} });
23 return prisma.test.findMany({ where: {} }).then((r) => r.map(({ id }) => id));
24}

まとめ

Prisma Accelerate を Self Hosting することで、Prisma Engine のサイズを気にすることなく、Prisma Client を Cloudflare Workers/Pages の無料プランや Vercel Edge Functions でも利用できるようになります。また、WebAssembly 版の Prisma Engine を直接利用する場合は、Adapter 設定などを書く必要がありますが、Prisma Accelerate の Self Hosting を利用することで、その手間を省くことができます。特に無料プラン運用を考えると、Remix のようなフレームワークと Prisma Engine の同居はサイズ的に不可能なので 事実上 Engine 分離は必須です。