082
LVL 03 — MID DEVELOPERSESSION 082DAY 82

ZOD VALIDATION

🎫 PIXELCRAFT-069
🐛Fix | 🟡 Medium | Priority: 🟠 High

A user sent { "email": 42, "password": "" } to the register endpoint and crashed the server. None of the API inputs are validated. Validate ALL inputs with Zod. Share schemas between frontend and backend.
CONCEPTS.UNLOCKED
🛡️
Server-Side Validation
Never trust the client. Frontend validation is for UX. Server validation is for security. Anyone can bypass your React forms with curl or Postman — the server must validate everything.
Zod
TypeScript-first schema validation. Define a schema once → validate data, infer TypeScript types, generate error messages. Smaller and faster than Joi. The modern standard for Node.js.
🔧
Validation Middleware
One middleware, every route. Create a reusable validate() middleware that checks req.body, req.query, and req.params against Zod schemas. Invalid → 400 before the handler runs.
📋
Consistent Error Responses
Same error format everywhere. { error: "Validation failed", details: [{ field: "email", message: "Invalid email" }] }. Frontend can always parse errors the same way.
🔗
Shared Schemas
One schema, frontend AND backend. Define in a shared package. React forms validate with the same rules as Express routes. Change once → both sides update. Single source of truth.
🏷️
Type Inference
z.infer<typeof schema> generates types. No duplicate type definitions. The Zod schema IS the type. TypeScript catches mismatches at compile time.
HANDS-ON.TASKS
01
Define Zod Schemas
npm install zod // schemas/auth.js const { z } = require('zod'); const registerSchema = z.object({ email: z.string() .email('Invalid email address'), password: z.string() .min(8, 'Min 8 characters') .max(100, 'Max 100 characters') .regex(/[A-Z]/, 'Must contain uppercase') .regex(/[0-9]/, 'Must contain a number'), name: z.string() .min(2, 'Min 2 characters') .max(50, 'Max 50 characters') .trim(), }); const loginSchema = z.object({ email: z.string().email(), password: z.string().min(1, 'Password is required'), }); module.exports = { registerSchema, loginSchema };
02
Validation Middleware
function validate(schema) { return (req, res, next) => { const result = schema.safeParse(req.body); if (!result.success) { const errors = result.error.issues.map( (issue) => ({ field: issue.path.join('.'), message: issue.message, }) ); return res.status(400).json({ error: 'Validation failed', details: errors, }); } // Replace body with parsed // (trimmed, coerced) data req.body = result.data; next(); }; } // Usage on routes router.post('/auth/register', validate(registerSchema), registerHandler ); router.post('/auth/login', validate(loginSchema), loginHandler );
safeParse() returns success/failure without throwing. The parsed data is cleaned — strings trimmed, types coerced. Always use the parsed result, not the raw input.
03
Validate Query & URL Params
const paginationSchema = z.object({ page: z.coerce.number() .int().positive().default(1), limit: z.coerce.number() .int().min(1).max(100).default(20), sort: z.enum([ 'createdAt', 'name', 'size' ]).default('createdAt'), }); function validateQuery(schema) { return (req, res, next) => { const result = schema.safeParse(req.query); if (!result.success) { return res.status(400).json({ error: 'Invalid query parameters', details: result.error.issues, }); } req.query = result.data; next(); }; } const imageIdSchema = z.object({ id: z.string() .regex(/^[a-f0-9]{24}$/, 'Invalid image ID'), }); router.get('/images/:id', validateParams(imageIdSchema), getImage );
04
Shared Schemas: Frontend + Backend
// shared/schemas.js — used by BOTH const { z } = require('zod'); const imageUploadSchema = z.object({ title: z.string() .min(1).max(100).trim(), description: z.string() .max(500).optional(), tags: z.array(z.string().max(30)) .max(10).optional(), }); module.exports = { imageUploadSchema }; // --- Backend: Express route --- const { imageUploadSchema } = require('../shared/schemas'); router.post('/images/upload', validate(imageUploadSchema), handler); // --- Frontend: React form --- // import { imageUploadSchema } // from '../shared/schemas'; // const result = // imageUploadSchema.safeParse(formData); // if (!result.success) // setErrors(result.error.issues);
05
Close the Ticket
git switch -c feature/PIXELCRAFT-069-zod-validation git add server/ shared/ src/ git commit -m "Add Zod validation to all API inputs (PIXELCRAFT-069)" git push origin feature/PIXELCRAFT-069-zod-validation # PR → Review → Merge → Close ticket ✅
CS.DEEP-DIVE

Input validation is the first line of defense.

Most security vulnerabilities start with unvalidated input. SQL injection, XSS, buffer overflows — all exploit the gap between expected and actual input.

// The trust boundary:

Client (untrusted)
  Users, browsers, scripts, bots
  Can send anything

────── VALIDATION BOUNDARY ──────

Server (trusted)
  Only processes validated data
  Rejects malformed input at the gate

// OWASP Top 10 — #1 is Injection
// Validation prevents most of them.

// Defense in depth:
Layer 1: Schema validation (Zod)
Layer 2: Parameterized queries
Layer 3: Output encoding
Layer 4: CSP headers
"Validation Lab"
[A]Add Zod to React forms with react-hook-form + @hookform/resolvers/zod. Import the same shared schemas. Show inline errors under each field with the exact Zod messages.
[B]Add custom refinements: password can't contain the user's name, tags must be unique. Use .refine() and .superRefine() for complex cross-field validation.
[C]Research: compare Zod vs Joi vs Yup vs AJV. When is each appropriate? Benchmark parsing speed on 10,000 objects. Write a comparison document.
REF.MATERIAL
ARTICLE
Colin McDonnell
Official Zod reference: primitives, objects, arrays, unions, transforms, refinements, and error handling. TypeScript-first validation.
ZODOFFICIALESSENTIAL
VIDEO
Web Dev Simplified
Practical Zod walkthrough: defining schemas, parsing, error handling, and integrating with Express and React forms.
ZODTUTORIAL
ARTICLE
OWASP Foundation
The most critical web application security risks. Injection, broken auth, and why input validation is the first line of defense.
SECURITYOFFICIALESSENTIAL
ARTICLE
React Hook Form
Integrate Zod schemas with React forms: automatic validation, typed form data, and inline error messages from shared schemas.
REACTFORMS
VIDEO
Fireship
Ultra-fast Zod overview: schema definition, type inference, parsing, and why it's replacing Joi as the validation standard.
ZODQUICK
// LEAVE EXCITED BECAUSE
Send garbage to any endpoint → get a clear, helpful 400 error explaining exactly what's wrong. Same validation rules on frontend AND backend from a single source. The API is now bulletproof.