◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/開発ツール
⬡ 開発ツール/2026-04-01上級

Rork Max × tRPC × Cloudflare Workers:型検査が緑でも本番が 500 を返す3つの境界

tRPC と Cloudflare Workers でエッジAPIを組む実装手順に加えて、型検査を通り抜けてしまう3つの境界—D1の列名と出力スキーマ、KVキャッシュの戻り値、レート制限の計数—を実測値つきで検証し、修正版のコードまで示します。

tRPCCloudflare Workers25型安全エッジAPIRork Max236TypeScript8バックエンド11D1

✦ プレミアム記事

tsc は緑でした。trpc.user.getProfile.useQuery の戻り値には補完が効き、profile.name も profile.bio も、エディタ上では何ひとつ間違っていませんでした。

それでも、デプロイした Worker は INTERNAL_SERVER_ERROR を返し続けました。

原因は型ではありませんでした。D1 が返す列名が avatar_url で、.output() に渡した Zod スキーマが期待していたのは avatarUrl だった、というだけのことです。型検査はビルド時にコードの内側だけを見ます。データベースの列名は、その外側にあります。

個人開発では、この手の失敗に気づく人が自分しかいません。私がこの一件で学んだのは、tRPC が保証してくれる範囲と、保証してくれない範囲の線引きでした。ここを曖昧にしたまま「型安全だから安心」と考えると、コンパイラが通した設計がそのまま本番で 500 を返します。

以下では、tRPC(TypeScript Remote Procedure Call)と Cloudflare Workers を組み合わせたバックエンドを組み直す手順を追いながら、型が守ってくれる境界がどこで切れるのかを毎回明示していきます。ルーターの実装だけでなく、D1 との列名の対応、KV キャッシュの戻り値の形、レート制限の計数の正確さ——私が個人開発で実際に踏んで、手元で数字を取り直したところまで含めて共有します。

tRPC が守る範囲と、守らない範囲

先に全体像を置いておきます。以降の実装は、この表の「守らない」側を人手で埋めていく作業だと考えていただくと読みやすいはずです。

境界型の保証破れたときに起きること
クライアント ↔ ルーター定義あり(`AppRouter` 型の共有)ビルドが落ちる。本番には出ない
入力値 ↔ Zod スキーマあり(実行時に検証)`BAD_REQUEST` が返る。想定内の失敗
D1 の列名 ↔ 出力スキーマなし`.output()` が実行時に落ち、全件 500
KV の戻り値 ↔ 宣言した型なし(`as T` で素通り)形の違う値が型どおりの顔で返る
環境変数(Secrets)の存在なしデプロイ後の初回リクエストで気づく

型が効くのは上の 2 行だけです。下の 3 行は、書き手が意識して閉じない限り開きっぱなしになります。

tRPC と Cloudflare Workers の組み合わせが強力な理由

tRPC の核心:型の自動伝播

従来の REST API 開発では、バックエンドとフロントエンドで同じ型定義を別々に管理するのが一般的でした。しかし tRPC では、サーバー側で定義した型が自動的にクライアント側に伝わります。

// サーバー側(Cloudflare Workers)
export const appRouter = router({
  getUserProfile: publicProcedure
    .input(z.object({ userId: z.string().uuid() }))
    .query(async ({ input, ctx }) => {
      // 戻り値の型が自動的にクライアントに伝わる
      return {
        id: input.userId,
        name: 'Masaki',
        email: 'user@example.com',
        createdAt: new Date().toISOString(),
      };
    }),
});
 
// AppRouter 型をエクスポートしてクライアントと共有
export type AppRouter = typeof appRouter;

このように定義すると、Rork Max 側のコードでは補完が効き、型エラーが即座に検出されます。

Cloudflare Workers のエッジ実行の優位性

Cloudflare Workers は世界 300 以上のデータセンターで動作するエッジコンピューティング環境です。ユーザーに最も近いデータセンターでコードが実行されるため、従来のサーバーレス関数と比べてコールドスタートがなく、レイテンシが極めて低い点が特徴です。

