Building Type-Safe APIs with TypeScript
Type safety across the stack cuts whole classes of bugs and makes refactors calm. This is the short, practical version.
The Problem
Classic REST APIs break when the server response shape changes but the client code does not.
The Minimal Fix
Share types and validate at the boundary.
// types/user.ts
export interface User {
id: number;
email: string;
name: string;
role: "admin" | "user";
}
export interface CreateUserRequest {
email: string;
name: string;
password: string;
}// schema/user.ts
import { z } from "zod";
export const userSchema = z.object({
id: z.number(),
email: z.string().email(),
name: z.string().min(1),
role: z.enum(["admin", "user"]),
});
export const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
password: z.string().min(8),
});
export type User = z.infer<typeof userSchema>;// routes/users.ts
import { Request, Response } from "express";
import {
userSchema,
createUserSchema,
User,
CreateUserRequest,
} from "../types/user";
export async function getUser(
req: Request<{ id: string }>,
res: Response<User>,
) {
const userId = Number(req.params.id);
const user = await db.users.findById(userId);
if (!user) return res.status(404).json({ error: "User not found" });
res.json(userSchema.parse(user));
}
export async function createUser(
req: Request<{}, {}, CreateUserRequest>,
res: Response<User>,
) {
const body = createUserSchema.parse(req.body);
const user = await db.users.create(body);
res.json(user);
}If You Want the Full Experience
tRPC removes the client/server type gap entirely. Prisma gives you typed database queries. Combine them and you get end-to-end type safety with very little manual glue.
Simple Rules
- Share types between client and server
- Validate inputs and outputs at the boundary
- Prefer generated types when possible
Conclusion
You do not need a huge framework to get type-safe APIs. Start by sharing types and validating at the edge. That alone prevents most of the painful runtime surprises.
Related Posts
The Art of Minimal Design
Exploring the principles of minimal design, how to achieve clean interfaces, and the balance between functionality and aesthetics in modern web applications.
Inside the Cloudflare Outage (Nov 18, 2025)
A clear breakdown of what went wrong on November 18, 2025 and the distributed systems lessons that followed.
Understanding React Server Components
A comprehensive guide to React Server Components, their benefits, and how they change the way we think about building React applications.