Tech Stack
技術スタック
1. 採用技術一覧
Frontend / Framework
Next.js (App Router)
15.x
RSC + Route Handlers で統一的な開発体験
TypeScript
5.x
型安全性・開発体験の向上
Prisma
6.x
型安全な DB アクセス・マイグレーション管理
@prisma/adapter-d1
6.x
Prisma から Cloudflare D1 へ接続するアダプタ
Cloudflare Platform
Cloudflare Workers
—
OpenNext の Workers アセット出力でホスティング
@opennextjs/cloudflare
1.x
Next.js を Cloudflare Workers にデプロイするアダプタ
Cloudflare D1
—
SQLite 互換のサーバーレスエッジ DB
Cloudflare R2
—
S3 互換のオブジェクトストレージ(画像保存)
Cloudflare Queues
—
画像最適化の非同期処理
wrangler
4.x
D1 / R2 / Queues ローカルエミュレーション
Data & State Management
TanStack Query
5.x
宣言的なデータフェッチ・自動キャッシュ
TanStack Table
8.x
Headless UI・高機能テーブル
Jotai
2.x
軽量・atom ベースのグローバル状態
React Hook Form
7.x
高パフォーマンスなフォーム管理
Zod
3.x
スキーマ定義・フロント / バック共有
@jsquash/*
latest
WebP 変換(Workers 上で動作する wasm ベース)
UI / Styling
shadcn/ui
latest
Radix UI ベース・Tailwind CSS 統合
Tailwind CSS
3.x
ユーティリティファースト CSS
framer-motion
12.x
UI アニメーション
lucide-react
latest
SVG アイコンセット
sonner
2.x
トースト通知
cmdk
1.x
コマンドパレット UI
next-themes
latest
ライト / ダークモード管理
tailwind-merge
2.x
Tailwind クラス名マージ
Testing & Tooling
Biome
1.x
Rust 製高速ツール・ESLint + Prettier 代替
Vitest
4.x
Vite ベース・Jest 互換の高速テスト
@cloudflare/vitest-pool-workers
latest
workerd / Miniflare 上での D1 統合テスト
Playwright
1.x
クロスブラウザ E2E テスト
pnpm
10.x
高速・効率的なパッケージ管理
2. 技術選定の詳細
Next.js App Router + @opennextjs/cloudflare
@opennextjs/cloudflareを使って Next.js アプリを Cloudflare Workers(Workers アセット方式)にデプロイする- OpenNext がビルド時に
.open-next/worker.js(Worker 本体)と.open-next/assets/(静的アセット)を生成する - Route Handler ごとに
export const runtime = "edge"を宣言する必要はない - D1・R2・Queues バインディングへは
getCloudflareContext({ async: true })経由でアクセスする
Route Handler の基本構造
import { getCloudflareContext } from "@opennextjs/cloudflare"
export async function GET(request: Request) {
const { env } = await getCloudflareContext({ async: true })
const prisma = createPrismaClient(env.DB) // D1 バインディングを渡す
// ...
}
Prisma + Cloudflare D1
- Prisma Schema の
providerは"sqlite"のまま変更しない - ランタイムでは
@prisma/adapter-d1を使って D1 バインディングに接続する - D1 バインディングはリクエストスコープのため、Prisma Client はリクエストごとに生成する(シングルトン不可)
prisma/schema.prisma
datasource db {
provider = "sqlite"
url = env("DATABASE_URL") // ローカル開発時のみ参照
}
src/lib/prisma.ts
import { PrismaClient } from "@prisma/client"
import { PrismaD1 } from "@prisma/adapter-d1"
export function createPrismaClient(d1: D1Database): PrismaClient {
const adapter = new PrismaD1(d1)
return new PrismaClient({ adapter })
}
Cloudflare D1 の特徴
| 項目 | 内容 |
|---|---|
| 互換性 | SQLite 互換 SQL |
| ロケーション | Cloudflare エッジネットワーク(自動分散) |
| 料金 | 無料枠: 500万行/日の読み取り、10万行/日の書き込み |
| マイグレーション | wrangler d1 migrations apply <DB_NAME> |
| ローカル開発 | wrangler dev で .wrangler/state/ に SQLite ファイルを自動作成 |
| バックアップ | Cloudflare ダッシュボードからエクスポート可能 |
Cloudflare R2(画像ストレージ)
- S3 互換のオブジェクトストレージ。説明画像の保存に使用する
- バインディング名:
TERM_IMAGES(env.TERM_IMAGES) - キー: オリジナル
terms/original/<uuid>.<ext>、軽量版terms/slim/<uuid>.webp - 画像配信は
/api/media/[...path]Route Handler でプロキシする - ローカル開発は Miniflare のメモリ内 R2 を使用(
remote = trueは設定しない)
Cloudflare Queues + 画像最適化 Worker
- アップロード後に
IMAGE_OPTIMIZE_QUEUEにメッセージを送信する workers/image-optimize-consumer.tsが @jsquash を使って WebP 軽量版を生成しterms/slim/に書き込む- デプロイは
pnpm deploy:image-worker(wrangler.image-optimizer.tomlを使用)
wrangler.toml 設定概要
name = "aws-glossary"
main = ".open-next/worker.js"
compatibility_date = "2024-09-23"
compatibility_flags = ["nodejs_compat", "global_fetch_strictly_public"]
[assets]
directory = ".open-next/assets"
binding = "ASSETS"
[[services]]
binding = "WORKER_SELF_REFERENCE"
service = "aws-glossary"
[[d1_databases]]
binding = "DB"
database_name = "aws-glossary-db"
database_id = "<D1_DATABASE_ID>"
migrations_dir = "prisma/migrations"
[[r2_buckets]]
binding = "TERM_IMAGES"
bucket_name = "aws-glossary-images"
# remote = true は設定しない(ローカルは Miniflare in-memory R2 を使用)
[[queues.producers]]
binding = "IMAGE_OPTIMIZE_QUEUE"
queue = "aws-glossary-image-optimize"
TanStack Query
// QueryClient 設定
{
staleTime: 30 * 1000, // 30秒
gcTime: 5 * 60 * 1000, // 5分
retry: 1,
refetchOnWindowFocus: false,
}
// Query Key 形式
["terms", "list", { q, searchBy, category, page }]
Biome
// biome.json(主要設定)
{
"linter": { "enabled": true },
"formatter": { "enabled": true, "indentStyle": "space", "indentWidth": 2 },
"javascript": { "formatter": { "quoteStyle": "double" } }
}
Vitest(t-wada TDD スタイル)
- ユニットテスト: 通常の jsdom 環境で実行(ソースファイルと同ディレクトリに配置)
- D1 統合テスト:
@cloudflare/vitest-pool-workersを使い Miniflare 上で実行
Playwright
tests/e2e/ディレクトリに E2E テストを配置pnpm dev:e2e(pnpm db:migrate:local && pnpm db:seed:local && next dev)に対して実行
3. Cloudflare への公開(最終形態)
本プロジェクトの本番公開は Cloudflare Workers(OpenNext の Workers アセット出力)を前提とする。
| 要素 | 採用内容 |
|---|---|
| ホスティング | Cloudflare Workers([assets] バインディングで静的アセット配信) |
| Next.js ビルド | @opennextjs/cloudflare(OpenNext の Cloudflare アダプター) |
| DB | Cloudflare D1 + @prisma/adapter-d1 |
| 画像ストレージ | Cloudflare R2(env.TERM_IMAGES) |
| 非同期処理 | Cloudflare Queues + 画像最適化 Worker(別 Worker) |
| Route Handlers | getCloudflareContext({ async: true }) で各バインディングに接続 |
デプロイの一般的な流れ
- OpenNext ビルド —
opennextjs-cloudflare buildを実行(.open-next/worker.js+.open-next/assets/を出力) - Workers へのデプロイ —
opennextjs-cloudflare deploy(内部でwrangler deploy)—pnpm deployに集約 - 画像最適化 Worker のデプロイ —
pnpm deploy:image-worker(wrangler deploy -c wrangler.image-optimizer.toml)
押さえておく制約(Workers ランタイム)
Node.js フル互換ではない:
next start 前提のパッケージは動かないことがある。compatibility_flags = ["nodejs_compat", "global_fetch_strictly_public"] を指定すること。
- CPU・実行時間: 長時間・重い処理は Workers の制限対象。画像軽量化(wasm 実行)は Queue 経由で別 Worker に分離
- リクエストスコープ: D1 / R2 バインディングはリクエストごとに取得し、Prisma Client もリクエストごとに生成する(シングルトン不可)
公式参照リンク
- OpenNext — Cloudflare
- Cloudflare Workers — Static Assets
- Cloudflare D1
- Cloudflare R2
- Cloudflare Queues
4. 依存関係
dependencies
next
react / react-dom
@prisma/client
@prisma/adapter-d1 # D1 アダプタ
@opennextjs/cloudflare # Cloudflare Workers デプロイアダプタ
@tanstack/react-query
@tanstack/react-table
jotai
react-hook-form
zod
@hookform/resolvers # RHF + Zod インテグレーション
sonner # トースト通知
framer-motion # アニメーション
cmdk # コマンドパレット
next-themes # ダークモード
lucide-react # アイコン
tailwind-merge
class-variance-authority
@jsquash/jpeg / png / webp / resize # Workers 上の画像処理
devDependencies
prisma
wrangler # D1 ローカル開発・マイグレーション
typescript / @types/react / @types/node
@cloudflare/workers-types # D1Database・R2Bucket・Queue 等の型定義
tailwindcss
@biomejs/biome
vitest # 4.x
@cloudflare/vitest-pool-workers
@testing-library/react
@testing-library/user-event
@playwright/test
shadcn # shadcn/ui CLI