遂に Cloudflare + Next.js(OpenNext) + Prisma 6.7.0(No Rust) が動く時代が来た
現状たぶんこれが一番安いと思います。(※個人開発前提のスタックです)
実現したこと
- opennext for cloudflare
- prisma (no-rust, no-engine)
- prisma-postgres (free plan)
つまり Cloudflare 上で Next.js を動かして、現実的なビルドサイズで Prisma を動かせました。
自分の手元のビルドサイズです。
┌ ○ /                                      149 B         102 kB
├ ○ /_not-found                            978 B         103 kB
├ ○ /prisma-test                           149 B         102 kB
# ...
+ First Load JS shared by all             102 kB
  ├ chunks/770-76939705ff65587a.js       46.5 kB
  ├ chunks/96e220d1-21a0fdc894793ec0.js  53.2 kB
  └ other shared chunks (total)          1.89 kB
今まではこれが数十 MB~あり、そもそも opennext では色々絡まってビルド不可能だった状態が解消されます。
今までのあらすじ(読まなくてもいい)
Next.js + Prisma + Cloudflare には苦難の歴史があります。
- RSC を使うのに Next.js を使いたいが...
- 素の next.js は当然 cloudflare env にアクセスできない
- opennext for cloudflare でできるようになった
 
- prisma-wasm 版はバンドルサイズに難がある(free-plan ギリギリ)
- paid でも多少コードを書くと超過するかも
 
- という事情が色々あって Prisma 自体が Rust から TS で書き直されている
- prisma 6.7.0 で no rust 版のプレビュー版がでた
- https://github.com/prisma/prisma/releases/tag/6.7.0
- queryCompiler で事前に SQL を変形して外側実行できるように
 
これらがやっと噛み合って動くようになったのが今
Optional: prisma-postgres で DB をホスティング
今回、自分は prisma-postgrss ホスティングサービス(無料枠)を使いました。
すでにある DB やローカルにある postgres に繋ぐ場合は不要ですが、今回は リモート DB に、ローカルのドライバ抜きで 繋ぐ検証なので、これを優先します。
設定ポチポチやって、 東京のリージョンを選択しつつ、 DATABASE_URL だけメモっておいてください。
同等の設定ができるなら supabase でも何でも構いません。
OpenNext for Cloudflare
open といいつつ、ほぼ Cloudflare で Next.js を動かすプロジェクトと理解して大丈夫です。多少 AWS 向けのバインディングがあります。 実質的に next-on-pages の後継です。
next-on-pages はフルビルドしないと cloudflare env につなげませんでしたが、 opennext は開発環境でも miniflare を通じて env の各種バインディングにアクセス可能です。
コード例
import { getCloudflareContext } from "@opennextjs/cloudflare";
export async function GET(request) {
  let responseText = "Hello World";
  const ctx = await getCloudflareContext({ async: true });
  const myKv = ctx.env.MY_KV_NAMESPACE;
  await myKv.put("foo", "bar");
  const foo = await myKv.get("foo");
  return new Response(foo);
}
プロジェクト生成
$ npm create cloudflare@latest -- my-next-app --framework=next --platform=workers
これは普通の Next.js がカスタマイズされたもので、一度 next build してから npx opennextjs-cloudflare build && npx opennextjs-cloudflare upload のように cloudflare 用のアダプタを差し込んで多段でビルドします。
プロジェクト固有のセットアップとして、 next.config.mjs で initOpenNextCloudflareForDev() を呼ぶ必要があります。
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
initOpenNextCloudflareForDev();
あとは普通の next.js と 大きな違いはありませんが、現状 "use cache"; は未対応です。
(今後も vercel 側の新機能の追従は遅くなると思われます)
Prisma (No-Rust) Setup
先にセットアップした prisma-postgres に対して、 prisma の環境を設定します。
$ npm add prisma @prisma/client -D
$ npx prisma init
.env に DATABASE_URL を設定します。
DATABASE_URL="prisma+postgres://accelerate.prisma-data.net/?api_key=<your-api-key>"
prisma/schema.prisma に以下のファイルを設定。
generator client {
  provider = "prisma-client-js"
  previewFeatures = ["queryCompiler", "driverAdapters"]
  output   = "../src/generated/prisma"
}
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
model User {
  id        String   @id @default(cuid())
  name      String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
大事なのは ["queryCompiler", "driverAdapters"] の部分で、他はどうでもいいです。
この時点で本番 DB につながるはずなので、コードを生成してマイグレーションします。
$ npx prisma generate --no-engine
$ npx prisma migrate dev
--no-engine 指定によって、Prisma のランタイムアダプタが生成コードから省かれます。Rust 版はここで .wasm を読み込んで、バンドルサイズの問題が発生していました。
(ローカルマイグレーションに npm add @prisma/adapter-pg も必要かも?あとで確認)
Prisma を実行するコードを書く
import { PrismaClient } from "./generated/prisma";
export const prisma = new PrismaClient();
これを読み込んで、 async function component から実行。
// src/app/prisma-test/page.tsx
import { prisma } from "../../db";
export default async function Page() {
  const users = await prisma.user.findFirst({});
  // 書き込みテスト用
  // const randomUser = await prisma.user.create({
  //   data: {
  //     name: "Random User",
  //   },
  // });
  // console.log("Random User Created:", randomUser);
  return (
    <div>
      <h1>Welcome to the Prisma Test Page</h1>
      <pre>{JSON.stringify(users, null, 2)}</pre>
    </div>
  );
}
これで自分のローカル+プロダクション環境で両方動きました。
デプロイする場合
# wrangler.json を設定する
$ npx opennextjs-cloudflare build && npx opennextjs-cloudflare deploy
備考: ローカルの開発環境の postgres に繋ぐ
本番に直で繋ぎたくない場合の処理です。
最初に compose.yaml で適当にコンテナを建てるとします。
services:
  postgres:
    image: "postgres:16.1"
    restart: always
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: "postgres"
      POSTGRES_PASSWORD: "postgres"
@prisma/adapter-pg でアダプタをセットアップして、これを client の初期化に渡します。
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "./generated/prisma";
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
export const prisma = new PrismaClient({ adapter });
(このとき --no-engine 指定できないかも)
残る課題
- まだ preview release
- opennext の枯れてなさ+多段ビルドの複雑性
- sqlite は better-sqlite3 の実験的なサポートはあるが、 d1 バインディングがない
まとめ
本当に安く済ませたいときの d1 バインディングがなかったりするんですが、一応現実的に動く状態が来た、と言える状態が来たんじゃないでしょうか。
RSC も vercel + next.js の専売特許だったのが、やっと開放されつつあります。



Discussion
MySQLはまだ未対応ですよね。
なるほど、確かにRSCをVercel以外で安定して動かすのは難しかった印象です。ただ、Cloud Run + Next.js でも基本的には動作可能ですよね?もちろんmiddlewareの挙動などは異なりますが、その点はCloudflareでもまだ完全ではないように思います。このあたり、どこにブレイクスルーがあったのか気になります。