Skip to main content
Admin Panel
SC
Draft
1 min read
190 words
Save
Publish
## The Type Safety Gap Your frontend is fully typed with TypeScript. Your database has typed schemas. But your API layer? Often a loosely-typed mess. Let's fix that with a pattern that gives you full type safety from request to response. ## Zod for Request Validation ```typescript import { z } from "zod" const CreatePostSchema = z.object({ title: z.string().min(1).max(200), content: z.string().min(1), tags: z.array(z.string()).max(10), status: z.enum(["draft", "published"]), }) type CreatePostInput = z.infer<typeof CreatePostSchema> ``` > **Tip:** Use `z.infer` to derive TypeScript types from your Zod schemas — single source of truth for both runtime validation and compile-time types. ## The API Route ```typescript export async function POST(request: Request) { const body = await request.json() const result = CreatePostSchema.safeParse(body) if (!result.success) { return Response.json( { error: result.error.flatten() }, { status: 400 } ) } const post = await createPost(result.data) return Response.json(post, { status: 201 }) } ``` ## Type-Safe Client ```typescript async function createPost(input: CreatePostInput): Promise<Post> { const response = await fetch("/api/posts", { method: "POST", body: JSON.stringify(input), }) if (!response.ok) throw new APIError(response) return response.json() } ``` This gives you compile-time errors if you pass the wrong shape to the API client.