Build This Now
Build This Now
クロード・コードとは何か?Claude Code のインストールClaude Code ネイティブインストーラーClaude Code で最初のプロジェクトを作る
Failed Payment RecoveryLocalization and i18nPDF InvoicesReferral ProgramTwo-Factor AuthNext.js DevTools MCPNext.js Agent SetupSubscription BillingMulti-Tenant SaaSAI Chat FeatureRate LimitingStripe WebhooksSemantic SearchRealtime UpdatesDrip Email Sequencesv2.1.122 Release NotesClaude Code の Dynamic Workflows:実際のコードベースで 1,000 個の subagents を動かす方法Claude Code ベストプラクティスClaude Opus 4.7 ベストプラクティスVPS上でのClaude CodeGit 統合Claude Code レビューClaude Code WorktreesClaude CodeリモートコントロールClaude Code ChannelsChannels、Routines、Teleport、DispatchClaude Code スケジュールタスクClaude Code権限管理Claude Code オートモードClaude Code で Stripe 決済を組み込むフィードバックループTodoワークフローClaude Code タスク管理プロジェクトテンプレートClaude Code の料金とトークン使用量Claude Code の料金:実際にいくら払うことになるのかClaude Code Ultra Review 完全ガイドClaude Code で Next.js アプリを作るSupabase DatabaseVercel DeepsecTest-Driven DevelopmentClaude Code で SaaS の MVP を作る方法Claude Code で認証を追加する(Supabase Auth)Claude Code でトランザクションメールを追加する(Resend + React Email)Claude Code で型安全な API を作る(oRPC + Zod)File UploadsBackground Jobs (Inngest)Admin DashboardFull-Text SearchClaude Agent SDKKiro Migration GuideClaude Research AgentLeave Grok BuildRoute Subagent ModelsClaude Monorepo SetupCI Repair AgentVisual Regression TestsAgent Cost DashboardCursor Migration Guideエージェント型コマース:AI エージェントが支払えるアプリの作り方1M ContextUser API KeysAudit LogsCSV Import PipelineDatabase MigrationsProduction Error TrackingFeature FlagsGitHub ActionsHeadless ModeMax Plan vs APICaching and RevalidationIn-App NotificationsOutbound WebhooksPrompt CachingRoles & PermissionsMarketplace PaymentsUsage-Based Billing2026年、Claude Code で SaaS を作るといくらかかるかParallel AI AgentsCoding Agent Injection
speedy_devvkoen_salo
Blog/Handbook/Workflow/Type-Safe API

Claude Code で型安全な API を作る(oRPC + Zod)

oRPC と Zod を使って、Next.js 16 で完全に型安全な API 層を Claude Code で作る方法。スキーマ変更が本番ではなくビルドを壊すようにする。

設定をやめて、構築を始めよう。

AIオーケストレーション付きSaaSビルダーテンプレート。

企業向けに構築している実績を見る →
speedy_devvkoen_salo
speedy_devvWritten by speedy_devvPublished Jul 14, 202610 min readHandbook hubWorkflow index

本当に効いてくる型安全な API の姿は、図ではなくこれです。Zod スキーマのフィールド名を変えると、それを読むクライアントコンポーネントが、ファイルを保存する前にエディタで赤くなる。更新すべき Postman コレクションはありません。3 日後に本番でランタイム 500 が出ることもありません。エラーは、あなたが何かを壊したまさにその瞬間に、直すべきまさにそのファイルに現れます。

それが、Next.js 16 アプリで oRPC と Zod が手に入れさせてくれるものです。そして Claude Code は、それを最初のパスで正しく繋ぐ速い方法です。

このセットアップが防ぐバグ

典型的な Next.js の API ルートでよくある失敗はこうです。{ id, title, done } を返すルートハンドラを定義する。どこか別の場所で、コンポーネントが task.completed を分割代入している。6 週間前に誰かがサーバー側でフィールド名を変えたのに、クライアントを誰も更新しなかったからです。TypeScript はこれを捕まえられません。fetch() が any(あるいは自分で手書きして今は盲目的に信じている型)を返すからです。バグは出荷されます。それに気づくのは、コンパイラではなく Sentry のアラートからです。

「データベースからフロントエンドまで型安全」とは、データの形について信頼できる情報源がちょうど 1 つ(Zod スキーマ)あり、サーバーハンドラもクライアント呼び出しも、その同じスキーマから型付けされる、ということです。スキーマを変えれば、その形が流れるすべての場所が、直すまで型エラーで光ります。

oRPC と Zod をインストールする

