javascript 45 lines · 9 steps

Validating query params with Zod in Express

An Express route uses a Zod schema to coerce, validate, and default query parameters before building a safe database query.

Explained by highlit
1const express = require('express');
2const { z } = require('zod');
3const router = express.Router();
4 
5const filterSchema = z.object({
6 status: z.enum(['active', 'archived', 'pending']).optional(),
7 minPrice: z.coerce.number().nonnegative().optional(),
8 maxPrice: z.coerce.number().positive().optional(),
9 tags: z
10 .string()
11 .transform((s) => s.split(',').map((t) => t.trim()).filter(Boolean))
12 .pipe(z.array(z.string()).max(10))
13 .optional(),
14 sort: z.enum(['created_at', 'price', 'name']).default('created_at'),
15 order: z.enum(['asc', 'desc']).default('desc'),
16 page: z.coerce.number().int().min(1).default(1),
17 limit: z.coerce.number().int().min(1).max(100).default(20),
18});
19 
20router.get('/products', async (req, res, next) => {
21 const parsed = filterSchema.safeParse(req.query);
22 if (!parsed.success) {
23 return res.status(400).json({ error: 'Invalid filters', details: parsed.error.flatten().fieldErrors });
24 }
25 
26 const { status, minPrice, maxPrice, tags, sort, order, page, limit } = parsed.data;
27 
28 let query = req.db('products');
29 if (status) query = query.where('status', status);
30 if (minPrice != null) query = query.where('price', '>=', minPrice);
31 if (maxPrice != null) query = query.where('price', '<=', maxPrice);
32 if (tags?.length) query = query.whereRaw('tags && ?', [tags]);
33 
34 try {
35 const rows = await query
36 .orderBy(sort, order)
37 .limit(limit)
38 .offset((page - 1) * limit);
39 res.json({ page, limit, data: rows });
40 } catch (err) {
41 next(err);
42 }
43});
44 
45module.exports = router;
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Coercing and defaulting query strings at the schema boundary keeps route logic clean and predictable.
  2. 2safeParse lets you reject bad input with structured errors instead of throwing mid-handler.
  3. 3Building queries conditionally from validated data avoids injecting untrusted values into the database.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Validating query params with Zod in Express — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code