Zodスキーマで完全なAPIレスポンスを検証する

メタデータ、ページネーション、ネストされたデータを含む完全なREST APIレスポンスを包括的なZodバリデーションスキーマに変換する方法を学びます。

Advanced

詳細な説明

Zodでの実際のAPIレスポンス検証

本番環境のAPIレスポンスはデータだけではありません。ステータスコード、ページネーション情報、エラーエンベロープ、深くネストされたオブジェクトを含みます。Zodスキーマはレスポンス構造全体をランタイムで検証します。

JSON例

{
  "status": "success",
  "data": {
    "users": [
      {
        "id": 1,
        "name": "Alice",
        "email": "alice@example.com",
        "profile": {
          "avatar": "https://cdn.example.com/alice.jpg",
          "bio": "Full-stack developer"
        }
      }
    ],
    "pagination": {
      "page": 1,
      "perPage": 20,
      "total": 150,
      "totalPages": 8
    }
  },
  "requestId": "req_abc123"
}

生成されるZodスキーマ

import { z } from "zod";

const profileSchema = z.object({
  avatar: z.string().url(),
  bio: z.string(),
});

const userSchema = z.object({
  id: z.number().int(),
  name: z.string(),
  email: z.string().email(),
  profile: profileSchema,
});

const paginationSchema = z.object({
  page: z.number().int().positive(),
  perPage: z.number().int().positive(),
  total: z.number().int().nonnegative(),
  totalPages: z.number().int().nonnegative(),
});

const apiResponseSchema = z.object({
  status: z.enum(["success", "error"]),
  data: z.object({
    users: z.array(userSchema),
    pagination: paginationSchema,
  }),
  requestId: z.string(),
});

type ApiResponse = z.infer<typeof apiResponseSchema>;

ジェネリック化

function createPaginatedSchema<T extends z.ZodTypeAny>(itemSchema: T) {
  return z.object({
    status: z.enum(["success", "error"]),
    data: z.object({
      items: z.array(itemSchema),
      pagination: paginationSchema,
    }),
    requestId: z.string(),
  });
}

const userListSchema = createPaginatedSchema(userSchema);
const productListSchema = createPaginatedSchema(productSchema);

エラーレスポンスの処理

const errorSchema = z.object({
  status: z.literal("error"),
  error: z.object({
    code: z.string(),
    message: z.string(),
  }),
  requestId: z.string(),
});

const responseSchema = z.union([apiResponseSchema, errorSchema]);

fetchとの統合

async function fetchUsers(): Promise<ApiResponse> {
  const res = await fetch("/api/users");
  const json = await res.json();
  return apiResponseSchema.parse(json);
  // レスポンス形状が無効な場合ZodErrorをスロー
}

このパターンは、不正なデータがアプリケーションに伝播する前に、APIコントラクト違反を即座にキャッチします。

ユースケース

REST APIのフロントエンドクライアントライブラリを構築し、UIエラーの原因となる前にバックエンドのコントラクト違反をキャッチするために、すべてのレスポンスのランタイムバリデーションが必要な場合に使用します。

試してみる — JSON to Zod Schema

フルツールを開く