oRPC は小さなパッケージいくつかとして出荷されます。手続きを定義してハンドラとしてマウントするサーバーパッケージと、完全な推論つきでそれを呼ぶクライアントパッケージです。両方を Zod と一緒にインストールします。

npm install @orpc/server @orpc/client zod

他には何も要りません。コード生成のステップも、スキーマ変更のたびに走らせる CLI もありません。型は、再生成を忘れずにいなければならないビルド成果物からではなく、TypeScript が Zod スキーマを通じて推論することから来ます。

スキーマを定義する

まず、API が返すものの形から始めます。これは小さなタスクリストの API で、タスク用の Zod オブジェクトスキーマが 1 つと、「作成」リクエストが受け付けるものの派生スキーマ(id と done はサーバー側で設定されるので、タイトルだけ)です。

// lib/schemas/task.ts
import * as z from "zod";

export const taskSchema = z.object({
  id: z.string(),
  title: z.string().min(1).max(200),
  done: z.boolean(),
});

export const createTaskInput = taskSchema.pick({ title: true });

export type Task = z.infer<typeof taskSchema>;

ここで本当の仕事をしているのは z.infer です。Task は手で保守する型ではなく、スキーマから導出されるので、ランタイムのバリデータとずれることが決してありません。taskSchema にフィールドを追加すれば、Task はそれを自動的に拾います。

チュートリアルでは「データベース」はメモリ上の配列です。本番アプリでは、これを本物の Supabase や Postgres のクエリに差し替えてください。残りのセットアップは変わりません。

// lib/db/tasks.ts
import { randomUUID } from "crypto";
import type { Task } from "@/lib/schemas/task";

const tasks: Task[] = [
  { id: randomUUID(), title: "Wire up the API layer", done: false },
];

export async function listTasks(): Promise<Task[]> {
  return tasks;
}

export async function createTask(title: string): Promise<Task> {
  const task: Task = { id: randomUUID(), title, done: false };
  tasks.push(task);
  return task;
}

oRPC の手続きを定義する

oRPC の手続きは、@orpc/server のチェイン可能なビルダー os で作ります。入力スキーマ、出力スキーマ、ハンドラ関数を付けます。どちらのスキーマ呼び出しも任意ですが、両方付けることで、入り口でバリデーションが効き、出口で型付けされ検査された形が得られます。

// lib/orpc/router.ts
import * as z from "zod";
import { os } from "@orpc/server";
import { taskSchema, createTaskInput } from "@/lib/schemas/task";
import { listTasks, createTask } from "@/lib/db/tasks";

const list = os.output(z.array(taskSchema)).handler(async () => {
  return listTasks();
});

const create = os
  .input(createTaskInput)
  .output(taskSchema)
  .handler(async ({ input }) => {
    return createTask(input.title);
  });

export const router = {
  tasks: {
    list,
    create,
  },
};

export type AppRouter = typeof router;

注目すべき点が 2 つ。1 つ目、create ハンドラの中の input はすでに { title: string } として型付けされています。キャストも手書きのインターフェースもありません。2 つ目、createTask が taskSchema に合わないものを返したら、TypeScript はコードが走る前に、そのハンドラの中でその場で指摘します。出力スキーマは、クライアントだけでなく、自分のサーバーコードに対する契約でもあるのです。

ルーターを Route Handler としてマウントする

oRPC は、素の fetch の Request/Response を話す RPC ハンドラを公開していて、これは Next.js 16 の Route Handler に直接マッピングされます。すべての手続きが 1 つのプレフィックスの下で届くよう、キャッチオールのセグメントにマウントします。

// app/rpc/[[...rest]]/route.ts
import { RPCHandler } from "@orpc/server/fetch";
import { router } from "@/lib/orpc/router";

const handler = new RPCHandler(router);

async function handleRequest(request: Request) {
  const { response } = await handler.handle(request, {
    prefix: "/rpc",
    context: {},
  });

  return response ?? new Response("Not found", { status: 404 });
}

export const GET = handleRequest;
export const POST = handleRequest;
export const PUT = handleRequest;
export const PATCH = handleRequest;
export const DELETE = handleRequest;

すべての HTTP 動詞が同じ handleRequest 関数を指しているのは、RPC ハンドラがどの手続きを呼ぶかをリクエスト自身から判断するからです。エンドポイントごとに別ファイルを書くことも、URL を関数に手でマッピングすることもありません。lib/orpc/router.ts の router に新しい手続きを追加することが、差分のすべてです。

Server Component から呼ぶ

