Impact: MEDIUM (prevents runtime crashes from unexpected API responses or user input)
TypeScript types disappear at runtime. When data crosses a system boundary — API responses, form submissions, URL parameters, webhook payloads, third-party SDK responses — the shape is not guaranteed. Runtime validation with a library like Zod catches malformed data before it causes cryptic errors deep in your application.
Incorrect (trusting external data with only TypeScript):
// ❌ TypeScript doesn't protect you at runtimeinterface User { id: string name: string email: string}export async function GET() { const res = await fetch('https://api.example.com/users/1') const user: User = await res.json() // ← This is a lie. It could be anything. return Response.json({ greeting: `Hello ${user.name}` }) // If API returns { id: 1, fullName: "..." } → user.name is undefined → "Hello undefined"}
// ❌ Trusting URL paramsexport default function Page({ searchParams }: { searchParams: { page: string } }) { const page = parseInt(searchParams.page) // Could be NaN, negative, or 999999 const data = await fetchItems({ skip: page * 20 })}
Correct (validate at the boundary):
// ✅ Validate API responsesimport { z } from 'zod'const UserSchema = z.object({ id: z.string(), name: z.string(), email: z.string().email(),})type User = z.infer<typeof UserSchema> // Derive the type from the schemaexport async function GET() { const res = await fetch('https://api.example.com/users/1') const json = await res.json() const user = UserSchema.parse(json) // Throws descriptive error if shape is wrong return Response.json({ greeting: `Hello ${user.name}` })}