始まり始まり
記事なんて書くだけで偉いですからね、もし僕がこの1行だけ書いて保存してアップロードしたところで、それはそれで現代アートみたいで「うーむ深い」という人がいると思いますが、早起きしたので続きを書きます。
今回は約4日でフルスタックのアプリケーションを作りました
AIがインタビューして記事を作成してくれるサービスで、Twitterのコミュニティでのお題に合わせて開発したということと、1年前に購入したOpen AIのクレジットが期限切れになるのでとにかくAPIを使うためのものを作りたかったという背景があります。
自分の考えをアウトプットするぞ!ってなると意外と「あれ、何が書きたいんだっけ」とか、「うーんななんか違う」という感じで結局何も書かないことってあると思うのですが、でも不思議、誰かに話すときはスラスラ話せたりするんですよね、ということで、アウトプットのハードルを下げるためには「インタビュー」というものが有効だと思いました。しかも相手がAIだと否定もされないのでとてもやりやすいですね。良かったら試してみてください。
今回のアプリケーションでは以下のような技術を使用しています。
#### スタック
- OpenNext
- shadcn/ui
- tailwindcss
- drizzle-orm
- better-auth
- ai-sdk
#### Feature
以下の機能を試してみたい人はリポジトリを見てもらえれば実装方法がわかると思います。
UI上の工夫
- `gpt-4o-transcribe`を使用した音声入力とAIによる校正
- インタビューでのAIのレスポンスのストリーミング
- 記事生成のAIのレスポンスのストリーミング
- 記事OGの動的生成
裏側の工夫
- betterAuthで新規ユーザー作成後に他のDBにデータを登録する
- ストリームしたデータをサーバーサイドで保存する
日付で見ると結構多いなと思ったけど、実質の作業時間は4人日(8h * 4)
くらいだったので4日ということにします。まるまる休みが4日あったら絶対できてたからね!(Vibe Codingはしてません)
git log --date=short --format="%ad" | sort -u
2025-07-28
2025-07-31
2025-08-03
2025-08-05
2025-08-09
2025-08-12
2025-08-13
2025-08-14
この記事を読むと何がわかるのか
この記事を読むと、「OpenNextを使用したNext.js on Cloudflare Workers
の構成の開発でどんなことができるのか」ということがわかります。
結論から言うと、今まで通りのNext.jsでの開発に加え、Next.jsのサーバーサイドのコードでCloudflare Workersのリソースやコンテキストを使用した開発
ができます。(next devでも使用できます)
これによって今回、Vercelでホスティングするときに比べて費用が抑えられたり、D1やKVを使用できるので、他のクラウドDBを使用するよりも費用が抑えられるというメリットがあります。
なんかここまで書いてみて、ドキュメントに書いてあるし普通にドキュメント見てる人なら誰でも知ってるなと思ったんですが、まあ具体的な使用例として見て貰えばと思います。
リポジトリに関しては公開してるので、必要であれば参照ください。
あらかじめ
言っておくと、これはCloudflare WorkersのPaid Plan
が必要になると思います。めちゃくちゃシンプルな構成なら不要ですが、そもそもOpenNextがでかいので、自分はPaid Plan
が必要になりました。
本編
セットアップ
セットアップに関してはCloudflareのドキュメントを参考にしました。
ここに書いてある通り、以下のコマンドだけで最初の土台が出来上がります。
pnpm create cloudflare@latest {appName} --framework=next
環境変数について
ここからは、体系的にドキュメントにまとめられていなかったり、探すのが難しいと思ったものについてリンク貼ったり、サンプルを示したりして行きます。丸ごとAIに読ませて開発したらいい感じになると思います。
Cloudflare Workersでローカルの環境変数を使用する場合は.dev.vars
というファイルを使用するのですが、OpenNextでもこれを使用できるようになっています。ですが、next dev
などの実行時に読み込むことができないので、環境変数自体は.env
や.env.development
などに記載します。
next dev
の実行時に.env.development
を読み込みたい場合は以下のように.dev.vars
でNEXTJS_ENV
を指定します。.dev.vars
に書くのはこれだけです。
# .dev.vars
NEXTJS_ENV=development
そして、以下のような環境変数は.env.development
に記載します。
import { createEnv } from '@t3-oss/env-nextjs'
import { z } from 'zod'
export const env = createEnv({
server: {
BETTER_AUTH_SECRET: z.string(),
BETTER_AUTH_URL: z.url(),
TURSO_DATABASE_URL: z.url(),
TURSO_AUTH_TOKEN: z.string(),
GOOGLE_CLIENT_ID: z.string(),
GOOGLE_CLIENT_SECRET: z.string(),
OPENAI_API_KEY: z.string(),
OPENAI_ADMIN_API_KEY: z.string(),
OPENAI_PROJECT_ID: z.string(),
},
client: {
NEXT_PUBLIC_APP_URL: z.url(),
},
runtimeEnv: {
BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET,
BETTER_AUTH_URL: process.env.BETTER_AUTH_URL,
TURSO_DATABASE_URL: process.env.TURSO_DATABASE_URL,
TURSO_AUTH_TOKEN: process.env.TURSO_AUTH_TOKEN,
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
OPENAI_ADMIN_API_KEY: process.env.OPENAI_ADMIN_API_KEY,
OPENAI_PROJECT_ID: process.env.OPENAI_PROJECT_ID,
},
})
next dev時のBindingsについて
next dev
時のBindings
ではwrangler.jsonc
のenv
でdevelopment
とかに設定してあっても読み込まれないので、トップレベルでの指定が必要になります。ローカルではid
とかは適当でも動きます。
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "thisisooo",
"main": ".open-next/worker.js",
"compatibility_date": "2025-03-01",
"compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],
"assets": {
"binding": "ASSETS",
"directory": ".open-next/assets"
},
"observability": {
"enabled": true
},
"workers_dev": false,
"kv_namespaces": [
{
"binding": "KV",
"id": "next-dev-kv"
}
],
"d1_databases": [
{
"binding": "D1",
"database_name": "d1",
"database_id": "next-dev-d1",
"migrations_dir": "drizzle/d1"
}
],
}
preview
時には以下のようにしてenv
を指定すればwrangler.jsonc
の対象のenvのBindings
が使用できるようになります。
opennextjs-cloudflare build && opennextjs-cloudflare preview --env development
Preview時のエラーについて
Previewで動かそうとしたときに、バンドル時にエラーが発生することがありますが、こちらに書いてあるように、対象のライブラリをnext.config.ts
のserverExternalPackages
に追加することでエラーを解消できるようになります。
const nextConfig: NextConfig = {
serverExternalPackages: [
'@libsql/client',
'@libsql/isomorphic-ws',
],
}
D1の使用
getCloudflareContext
を使用することで、サーバーサイドではどこでもCloudflare Workersのリソースにアクセスすることができるようになります。
上記ページにあるように、getDbという関数を作成してDBにアクセスするようにすると良いです。
import { getCloudflareContext } from "@opennextjs/cloudflare";
import { drizzle } from "drizzle-orm/d1";
import { cache } from "react";
import * as schema from "./schema/d1";
export const getDb = cache(() => {
const { env } = getCloudflareContext();
return drizzle(env.MY_D1, { schema });
});
export const getDbAsync = cache(async () => {
const { env } = await getCloudflareContext({ async: true });
return drizzle(env.MY_D1, { schema });
});
リクエストのキャッシュ
現在はISRとか使用せずに単純に重いfetchをrevalidate: 3600
でキャッシュしているだけなので、以下のようにwrangler.jsonc
に追加するのと、
"services": [
{
"binding": "WORKER_SELF_REFERENCE",
"service": "thisisooo"
}
],
"r2_buckets": [
{
"binding": "NEXT_INC_CACHE_R2_BUCKET",
"bucket_name": "thisisooo-next-incremental-cache-prod"
}
],
以下のようにopen-next.config.ts
に記載するだけでキャッシュが使用できるようになります。
import { defineCloudflareConfig } from '@opennextjs/cloudflare'
import r2IncrementalCache from '@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache'
export default defineCloudflareConfig({
incrementalCache: r2IncrementalCache,
})
また、withRegionalCache
を使用することでfetch時のR2キャッシュへのアクセスを減らすことができます。
import { defineCloudflareConfig } from '@opennextjs/cloudflare'
import r2IncrementalCache from '@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache'
import { withRegionalCache } from '@opennextjs/cloudflare/overrides/incremental-cache/regional-cache'
export default defineCloudflareConfig({
incrementalCache: withRegionalCache(r2IncrementalCache, {
mode: 'long-lived',
shouldLazilyUpdateOnCacheHit: true,
}),
})
その他
に記載されている通り、waitUntilなどもNext.jsのサーバーサイドで使用することができます。
終わりに
今回はNext.js on Cloudflare Workers
の構成でフルスタックのアプリケーションを開発してみました。
Vercelでは手軽さと引き換えに、ユーザーが増えた時の費用面の負担があったのですが、今回Cloudflareで問題なく動かせることがわかったので、今後もこの構成で開発することが多くなりそうです。
Server Actionsを使用した開発は一人でどちらもできる人であれば高速にフルスタックアプリケーションを開発できるので、早く安くそして品質高くできる構成としてこれはかなり良いと思いました。
Views: 0