Server Component の中では、自分のサーバーを呼ぶのに HTTP を往復する理由はありません。oRPC のサーバーサイドクライアント(createRouterClient)は、手続きのハンドラ関数をプロセス内で直接呼びつつ、ネットワーククライアントとまったく同じ型付けされたインターフェースを保ちます。

// lib/orpc/server-client.ts
import "server-only";
import { createRouterClient } from "@orpc/server";
import { router } from "@/lib/orpc/router";

export const orpc = createRouterClient(router, {
  context: {},
});

server-only のインポートはガードレールで、このファイルが誤ってクライアントコードに取り込まれると、ビルドエラーを投げます。ページで使います。

// app/tasks/page.tsx
import { orpc } from "@/lib/orpc/server-client";

export default async function TasksPage() {
  const tasks = await orpc.tasks.list();

  return (
    <main className="max-w-lg mx-auto py-12">
      <h1 className="text-2xl font-bold mb-6">Tasks</h1>
      <ul className="space-y-2">
        {tasks.map((task) => (
          <li key={task.id}>{task.title}</li>
        ))}
      </ul>
    </main>
  );
}

tasks は Task[] で、taskSchema からずっと推論されています。TasksPage に手書きの戻り値型はなく、型のない fetch 呼び出しから any が忍び込むこともありません。

Client Component から呼ぶ

ブラウザはサーバー関数をプロセス内で呼べないので、クライアントサイドのクライアントは、先ほどマウントしたルートを指す RPC リンクを使って、ネットワーク越しに動きます。インターフェースはサーバーサイドのクライアントと同一です。同じメソッド名、同じ引数の形、同じ推論された戻り値型です。

// lib/orpc/client.ts
import { createORPCClient } from "@orpc/client";
import { RPCLink } from "@orpc/client/fetch";
import type { RouterClient } from "@orpc/server";
import type { AppRouter } from "@/lib/orpc/router";

const link = new RPCLink({
  url: "/rpc",
});

export const orpc: RouterClient<AppRouter> = createORPCClient(link);

フォームで使います。title は useState から来る素の文字列で、orpc.tasks.create(...) の戻り値は、.json() してキャストしなければならない Response ではなく、完全に型付けされた Task です。

// components/new-task-form.tsx
"use client";

import { useState } from "react";
import { orpc } from "@/lib/orpc/client";

export function NewTaskForm() {
  const [title, setTitle] = useState("");
  const [pending, setPending] = useState(false);

  async function handleSubmit(event: React.FormEvent) {
    event.preventDefault();
    setPending(true);
    const task = await orpc.tasks.create({ title });
    setPending(false);
    setTitle("");
    console.log("created task", task.id, task.done);
  }

  return (
    <form onSubmit={handleSubmit} className="flex gap-2">
      <input
        value={title}
        onChange={(event) => setTitle(event.target.value)}
        className="border rounded px-3 py-2 flex-1"
        placeholder="New task"
      />
      <button
        type="submit"
        disabled={pending}
        className="bg-black text-white rounded px-4 py-2"
      >
        Add
      </button>
    </form>
  );
}

もし orpc.tasks.create に { title, done: false } を渡そうとすると、TypeScript は何かを走らせる前にそれを拒否します。createTaskInput は title しか受け付けないからです。これは入力スキーマが、サーバー側だけでなくクライアント側でも仕事をしている、ということです。

コンパイラが破壊的変更を捕まえるのを見る

これは実際に試してみる価値のある部分です。lib/schemas/task.ts を開いて、done を completed にリネームします。

export const taskSchema = z.object({
  id: z.string(),
  title: z.string().min(1).max(200),
  completed: z.boolean(),
});

ファイルを保存します。NewTaskForm は今や task.done でコンパイルエラーを表示します。Property 'done' does not exist on type '{ id: string; title: string; completed: boolean }'。lib/db/tasks.ts もエラーになります。createTask がいまだに done キーを持つオブジェクトを組み立てているからです。そのすべてが直るまで、何も走りません。

これが売り文句のすべてです。スキーマは 1 つのファイルで一度だけ変わり、TypeScript は古い形を前提としていた下流のあらゆる場所を見つけました。コードベース全体を .done で grep する必要も、本物のユーザーが古いコードパスに当たって数日後にランタイムエラーが出ることもありません。

Claude Code でこれを繋ぐ

新しい手続きを追加するよう Claude Code に頼めば、CLAUDE.md に慣習をはっきり書いておく限り、パターンに従います。

## API Layer

- oRPC procedures live in lib/orpc/router.ts
- Every procedure gets both .input() and .output() with a Zod schema
- Zod schemas for API-facing shapes live in lib/schemas/
- Route handler is mounted at app/rpc/[[...rest]]/route.ts, don't create new route files per endpoint

