docs(adr): add ADR 0012 — express-validator for input validation
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
# Use express-validator for input validation at the route level
|
||||||
|
|
||||||
|
* Status: accepted
|
||||||
|
* Date: 2026-04-26
|
||||||
|
|
||||||
|
## Context and Problem Statement
|
||||||
|
|
||||||
|
API endpoints accepted user input without validating format, length, or type. A `fieldValidation` helper existed in several controllers but only checked for empty values, was duplicated across three controllers (users, articles, products), and had a critical bug: it called `next(error)` without `return`, so execution continued even when a field was invalid.
|
||||||
|
|
||||||
|
## Decision Drivers
|
||||||
|
|
||||||
|
* Input validation must happen before business logic and must halt execution on failure.
|
||||||
|
* Validation rules should be declared at the route level, not buried inside controller logic.
|
||||||
|
* The solution must be consistent and reusable across the codebase without adding a framework wrapper.
|
||||||
|
|
||||||
|
## Considered Options
|
||||||
|
|
||||||
|
* Keep and fix the `fieldValidation` helper
|
||||||
|
* Joi with a validation middleware wrapper
|
||||||
|
* express-validator with route-level chains
|
||||||
|
|
||||||
|
## Decision Outcome
|
||||||
|
|
||||||
|
Chosen option: "express-validator", because it integrates natively as Express middleware, allows validation rules to be declared directly in route definitions, and requires no wrapper function. A shared `validate.js` middleware reads `validationResult` and returns HTTP 422 with a structured error body if any rule fails.
|
||||||
|
|
||||||
|
Structure:
|
||||||
|
- `src/middlewares/validate.js` — shared middleware that checks `validationResult` and short-circuits on errors
|
||||||
|
- `src/middlewares/validators/users.validators.js` — rule sets for user endpoints
|
||||||
|
- `src/middlewares/validators/articles.validators.js` — rule sets for article endpoints
|
||||||
|
- Rules applied at route level: `router.post('/', rules, validate, controller)`
|
||||||
|
|
||||||
|
Initial coverage: CMS user endpoints (register, login, update profile/email/username/role) and article endpoints (create, update). Products and tags endpoints retain the old `fieldValidation` pattern pending a separate ecommerce validation pass.
|
||||||
|
|
||||||
|
### Positive Consequences
|
||||||
|
|
||||||
|
* Validation always runs before the controller — no risk of continuing on invalid input.
|
||||||
|
* Rules are co-located with routes, making the contract of each endpoint visible at a glance.
|
||||||
|
* Reusable rule sets can be composed and shared across routes.
|
||||||
|
* `fieldValidation` removed from users and articles controllers — no more duplicated logic.
|
||||||
|
|
||||||
|
### Negative Consequences
|
||||||
|
|
||||||
|
* Products and tags controllers still use the old `fieldValidation` pattern — inconsistency until the ecommerce pass is done.
|
||||||
|
* Validation rules must be maintained in sync with model constraints (e.g. if a column length changes, the validator must be updated manually).
|
||||||
|
|
||||||
|
## Pros and Cons of the Options
|
||||||
|
|
||||||
|
### Keep and fix fieldValidation
|
||||||
|
|
||||||
|
* Good, because no new dependency.
|
||||||
|
* Bad, because it is duplicated in every controller that needs it.
|
||||||
|
* Bad, because it belongs in controllers, not at the boundary where input arrives.
|
||||||
|
|
||||||
|
### Joi
|
||||||
|
|
||||||
|
* Good, because schema-based validation with rich type coercion.
|
||||||
|
* Bad, because requires a wrapper middleware to integrate with Express — adds boilerplate not present in the existing codebase.
|
||||||
|
|
||||||
|
### express-validator
|
||||||
|
|
||||||
|
* Good, because native Express middleware — no wrapper needed.
|
||||||
|
* Good, because rule sets are plain arrays, easy to compose and test independently.
|
||||||
|
* Good, because actively maintained with wide adoption in Express projects.
|
||||||
|
* Bad, because rules are imperative chains rather than declarative schemas, which can be verbose for complex objects.
|
||||||
Reference in New Issue
Block a user