Exercises
Objectives
By the end of this lesson, you should be able to:
- Design a schema with length constraints and a default value
- Wire it into a route with the validate middleware
- Confirm valid, invalid, and default-applying requests all behave correctly
⚠️ A note on verification: every command and every output in this lesson was actually run, with real HTTP requests against a real Express app.
Exercise: Validating a Post-Creation Endpoint
a) Define the schema.
const postSchema = z.object({
title: z.string().min(3, 'Title must be at least 3 characters').max(100, 'Title must be at most 100 characters'),
body: z.string().min(1, 'Body is required'),
published: z.boolean().optional().default(false)
});
b) Build the route, using the validate middleware from Lesson 3:
app.post('/api/v1/posts', validate(postSchema), (req, res) => {
res.status(201).json(req.validatedBody);
});
c) Test a valid request with no published field, and notice the default:
GOOD (default published): 201 {"title":"Hello World","body":"My first post","published":false}
published wasn’t in the request at all, .default(false) filled it in automatically, req.validatedBody always has a defined, predictable value for it, no separate req.body.published ?? false check needed anywhere else in the code.
d) Test a title that’s too short:
TOO SHORT TITLE: 400 {"error":"ValidationError","message":"Invalid request data","details":[{"field":"title","message":"Title must be at least 3 characters"}]}
e) Test a missing body:
MISSING BODY: 400 {"error":"ValidationError","message":"Invalid request data","details":[{"field":"body","message":"Invalid input: expected string, received undefined"}]}
f) Add a fourth field. Add tags, an optional array of strings, with at most 5 entries (z.array(z.string()).max(5).optional()), and confirm both a valid array and an array with 6 entries behave correctly.
g) Combine with authentication. Add requireAuth (Module 3) before validate(postSchema) on this route, confirm the middleware order means an unauthenticated request is rejected with 401 before validation even runs, and explain, in one sentence, why checking identity before validating the body’s contents is the sensible order.
Recap
This module replaced ad hoc if checks with proper schema validation: Zod schemas expressing real constraints, reusable validation middleware returning structured, per-field errors, and closing the mass-assignment gap by ensuring routes read from validated, stripped data.
Next module: security hardening, rate limiting a login endpoint and managing secrets correctly, on top of everything built so far.