Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

A TypeScript type does not validate JSON received over the network. Treat the response as unknown, check HTTP and JSON parsing separately, then validate the fields your application depends on. A focused hand-written parser can make sense for a small contract; Zod is a documented option when schemas, richer validation, or shared runtime boundaries justify it. The right choice depends on the contract and the maintenance needs—not an assumed speed advantage.

How do I validate an API response in TypeScript?

Validate it at the boundary where untrusted data enters your application. A function that accepts unknown should check the required structure and field types, then return a value with the type downstream code can safely use. TypeScript annotations describe what the compiler should expect; they do not inspect a response at runtime.

The TypeScript Handbook explains that unknown communicates that a value could be anything, and that code must narrow it before treating it as a more specific type: TypeScript Handbook: Basic Types.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep the three failure boundaries distinct

  1. HTTP status: fetch() does not reject just because a server returns an error status such as 404. Check response.ok before treating the response as successful. MDN documents this behavior in Using the Fetch API.
  2. JSON decoding: response.json() reads and parses the body. Invalid JSON can fail here.
  3. Application shape: Successfully decoded JSON may still be a string, array, null, or object with missing or wrongly typed fields. Validate the shape your code consumes.

Keeping these boundaries separate makes failures easier to diagnose: a non-OK status is not a JSON error, and valid JSON is not necessarily valid application data.

A focused boundary parser

The following is a generic pattern, not a claim about any particular API or implementation. Replace the illustrative fields and endpoint context with the contract your application actually uses.

type Account = {
  id: string;
  active: boolean;
};

function parseAccount(input: unknown): Account {
  if (typeof input !== "object" || input === null || Array.isArray(input)) {
    throw new Error("GET /account: expected an object");
  }

  const value = input as Record<string, unknown>;

  if (typeof value.id !== "string") {
    throw new Error("GET /account: expected field 'id' to be a string");
  }

  if (typeof value.active !== "boolean") {
    throw new Error("GET /account: expected field 'active' to be a boolean");
  }

  return { id: value.id, active: value.active };
}

async function getAccount(): Promise<Account> {
  const response = await fetch("/account");

  if (!response.ok) {
    throw new Error(`GET /account: HTTP ${response.status}`);
  }

  const body: unknown = await response.json();
  return parseAccount(body);
}

Returning a newly constructed value makes the parser’s output explicit: only the checked fields enter the typed result. Errors identify the endpoint and expected field without dumping a full response, which may contain sensitive data. If the real contract includes nested objects, arrays, enums, or nullable fields, validate those deliberately too; checking only the top-level object is not enough.

Can I trust a TypeScript type for JSON returned by an API?

Not by itself. A declaration such as const account: Account = await response.json() does not prove that the server sent an account-shaped object. It only gives the compiler a type to assume. If the service changes its response or returns an unexpected error payload, code may still compile while making incorrect runtime assumptions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use unknown at the boundary, then narrow it through checks or a runtime schema. Keep the type tied to the validated result so callers receive values that passed the checks, rather than applying a type assertion to unchecked data.

When should I use Zod instead of writing a parser?

Zod’s project describes it as “a TypeScript-first validation library” and documents schema-based parsing with typed results. Its introduction also lists zero external dependencies, support for Node.js and modern browsers, and a 2 KB gzipped core bundle; that size is a Zod-published claim, not an independent measurement or a comparison with a hand-written parser. See Zod’s introduction.

A small parser may be a reasonable choice when the contract is narrow and direct field checks and contextual errors fit the surrounding code. That is an implementation judgment, not evidence that handwritten code is faster, smaller, safer, or easier in a given project. There is no basis here for a project-specific performance claim.

Decision factor A focused hand-written parser Zod
Contract complexity Can suit a few straightforward required fields; nested variants and refinements require more custom checks. Provides schema APIs; consider it as requirements grow. Zod’s capabilities are documented at zod.dev.
Error context You define the messages, including endpoint or resource context. Schema parsing supplies validation results; shape and presentation should be assessed against the needed error context.
Type relationship Return a typed value only after checks; keep the declared output aligned with those checks. Schema parsing provides typed results, as described in the Zod introduction.
Maintenance Checks and output types need updating as the contract changes. A schema can centralize runtime validation rules; decide whether that structure helps the project.
Dependency and reuse Avoids adding a validation-library dependency, but that may mean maintaining custom validation logic. Zod documents zero external dependencies and Node.js and modern-browser support; whether to add it depends on project policy and reuse needs. Zod introduction.
Unknown keys You choose whether to ignore, preserve, or reject extra properties. z.object() strips unknown keys by default; z.strictObject() rejects them. See Zod’s documentation.

Required fields are required in Zod unless made optional. Its default object behavior and strict-object option make unknown-key policy an explicit design consideration, not a reason to reject every extra field automatically. Additive fields may be compatible with an API that evolves independently; reject extras only when the contract requires that strictness. Zod documents these object behaviors at zod.dev.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How do I make an API parser fail loudly when the response changes?

Fail loudly by making invalid required data produce an error that tells a maintainer what failed and where. Name the resource or endpoint and the expected field or type. Avoid a generic “invalid response” when a precise check can identify the broken assumption.

  • Check the minimum enclosing structure before reading properties.
  • Validate every required field the application uses, including nested values that downstream code assumes exist.
  • Keep malformed required data distinct from additive unknown keys; choose an unknown-key policy intentionally.
  • Do not include secrets or dump the entire response in an error message.
  • Let HTTP status failures, JSON decoding failures, and shape failures remain distinguishable.

“Fail loudly” does not have to mean “reject every change.” A parser that validates required fields while ignoring unrelated additions can catch breaking changes without making harmless extra fields fatal. Use strict rejection only where unexpected keys themselves violate the contract.

How to choose for an API you do not control

Start with the contract your application consumes and the consequences of a mismatch. For a few stable, simple fields, a narrow parser can make the boundary visible without introducing a schema library. For nested unions, refinements, transformations, several API boundaries, or a need to reuse schemas, evaluate Zod or another schema approach. In either case, the important property is runtime validation before untrusted data reaches typed application logic—not the name of the tool.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.