Validate Request Inputs with SEAM API's Schema Middleware
SEAM API’s validate middleware checks field rules on body, params, or query before any controller runs. Covers all 7 rule types with real schema examples.
Use this file to discover all available pages before exploring further.
SEAM API ships a lightweight schema-based validation middleware that intercepts incoming requests and checks every configured field before a controller is ever invoked. If any field fails its rules, the request is rejected immediately with a 400 Bad Request containing all error messages joined into a single response. Nothing invalid reaches the database layer.
validate is a higher-order function (a factory): you call it with a schema object and an optional source string, and it returns a standard Express middleware function. When the middleware runs, it reads the data from req[source], iterates over every field defined in the schema, collects all violations, and either rejects with a BadRequestError or calls next() to pass the request to the controller.
Each key in the schema object maps to a field name. Its value is a rules object whose properties describe all constraints to enforce:
Rule
Type
Applies To
Description
required
boolean
any
Field must be present, non-null, and non-empty string
type
'string' | 'number' | 'boolean'
any
JavaScript typeof check on the raw value
minLength
number
strings
Minimum character length (uses String(value).length)
maxLength
number
strings
Maximum character length (uses String(value).length)
min
number
numbers
Minimum numeric value (uses Number(value))
max
number
numbers
Maximum numeric value (uses Number(value))
pattern
RegExp
strings
Value must match the regular expression
Rules are evaluated in order. If a required field is absent, the remaining rules for that field are skipped. If a field is optional and absent, validation passes silently for that field. Type mismatches abort further checks on the same field to avoid misleading length/range errors.
const validate = (schema, source = 'body') => (req, res, next) => { const data = req[source] || {}; const errors = []; for (const [field, rules] of Object.entries(schema)) { const value = data[field]; const isEmpty = value === undefined || value === null || value === ''; if (rules.required && isEmpty) { errors.push(`'${field}' es requerido`); continue; } if (isEmpty) continue; if (rules.type && typeof value !== rules.type) { errors.push(`'${field}' debe ser de tipo ${rules.type}`); continue; } if (rules.minLength !== undefined && String(value).length < rules.minLength) errors.push(`'${field}' debe tener al menos ${rules.minLength} caracteres`); if (rules.maxLength !== undefined && String(value).length > rules.maxLength) errors.push(`'${field}' no debe exceder ${rules.maxLength} caracteres`); if (rules.min !== undefined && Number(value) < rules.min) errors.push(`'${field}' debe ser mayor o igual a ${rules.min}`); if (rules.max !== undefined && Number(value) > rules.max) errors.push(`'${field}' debe ser menor o igual a ${rules.max}`); if (rules.pattern && !rules.pattern.test(String(value))) errors.push(`'${field}' tiene un formato inválido`); } if (errors.length > 0) return next(new BadRequestError(errors.join('. '))); next();};
All errors are accumulated before the request is rejected, so the client receives a complete list of every failing field in a single response rather than discovering them one at a time.
Because validate returns a standard Express middleware, you can chain as many calls as you need in a single route definition. Express processes them left to right and stops at the first rejection:
router.put( '/:id', validate(idSchema, 'params'), // 1. Validate URL :id is a numeric string validate(bodySchema, 'body'), // 2. Validate request body fields controller.update // 3. Controller only reached when both pass);
Each validate call covers exactly one source. Chaining two calls — one for params and one for body — is the recommended pattern when a route needs both. Errors from params validation will be returned before body validation even runs.
The built-in validate middleware covers the most common single-field constraints. For more advanced use cases — nested objects, arrays, conditional rules, cross-field dependencies, or automatic sanitization — consider replacing or supplementing it with Zod or Joi. Both integrate cleanly with Express and provide richer error formatting out of the box.