How to Use Zod with Next.js for Validation
Use
zod in Next.js by defining schemas to validate data, then parse inputs with schema.parse() or schema.safeParse(). This works well in API routes or form handling to ensure data correctness before processing.Syntax
Zod lets you create schemas to describe the shape and rules of your data. You define a schema with z.object() for objects, and use methods like string(), number(), and optional() to specify types and constraints. To validate data, use schema.parse(data) which throws on invalid data, or schema.safeParse(data) which returns a result object with success status.
typescript
import { z } from 'zod'; const schema = z.object({ name: z.string(), age: z.number().int().positive(), email: z.string().email().optional() }); // Validate data (throws error if invalid) schema.parse({ name: 'Alice', age: 30 }); // Or safely parse data const result = schema.safeParse({ name: 'Bob', age: 25 }); if (result.success) { // valid data in result.data } else { // errors in result.error }
Example
This example shows how to use Zod in a Next.js API route to validate incoming JSON data before processing it. It returns a 400 error if validation fails, or a success message if data is valid.
typescript
import { NextApiRequest, NextApiResponse } from 'next'; import { z } from 'zod'; const userSchema = z.object({ username: z.string().min(3), email: z.string().email(), age: z.number().int().positive().optional() }); export default function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'POST') { res.status(405).json({ error: 'Method not allowed' }); return; } const parseResult = userSchema.safeParse(req.body); if (!parseResult.success) { res.status(400).json({ error: parseResult.error.errors }); return; } // Data is valid here const userData = parseResult.data; res.status(200).json({ message: 'User data is valid', user: userData }); }
Output
POST /api/user with body {"username":"joe","email":"joe@example.com"}
Response: {"message":"User data is valid","user":{"username":"joe","email":"joe@example.com"}}
Common Pitfalls
- Not parsing the request body as JSON before validation can cause errors; Next.js API routes parse JSON automatically if
Content-Typeisapplication/json. - Using
schema.parse()without try-catch will crash the server on invalid data; prefersafeParse()for safer error handling. - For optional fields, forgetting to mark them as
optional()causes validation failures if missing. - Not validating query parameters or cookies if they are used as input can lead to unexpected bugs.
typescript
import { z } from 'zod'; const schema = z.object({ name: z.string(), age: z.number().int() }); // Wrong: parse throws error if invalid try { schema.parse({ name: 'Ann' }); // Missing age } catch (e) { console.error('Validation failed:', e.errors); } // Right: safeParse returns result object const result = schema.safeParse({ name: 'Ann' }); if (!result.success) { console.log('Errors:', result.error.errors); }
Quick Reference
Here is a quick summary of common Zod methods used in Next.js:
| Method | Description |
|---|---|
| z.string() | Validates a string value |
| z.number() | Validates a number value |
| z.boolean() | Validates a boolean value |
| z.object({...}) | Validates an object with specified shape |
| schema.parse(data) | Validates data or throws error if invalid |
| schema.safeParse(data) | Validates data and returns success status without throwing |
| z.optional(schema) | Marks a schema as optional |
| z.array(schema) | Validates an array of items matching schema |
| z.enum([...]) | Validates a value against a set of allowed strings |
Key Takeaways
Use Zod schemas to define and validate data shapes clearly in Next.js.
Prefer safeParse() over parse() to handle validation errors gracefully.
Validate all external inputs like API request bodies to avoid bugs.
Mark optional fields explicitly with optional() to prevent validation failures.
Integrate Zod easily in Next.js API routes for robust data validation.