TypeScript
Madhusudhan Kakurala • • 2 min read
Building Type-Safe APIs with TypeScript and Zod
Learn how to create robust, type-safe APIs using TypeScript and Zod for runtime validation that keeps your frontend and backend in sync.
Type safety is one of TypeScript’s greatest strengths, but it only helps at compile time. What about runtime? That’s where Zod comes in.
The Problem
Consider a typical API response:
interface User {
id: number;
name: string;
email: string;
}
const response = await fetch('/api/user/1');
const user: User = await response.json(); // Trust issues!
TypeScript trusts that the response matches our interface, but what if it doesn’t?
Enter Zod
Zod lets us define schemas that validate at runtime while inferring TypeScript types:
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
});
type User = z.infer<typeof UserSchema>;
Validating API Responses
Now we can safely parse responses:
async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/user/${id}`);
const data = await response.json();
return UserSchema.parse(data); // Throws if invalid
}
Safe Parsing
For graceful error handling:
const result = UserSchema.safeParse(data);
if (result.success) {
console.log(result.data); // Typed as User
} else {
console.error(result.error.issues);
}
Best Practices
- Define schemas once - Share between frontend and backend
- Use strict mode - Catch extra properties with
.strict() - Transform data - Use
.transform()for data manipulation - Provide defaults - Use
.default()for optional fields
Conclusion
Combining TypeScript with Zod gives you the best of both worlds: compile-time type checking and runtime validation. Your APIs become truly type-safe.