Files
adastra_api/docs/decisions/0012-input-validation-express-validator.md

3.3 KiB

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.