Episode 3 — NodeJS MongoDB Backend Architecture / 3.10 — Input Validation
3.10 — Exercise Questions: Input Validation
30+ hands-on exercises to practice input validation with express-validator, Zod, and error handling patterns.
< Error Response Format | Interview Questions >
Section A: Conceptual Questions
1. Explain the difference between validation and sanitization. Give two examples of each.
2. Why is client-side validation alone insufficient for a secure application? Describe a scenario where an attacker bypasses client-side validation.
3. What is "defense in depth" in the context of input validation? Name at least four layers where validation can occur.
4. What is mass assignment vulnerability? How does input validation prevent it?
5. When should you use HTTP status 400 vs 422 for validation errors? What is the practical difference?
Section B: express-validator
6. Write an express-validator chain for a POST /api/contacts endpoint with these fields:
name(required, 2-100 characters, letters and spaces only)email(required, valid email)phone(optional, numeric, 10-15 digits)message(required, 10-2000 characters)
7. Write a custom validator using .custom() that checks if a username field contains no profanity (check against a hardcoded list of banned words).
8. Using checkSchema(), define a validation schema for a PUT /api/settings endpoint that accepts:
theme(must be "light" or "dark")language(must be a valid ISO language code from a list)notificationsEnabled(boolean)itemsPerPage(integer between 10 and 100)
9. Create a reusable validate middleware function that:
- Runs all validations
- Returns errors in the format
{ success: false, errors: [{ field, message }] } - Returns only the first error per field
10. Write express-validator chains for a POST /api/orders endpoint:
items(required, array, 1-50 items)items.*.productId(required, valid MongoDB ObjectId)items.*.quantity(required, integer, 1-999)shippingAddress.street(required, string)shippingAddress.city(required, string)shippingAddress.zip(required, matches format XXXXX or XXXXX-XXXX)couponCode(optional, alphanumeric, exactly 8 characters)
11. Write a custom async validator that checks if a productId exists in the database before allowing an order to be placed.
12. Explain what .bail() does in express-validator. Write an example where it prevents unnecessary database queries.
13. Create validation middleware for a file upload endpoint that validates:
- File size (max 5MB)
- File type (only JPEG, PNG, GIF)
captionfield (optional, max 200 characters)
Section C: Zod
14. Write a Zod schema for a blog post with:
title(string, 5-200 characters)content(string, minimum 50 characters)tags(array of strings, each max 30 chars, max 10 tags)category(one of: "tech", "lifestyle", "business", "health")publishDate(optional, ISO datetime string)isDraft(boolean, defaults to true)
15. Write a Zod schema that uses .refine() to validate that a startDate is before an endDate, and that the date range does not exceed 90 days.
16. Create a Zod schema with .transform() that:
- Trims and lowercases an email
- Converts a string price to a number in cents (e.g., "19.99" -> 1999)
- Converts a comma-separated string of tags into an array
17. Write a Zod schema for a search endpoint's query parameters, using z.preprocess() to handle the fact that all query params arrive as strings:
page(number, default 1)limit(number, default 20, max 100)sort(one of: "newest", "oldest", "popular")minPrice(optional, number)maxPrice(optional, number)
18. Use z.discriminatedUnion() to create a schema for a notification preferences object where the shape depends on the type field:
type: "email"requiresemailAddressfieldtype: "sms"requiresphoneNumberfieldtype: "push"requiresdeviceTokenfield
19. Write Express middleware using Zod that validates req.body, req.params, and req.query together in a single schema.
20. Create a Zod schema for a user profile update where all fields are optional but at least one field must be provided (use .refine()).
Section D: Error Response Format
21. Design a standard error response format for an API. Include the TypeScript interface and three examples: validation error, authentication error, and not found error.
22. Write a global error handler middleware that converts these error types into your standard format:
- Mongoose
ValidationError - Mongoose duplicate key error (code 11000)
JsonWebTokenError- Generic
Error
23. Write a function that converts Zod's error.issues array into a field-keyed error object suitable for a React form (e.g., { email: ["error1"], "address.city": ["error2"] }).
24. Design an error response format that supports i18n. Include error codes, interpolation parameters, and show how a French-language frontend would translate the errors.
25. Create a React hook useFormErrors(response) that takes an API error response and returns an object where you can look up errors by field name, including support for nested fields like address.city.
Section E: Integration Challenges
26. Build a complete POST /api/register endpoint from scratch with:
- express-validator validation
- Reusable validation middleware
- Mongoose model with schema validation
- Consistent error response format
- Both middleware and database validation errors caught and formatted identically
27. Build the same registration endpoint using Zod instead of express-validator. Compare the code volume and readability.
28. Write validation for a PATCH /api/users/:id endpoint where:
- The
:idparam must be a valid MongoDB ObjectId - All body fields are optional
- If
emailis provided, check it is not already taken (async) - If
passwordis provided, it must be strong - If
roleis provided, only admins can set it (checkreq.user.role)
29. Create a validation middleware factory that accepts a Zod schema and returns Express middleware, but also:
- Strips unknown fields (Zod's
.strict()or.strip()) - Logs validation failures with request ID
- Returns errors in your standard format
30. Design and implement validation for a multi-step form wizard API:
POST /api/wizard/step1— personal info (name, email, phone)POST /api/wizard/step2— address info (street, city, state, zip)POST /api/wizard/step3— payment info (cardNumber, expiry, cvv)- Each step validates only its own fields
- A final
POST /api/wizard/submitvalidates ALL steps together
31. Write a rate-limited validation endpoint that:
- Validates an email address
- Checks if the email exists in the database
- Rate-limits the check to 5 requests per minute per IP
- Returns appropriate error codes for each failure type
32. Build a file validation system for a profile picture upload:
- Check file size (max 2MB)
- Check MIME type (image/jpeg, image/png, image/webp)
- Validate accompanying
alttext (max 200 chars) - Return specific error messages for each validation failure
Bonus Challenges
33. Compare the performance of express-validator vs Zod by writing a benchmark that validates 10,000 objects through each library. Measure and compare execution times.
34. Write a Zod schema that validates a MongoDB aggregation pipeline array — each element must be an object with exactly one key that starts with $ (like $match, $group, $sort).
35. Create a "validation as configuration" system where validation rules are stored in a JSON file and dynamically loaded into either express-validator chains or Zod schemas at startup.