🛡️ C4 · Validation: express-validator / zod
C4 · Validation: express-validator / zod: Validation in Express is like **hiring and training the security guard at the door**: in Spring, writing `@Valid` + `@NotBlank` means se
Validation in Express is like **hiring and training the security guard at the door**: in Spring, writing `@Valid` + `@NotBlank` means security is already part of the building (the framework handles it); in Express you first pick a security firm (`express-validator` or `zod`), then teach it exactly which rules to enforce ONE BY ONE (`body('title').isLength({min:3, max:120})`). So why is a separate library required when JavaScript has no type system of its own (and even TypeScript only guards at compile time, not runtime)? Because `req.body` is raw JSON arriving over HTTP — even if you print `typeof` in a browser console you just see "object", but no compiler guarantees that the `title` field inside it is REALLY a 3-120 character string; only a library that EXPLICITLY checks at runtime can guarantee that. In Java, Bean Validation (`@NotBlank`, `@Size`) is "glued" to the class field; in Express, validation sits as a "guard" IN FRONT OF the route (as middleware) — same logic (validate the contract AT RUNTIME), different syntax. The outcome for a tester is the same: even if validation rules LOOK defined, if no code line actually READS the result, the rules do nothing at all — as you will see next.
Defining Rules and READING the Result
**🐞 Defect Birth — if the `validationResult(req)` check is forgotten** **Code:** `body('title').isLength(...)` rules WERE defined, but the handler has no `validationResult(req)` call and no `if (!errors.isEmpty())` check. **What happens:** `express-validator` rules RUN in the background and collect errors, but since nobody READS the result, the errors are silently ignored. A `POST /api/v1/bugs { "title": "" }` request returns 201 instead of 400 and an empty-title bug gets saved. **Why sneaky:** on inspection the validation rules are REALLY there — a code review might say "validation exists" and move on. But DEFINING a rule and READING its result are two separate steps; doing the first and forgetting the second produces the SAME outcome (silent 201) from a DIFFERENT root cause than the missing `starter-validation` in Spring (B1). **Where the tester catches it:** sending invalid data (empty title, invalid email) as a negative test and getting 201 — this is the Express counterpart of the "rule exists but is never read" defect family.
🎬 The Rule Exists, But Nobody Reads It
body(...).isLength(...)
Rules run in the background
errors object filled
Got 201 with empty title
The developer writes the rule `body('title').isLength({min:3,max:120})` — the rule is DEFINED.
When a request arrives, this rule REALLY RUNS and checks the `title` field.
An empty `title` violates the rule, the `errors` object fills with error info.
But the handler never calls `validationResult(req)` — the filled `errors` object is NEVER read, silently ignored.
The lesson — Defining a rule is not enough, reading the RESULT and short-circuiting the handler (return) is mandatory. A tester always asks not "does a rule exist" but "is the rule really ENFORCED?"
From Defining a Rule to Rejecting a Request