Validate a Full API Response with Zod Schema

Learn how to convert a complete REST API response including metadata, pagination, and nested data into a comprehensive Zod validation schema.

Advanced

Detailed Explanation

Validating Real API Responses with Zod

Production API responses include more than just data. They contain status codes, pagination info, error envelopes, and deeply nested objects. Zod schemas validate the entire response structure at runtime.

Example 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"
}

Generated Zod Schema

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>;

Making It Generic

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);

Error Response Handling

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]);

Integrating with fetch

async function fetchUsers(): Promise<ApiResponse> {
  const res = await fetch("/api/users");
  const json = await res.json();
  return apiResponseSchema.parse(json);
  // Throws ZodError if response shape is invalid
}

This pattern catches API contract violations immediately, before malformed data propagates through your application.

Use Case

You are building a frontend client library for your REST API and want runtime validation of every response to catch backend contract violations before they cause UI errors.

Try It — JSON to Zod Schema

Open full tool