これを置いておけば、「タスクを完了にする手続きを追加して」は、独自の手書きの型を持つその場しのぎの新しい API ルートではなく、同じ router オブジェクトへの新しいエントリ、必要ならスキーマの更新、そして既存のスタイルに合ったハンドラを生み出します。

$29 の Code Kit は、まさにこのパターンの上に作られています。これは Claude Code の上に載る harness であって、別のフレームワークではありません。生成される API 層はデフォルトで oRPC と Zod で、データベースのクエリから、データを描画するコンポーネントまで型安全です。Claude Code 自体を動かすには、やはり有料の Anthropic プランが必要です。harness が追加するのは、慣習をすでに繋いだ状態です。だから、それが作るすべての新機能が、毎回頼まなくてもこの同じ保証を得られます。

Posted by @speedy_devv

Continue in Workflow

  • エージェント型コマース:AI エージェントが支払えるアプリの作り方
    2026年のエージェント型コマースをわかりやすく解説するガイド。x402、ACP、Machine Payments Protocol が何をするのか、そして AI エージェントが購入できる有料 API を週末で出荷するための手順を紹介します。
  • Claude Code ベストプラクティス
    Claude Codeで成果を出すエンジニアを分ける5つの習慣: PRD、モジュラーなCLAUDE.mdのルール、カスタムスラッシュコマンド、/clearリセット、そしてシステム進化の思考法。
  • Claude Code オートモード
    2つ目の Sonnet モデルが、Claude Code のすべてのツール呼び出しを実行前に審査します。オートモードがブロックするもの・許可するもの、そして settings.json に追加される許可ルールについて解説します。
  • Channels、Routines、Teleport、Dispatch
    Anthropic が2026年3月と4月に出荷した4つの Claude Code 機能。これらは CLI を、スマホ・ウェブ・デスクトップをまたぐイベント駆動の調整レイヤーに変えます。
  • Claude Code 1M Context in Practice: When Bigger Isn't Better
    The 1M-token context window is GA at flat pricing, but bigger isn't always better. A decision framework, token-cost math, and when to use /compact, subagents, and dynamic workflows instead.
  • How to Build an Admin Dashboard With Claude Code
    Ship an internal admin panel with Claude Code: role-gated routes, a searchable users and orders table with pagination, impersonation-safe RLS, and metrics tiles pulled through a type-safe API.

More from Handbook

  • Best SaaS Boilerplate 2026: The Honest Comparison
    An honest 2026 roundup of the best SaaS boilerplates and starter kits (ShipFast, Makerkit, Supastarter, SaaS Pegasus, Divjoy, open source), with real pricing, stacks, and the trade-off nobody mentions: a boilerplate still leaves you coding.
  • Claude Code 変更履歴
    v0.2ベータから2026年3月までのClaude Codeのリリースノート。ベアモード、チャンネル権限リレー、OAuthの修正、すべての破壊的変更を網羅。
  • What It Really Costs to Build a SaaS MVP in 2026
    A buyer's cost breakdown for building a SaaS MVP in 2026. Real freelancer, agency, no-code, AI-tool, and DIY numbers with citations, plus where the money actually goes.
  • コードを生み出すプロンプトテンプレート
    コードを出荷する10のプロンプトレシピ: フルスタックスキャフォールディング、API、スキーマ、テスト、リファクタリング、デバッグ、レビュー、CI。それぞれの失敗パターンも紹介。

設定をやめて、構築を始めよう。

AIオーケストレーション付きSaaSビルダーテンプレート。

企業向けに構築している実績を見る →
speedy_devvkoen_salo

Claude Code でトランザクションメールを追加する(Resend + React Email)

Resend と React Email を使って、Next.js 16 アプリでウェルカム・レシート・パスワードリセットのメールを作る方法。テンプレートと送信ロジックは Claude Code が書きます。

File Uploads

Build secure user file and image uploads end to end with Claude Code: private Supabase Storage buckets, per-user RLS, signed URLs, and an upload UI wired through a type-safe oRPC API.

On this page

このセットアップが防ぐバグ
oRPC と Zod をインストールする
スキーマを定義する
oRPC の手続きを定義する
ルーターを Route Handler としてマウントする
Server Component から呼ぶ
Client Component から呼ぶ
コンパイラが破壊的変更を捕まえるのを見る
Claude Code でこれを繋ぐ

設定をやめて、構築を始めよう。

AIオーケストレーション付きSaaSビルダーテンプレート。

企業向けに構築している実績を見る →