The Case for Typing Your API Responses
One of the most common sources of runtime bugs in TypeScript projects is untyped API responses. You call fetch, get back any, and TypeScript goes quiet — right when you need it most.
The fix is straightforward: use a schema validation library to parse and type your responses at the boundary.
Zod Makes This Easy
Zod lets you define a schema and parse unknown data against it. If the data doesn’t match, it throws a descriptive error. If it does, you get a fully-typed object.
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
role: z.enum(['admin', 'user', 'guest']),
createdAt: z.string().datetime(),
});
type User = z.infer<typeof UserSchema>;
async function getUser(id: number): Promise<User> {
const res = await fetch(`/api/users/${id}`);
const data = await res.json();
return UserSchema.parse(data); // throws if shape is wrong
}
Now getUser returns a proper User type and will surface API contract violations at runtime instead of silently propagating undefined through your UI.
Why Not Just Cast?
const data = await res.json() as User; // don't do this
This compiles fine but proves nothing. If the API returns null for email, TypeScript won’t notice — Zod will.
Alternatives
- Valibot — similar to Zod but tree-shakeable and tiny.
- Arktype — very fast, ergonomic syntax.
- TypeBox — generates JSON Schema, great if you need runtime schema sharing.
Pick any of them. Just pick one. Your future self debugging a Cannot read properties of undefined error at 11pm will be grateful.
About
Network Entropology (n.): The study of chaos in data network systems; the discipline concerned with understanding how order degrades, complexity accumulates, and entropy propagates across connected infrastructure, and the practice of bringing order back to it. A field that exists whether or not its practitioners know they are in it.