個人開発者にとっての現実的なメリットとして、無料プランで月 10 万リクエストまで処理でき、従量課金のコストも非常に安価です。Rork Max アプリのバックエンドとして、スタートアップのコストを最小限に抑えながら本番グレードのインフラを利用できます。

環境構築:プロジェクト全体の構成設計

モノレポ構成の採用

型を共有するために、フロントエンド(Rork Max)とバックエンド(Cloudflare Workers)を同一リポジトリで管理するモノレポ構成を推奨します。

my-rork-app/
├── apps/
│   └── mobile/        ← Rork Max プロジェクト
│       ├── app/
│       ├── package.json
│       └── ...
├── packages/
│   └── api/           ← tRPC バックエンド(Cloudflare Workers)
│       ├── src/
│       │   ├── index.ts       ← Workers エントリーポイント
│       │   ├── router/        ← tRPC ルーター定義
│       │   ├── middleware/    ← 認証・ログ等のミドルウェア
│       │   └── db/            ← Supabase / D1 連携
│       ├── wrangler.toml
│       └── package.json
├── package.json       ← ワークスペースルート
└── pnpm-workspace.yaml

依存パッケージのインストール

# バックエンドパッケージ
cd packages/api
pnpm add @trpc/server hono @hono/trpc-server zod
pnpm add -D wrangler @cloudflare/workers-types typescript
 
# Rork Max(モバイル)側
cd apps/mobile
pnpm add @trpc/client @trpc/react-query @tanstack/react-query zod

wrangler.toml の設定

# packages/api/wrangler.toml
name = "my-rork-api"
main = "src/index.ts"
compatibility_date = "2026-03-01"
compatibility_flags = ["nodejs_compat"]
 
[vars]
ENVIRONMENT = "production"
 
[[kv_namespaces]]
binding = "CACHE"
id = "your-kv-namespace-id"
 
[[d1_databases]]
binding = "DB"
database_name = "my-rork-db"
database_id = "your-database-id"
✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦SELECT * と .output() を同じ手続きに並べると本番で全件 500 になる理由と、境界に変換関数を1枚置く修正版コードを手に入れられる
✦「60回/分」と書いたレート制限が実測 30.5 回/分しか通さない原因を計測結果つきで確認し、時刻バケット方式の修正実装をそのまま流用できる
✦KVキャッシュが D1Result を配列の顔で返す罠と、waitUntil による書き込み待ちの解消まで含めた実装を把握できる
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

⬡ 開発ツール2026-07-18
文字サイズ最大とドイツ語で、生成された画面は静かに詰まる — Rork Max の SwiftUI にレイアウト耐性の検証を通した記録
Rork Max が生成した SwiftUI 画面を疑似ローカライズと最大文字サイズに通し、どこから詰まるかを機械的に洗い出した記録です。ViewThatFits・ScaledMetric・layoutPriority の使いどころと、再生成のたびに回帰を捕まえるスナップショット検証を、実際の数値とともにまとめました。
⬡ 開発ツール2026-05-17
Rork Max の SwiftUI 機能を壁紙アプリ開発で検証した結果 — 実際に動いた機能と手を入れた機能
運用中の壁紙アプリの本番コードを基準に、Rork Max の SwiftUI ネイティブ生成を検証しました。そのまま使えた機能、手を入れた機能、生成を諦めた機能を、動くコードと実機での計測値とともにお伝えします。
⬡ 開発ツール2026-05-16
Rork Max 製 iOS アプリの Firebase CocoaPods を Swift Package Manager へ移行した実録——2026年10月の廃止前に対応した手順と3つの落とし穴
Firebase Apple SDK の CocoaPods 配信が2026年10月に停止します。Rork Max 製 iOS アプリ4本を Swift Package Manager へ移行した実体験をもとに、手順・落とし穴・Dropbox競合コピー問題の解決策を詳細に解説します。