How to Validate JSON at Runtime in TypeScript
Table of Contents
TypeScript types are erased at runtime — your type annotations do not validate anything when your app is running. To validate JSON at runtime in TypeScript, you need a schema validation library like Zod that checks the actual data structure and types at execution time.
This guide shows the problem, explains why TypeScript types alone are not enough, and walks through runtime validation with Zod step by step.
Why TypeScript Types Are Not Enough at Runtime
Consider this TypeScript code:
interface User {
id: number;
name: string;
}
async function fetchUser(id: number): Promise<User> {
const res = await fetch("/api/users/" + id);
return res.json() as User; // type assertion, no actual check
}
The as User cast tells TypeScript to trust you — it does NOT check the data. If the API returns { id: "abc", name: null }, TypeScript compiles fine. Your app crashes at runtime when you try to use user.id as a number.
This gap between compile-time types and runtime data is one of the most common sources of TypeScript bugs in production.
The Zod Solution: Schema That Validates at Runtime
Zod schemas check the actual data at runtime, not just the types at compile time:
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string()
});
type User = z.infer<typeof UserSchema>;
async function fetchUser(id: number): Promise<User> {
const res = await fetch("/api/users/" + id);
const data = await res.json();
return UserSchema.parse(data); // throws if invalid
}
If the API returns wrong data, parse() throws with a detailed error. Your app never silently processes corrupt data.
Using safeParse to Handle Errors Gracefully
Use safeParse when you want to handle errors without try/catch:
const result = UserSchema.safeParse(data);
if (!result.success) {
// result.error contains detailed issues
result.error.issues.forEach(issue => {
console.error(issue.path, issue.message);
});
return null;
}
const user = result.data; // typed as User
safeParse returns { success: true, data: T } or { success: false, error: ZodError }. No exceptions. This pattern is clean for API handlers and service functions.
Validating API Responses in Practice
A complete example with a list endpoint:
const PostSchema = z.object({
id: z.number(),
title: z.string(),
body: z.string(),
userId: z.number()
});
const PostListSchema = z.array(PostSchema);
async function fetchPosts() {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
const data = await res.json();
const result = PostListSchema.safeParse(data);
if (!result.success) {
throw new Error("API returned unexpected data: " + result.error.message);
}
return result.data; // Post[]
}
If you need to generate the schema from a real API response, paste the JSON into the free JSON to Zod converter and get a ready-made schema in seconds.
Common Zod Methods for JSON Validation
Beyond basic type matching, Zod provides format validators:
z.string().email()— must be a valid email formatz.string().url()— must be a valid URLz.string().uuid()— must be a UUIDz.number().positive()— must be greater than 0z.number().int()— must be an integerz.string().optional()— field can be absentz.string().nullable()— field can be nullz.string().nullish()— field can be null or absent
These are the constraints you add after generating a base schema. Generate the structure, then layer on the business rules.
Try It Free — No Signup Required
Runs 100% in your browser. No data is collected, stored, or sent anywhere.
Open Free JSON to Zod ConverterFrequently Asked Questions
Do TypeScript types validate data at runtime?
No. TypeScript types are erased at compile time. They exist only during development for IDE support and type checking. At runtime, JavaScript runs and types are gone — there is no built-in runtime validation.
What is the best way to validate JSON in TypeScript?
Zod is the most popular TypeScript-first option. You define a schema with z.object() and call schema.parse() or schema.safeParse() on your data. Failed validation throws an error with detailed field-level messages.
What is the difference between parse and safeParse in Zod?
parse() throws a ZodError if validation fails. safeParse() returns a result object { success, data } or { success: false, error } without throwing. Use safeParse for cleaner error handling without try/catch.
Can I generate a Zod schema from a JSON sample?
Yes. Paste your JSON into the free JSON to Zod converter at /developer-tools/json-to-zod/. It generates the full z.object() schema automatically from your data.

