メインコンテンツへスキップ
見出し画像

言った言わないを防ぐ。 フロントと API の型契約

    〜 Client / Server 両側から考える型設計 〜

    フロントと API の間で、言った言わないが起きたことはありませんか。

    「このフィールド、返すって言ったよね」
    「いや、そんな話してない」

    ——人間同士なら議事録で解決できる。
    フロントと API のあいだでは、型が契約になるのです。

    フロントと API の言った言わないとは

    僕が個人開発でフロントと API を両方触っているとき、何度か言った言わないに近いことが起きました。

    📍 API のレスポンスに createdAt を追加した。
     フロントは created_at を期待していた。

    📍 フロントでこの型で受け取ると決めていたのに、API 側でフィールド名を変えてしまった。

    📍 仕様は口頭やチャットで共有していたが、型として残っていなかった。

    そのたびに、フロントでランタイムエラーや表示崩れが起き、原因を追う時間が奪われました。

    言った言わないを防ぐには、型を契約として明文化するしかない。

    そう気づいてから、Client / Server 両側から型を揃える設計に切り替えました。

    型を契約として明文化するとは

    型を契約として明文化する——
    それは、フロントと API のあいだで何を渡し、何を受け取るかを、型定義として共有するということです。

    📌 リクエストの型
    📌 レスポンスの型
    📌 エラーの型

    これらを、どちらか一方だけが持つのではなく、両側で参照できる形で置く。

    そうすると、API が返す形とフロントが期待する形が食い違った瞬間に、型エラーで検知できる。

    言った言わないが、コンパイル時に消えるのです。

    Client / Server 両側から考える型設計

    型を契約として明文化するには、Client と Server の両側から型を設計する必要があります。

    片側だけの型定義では足りない

    フロントだけが型を定義していると、API が変わったときに気づけない。
    API だけが型を定義していると、フロントが何を期待しているかが伝わらない。

    両側が同じ型定義を参照するか、一つのスキーマから両側の型を生成するか。

    どちらかの設計にしないと、契約は成立しません。

    契約の置き場所

    共有パッケージ:
    型定義を packages/types のような共有パッケージに置き、フロントも API もそこを参照する。

    API スキーマから生成:
    OpenAPI や Zod スキーマを正とし、そこから TypeScript の型を生成する。
    フロントはその型を import する。

    単一リポジトリ内の共有ファイル:
    モノレポなら、shared/types に型を置き、両側から import する。

    どの方式を選ぶかは、プロジェクトの規模や構成によります。
    大切なのは、型定義が一箇所にあり、両側がそれを参照していることです。

    型契約を破ったとき、 コンパイルが教えてくれる

    型を契約として明文化しておくと、契約を破った瞬間にコンパイルエラーが出ます。

    📌 API のレスポンス型からフィールドを削除した
    👉️ フロントでそのフィールドを参照している箇所が型エラーになる。

    📌 フロントが新しいフィールドを期待するように型を更新した
    👉️ API がまだ返していなければ、フロントの型と API の実装の不一致が、型の更新作業で明らかになる。

    言った言わないは、ランタイムで発覚するのではなく、ビルド時に検知できるようになります。

    【型契約の具体的な設計】 3 つのパターン

    僕が実践している、型契約の設計パターンを 3 つ紹介します。

    1️⃣ 共有型ファイルを両側から import する (最小構成)

    モノレポや同一リポジトリ内で、shared/types/api.ts のようなファイルに型を定義する。

    // shared/types/api.ts
    export type UserResponse = {
      id: string;
      name: string;
      email: string;
      createdAt: string; // ISO 8601
    };

    フロントと API の両方が、このファイルを import して使う。
    API はこの型に沿ってレスポンスを返し、フロントはこの型で受け取る。

    メリット:
    シンプルで、小規模なプロジェクトにすぐ導入できる。

    デメリット:
    ランタイムバリデーションは別途必要(Zod など)。

    2️⃣ Zod スキーマを正にして型を推論する

    Zod でスキーマを定義し、z.infer で型を導出する。

    // shared/schemas/user.ts
    import { z } from 'zod';
    
    export const UserResponseSchema = z.object({
      id: z.string(),
      name: z.string(),
      email: z.string(),
      createdAt: z.string(),
    });
    
    export type UserResponse = z.infer<typeof UserResponseSchema>;

    API 側では、レスポンスを返す前に UserResponseSchema.parse(data) でバリデーションする。
    フロント側では、UserResponse 型で受け取る。

    メリット:
    ランタイムバリデーションと型が一つのスキーマから得られる。
    不正なデータがフロントに届くのを防げる。

    デメリット:
    Zod の導入が必要。

    3️⃣ OpenAPI から型を生成する

    OpenAPI 仕様を書いておき、そこから TypeScript の型を自動生成する。
    openapi-typescript などのツールを使う。

    メリット:
    API の仕様がドキュメント化され、フロント・API・他サービスが同じ仕様を参照できる。

    デメリット:
    OpenAPI の記述とメンテナンスのコストがかかる。
    小規模だと過剰になることがある。

    型契約を守るためのルール

    型契約を設計したあと、守り続けるためのルールを決めておくとよいです。

    📌 型の変更は、両側の影響を確認してから行う
    片側だけ変えると、もう片側で型エラーが出る。
    意図的に壊すなら、両側を同時に直す。

    📌 とりあえず any を禁止する
    型が曖昧になると、契約が崩れる。
    any で逃げず、正しい型を定義する。

    📌 リクエスト・レスポンスの型は、必ず共有定義から参照する
    フロントと API で別々に型を書かない。
    一箇所で定義し、両側が参照する。

    この 3 つを守るだけでも、言った言わないはかなり減ります。

    型は、 フロントと API の議事録

    人間同士の言った言わないには、議事録がある。

    フロントと API の言った言わないには、型がある。

    型を契約として明文化し、Client / Server 両側から揃える。

    それだけで、仕様の食い違いで泣く時間が減ります。

    あわせて読みたい

    ▼ 境界や責務を先に決める設計を知りたいときに

    フロントとサーバーの境界を型やバリデーションで守る設計。
    型契約の考え方と通じます。

    ▼ スキーマや型を後回しにしない設計を知りたいときに

    型やスキーマを最初に決めておく重要性。
    API の型契約も同じ思想です。

    ▼ フォームやバリデーションを型で守りたいときに

    Zod でスキーマを定義し、型とランタイムバリデーションを揃える実例です。

    ▼ 設定や環境変数を型で守りたいときに

    Zod でスキーマを定義し、型とバリデーションを一体化する考え方の参考に。

    ▼ 認可や境界を最初に決める設計を知りたいときに

    最初に決める設計思想。
    型契約も同じく、最初に決めておくことで手戻りを防ぎます。

    ▼ API やバックエンドの層設計を知りたいときに

    API の責務や層を整理する設計。
    型契約は、その境界を守る役割も果たします。

    ひとりごと

    フロントと API の言った言わないで、何度か深夜デバッグをした経験があります。

    型を契約として明文化してから、その手戻りは激減しました。

    型は、フロントと API の議事録。
    書いておけば、食い違いはコンパイルが教えてくれる。

    あなたのプロジェクトでも、型契約を試してみませんか。

    画像
    2026© おおとろ
     
     
    15年のフリーランス / ソフトウェアエンジニア経験を基に、仕事が劇的にラクになる思考法を発信し、あなたの生産性と心の余裕を生み出します。吃音で選んだ物語の力で、人生の深い葛藤を乗り越えるヒントと癒しを提供。フォローで専門知識と心の拠り所となる物語が手に入ります。

    あなたへのおすすめ