A powerful, lightweight Express.js middleware for validating request fields (body, query, params, headers) with a fluent, chainable API. Zero dependencies, full TypeScript support, and automatic API documentation generation.
Key Features:
cleanUp() and removeIfEmpty()🛠️ Config Builder Tool - Generate validation configs visually!

$ npm install expressjs-field-validator
const {
validateBody,
validateParam,
validateQuery,
param,
} = require('expressjs-field-validator');
router.post('/users/:id',
validateParam().addParams([
param('id').isNumber()
]),
validateBody().addParams([
param('userId').isNumber()
]),
validateQuery().addParams([
param('userName').isRequired()
]),
validateHeader().addParams([
param('Authorization').isRequired()
]),
(req, res, next) => {
// Main Service Here
});
Use param(<field Name>) to define a field.
param('userName').isRequired()
Defines a field userName which is mandatory.
Field is mandatory
Expects array
Expects object
Expects number
Expects email
Expects UUID string
regex Mandatory — A RegExp object or a regex pattern stringValidates that the field value matches the provided regular expression. The value is coerced to a string before testing.
// Using a RegExp object
param('code').isRequired().isMatching(/^[A-Z]+-\d+$/)
// Using a pattern string
param('postalCode').isRequired().isMatching('^\\d{5}$')
Expects boolean value
Expects a date with default format YYYY-MM-DD
format Mandatory String
specify date format, supported
YYYY-MM-DD
DD-MM-YYYY
MM-DD-YYYY
YYYY/MM/DD
DD/MM/YYYY
MM/DD/YYYY
format Mandatory String
Converts a validated date value to the specified format. Must be used with isDate(). The source format is determined by dateFormat() (defaults to YYYY-MM-DD if not set).Supported formats are the same as dateFormat:
YYYY-MM-DD
DD-MM-YYYY
MM-DD-YYYY
YYYY/MM/DD
DD/MM/YYYY
MM/DD/YYYY
If the specified format is not in the supported list, the conversion is silently skipped and the original value is preserved.
// Convert DD/MM/YYYY input to YYYY-MM-DD
param('birthDate').isDate().dateFormat('DD/MM/YYYY').convertToFormat('YYYY-MM-DD')
// Input: "25/12/2024" → Value after validation: "2024-12-25"
// Convert with default source format (YYYY-MM-DD) to DD-MM-YYYY
param('eventDate').isDate().convertToFormat('DD-MM-YYYY')
// Input: "2024-12-25" → Value after validation: "25-12-2024"
min Mandatory Number
Expects number and must be greater than or equal to min
max Mandatory Number
Expects number and must be less than or equal to max
min Mandatory Number
Expects number/string and length must be less than or equal to min
max Mandatory Number
Expects number/string and length must be less than or equal to max
inclues Mandatory Array
Expects number/string and must be one of given array includes
excludes Mandatory Array
Expects number/string and must not be one of given array excludes
countryCode Mandatory String
Expects mobile number with or without countryCode
Expects mobile number which should starts with the country code set with isMobileNumberWithCountryCode
min Mandatory Number
Minimum length of mobile number without country code
max Mandatory Number
Maximum length of mobile number without country code
function Mandatory Function
A function with arguments (value, req, error)
value is the value of the field
req request object
error function with takes error message, should be called on error
(value, req, error) => {
if (value !== 100) {
error('Invalid value customValidator');
}
}
child Mandatory field definition object
Add a child object for arrays and objects
children Mandatory Array of field definition objects
Add a list of children objects for arrays and objects
message Mandatory String
Custom message to be send back in case of validation failure
// Default message
{
"error": [
{
"location": "body.sort",
"param": "sort",
"message": "Invalid Field Error"
}
]
}
// Custom message
{
"error": [
{
"location": "body.sort",
"param": "sort",
"message": "<Your Custom Error Message>"
}
]
}
value Mandatory Any value (non-array, non-object)
Sets a default value for the field when the current value is undefined, null, or '' (empty string). Only applies to fields that are not arrays or objects.
param('status').defaultValue('active')
param('count').isNumber().defaultValue(0)
Removes the field key from the request if the value is considered empty.
[]{}undefined, null, or ''
param('status').removeIfEmpty()
param('items').isArray().removeIfEmpty()
param('meta').isObject().removeIfEmpty()
Note: When both
defaultValueandremoveIfEmptyare used together,defaultValueis applied first. If the default value is non-empty, the field is kept.param('status').defaultValue('active').removeIfEmpty() // field is kept with value 'active'Creating a validation middleware
validateBody() Validate bodyvalidateParam() Validate paramvalidateQuery() Validate queryvalidateHeader() Validate headerDefines the validation failure event - Server returns http status code set via sendErrorCode (default 422), :heavy_exclamation_mark: will not proceed to the next middleware
Response body
{
"error": [
{
"location": "body.sort",
"param": "sort",
"message": "Invalid Field Error"
}
]
}
Defines the validation failure event - Error is set to request.locals.data and error code to request.locals.statusCode, :white_check_mark: will proceed to the next middleware
Error object
Response body
{
"error": [
{
"location": "body.sort",
"param": "sort",
"message": "Invalid Field Error"
}
]
}
const { checkService } = require('expressjs-field-validator');
Pass middleware to checkService, which must be skipped if isToBeForwarded enabled and validation errors are found
router.get('/users/:id',
validateBody().isToBeForwarded().sendErrorCode(500).debug(false).addParams([
param('id').isRequired().isNumber()
]),
checkService((req, res, next) => {
// This middleware is skipped if id is empty or not a number
}),
(req, res, next) => {
// This middleware Will not be skipped, error data will be availble here - req.locals.data and status code - request.locals.statusCode here
});
manually invoke forward mode, if this is set from any middleware, the middlewares wrapped inside checkService won’t be executed
const { skipService } = require('expressjs-field-validator');
router.get('/users/:id',
(req, res, next) => {
skipService(req, 'SOME-ERROR');
next();
}),
checkService((req, res, next) => {
// This middleware is skipped
}),
(req, res, next) => {
// This middleware Will not be skipped, error data will be availble here - req.locals.data and status code - request.locals.statusCode here
});
errorCode Mandatory Error code which should be rejected
isDebugEnabled Mandatory Pass true for development environments, the error object will contain more details about error
Error object
{
"error": [
{
"location": "body.sort",
"param": "sort",
"message": "Invalid Field Error :: somevalueforsort Must Be A Boolean" // More details on error
}
]
}
Applies removeIfEmpty behavior to all fields in the validation middleware. When enabled:
[]{}undefined, null, or ''This is a convenience method to avoid adding removeIfEmpty() to each field individually.
// Instead of adding removeIfEmpty() to each field:
validateBody().addParams([
param('name').removeIfEmpty(),
param('email').removeIfEmpty(),
param('phone').removeIfEmpty(),
])
// You can add it once at the top level:
validateBody().removeIfEmpty().addParams([
param('name'),
param('email'),
param('phone'),
])
Note: Field-level
removeIfEmpty()can still be used when you only want to remove specific fields.cleanUp()
Removes any keys from the request that are not declared in the validation params. This sanitizes the input to only include explicitly defined fields. Works recursively on nested objects and arrays.
// Request body: { name: "John", email: "john@test.com", hackAttempt: "malicious", extra: "data" } validateBody().cleanUp().addParams([ param('name').isRequired(), param('email').isEmail(), ]) // After validation, req.body will only contain: { name: "John", email: "john@test.com" } // The "hackAttempt" and "extra" keys are removedThis is useful for:
- Security: Preventing unexpected fields from being processed
- Data sanitization: Ensuring only declared fields reach your business logic
- API consistency: Keeping request payloads clean
Note:
cleanUp()also works recursively on nested objects and arrays defined withaddChild()oraddChildren().addParams(paramList)
paramListMandatory Array of field definition objectsvalidateBody().addParams([ // Add List of definition here param('field1').isRequired(), ])Definintion of a field here : Defining a Field
Dealing with nested objects
Request body
{ "field1": "Value", // String, Mandatory "field2": [ // array, Mandatory { "field21": "44443" }, // object Optional, number mandatory { "field21": "44443" } ], "field3": { // Object Optional "field31": "true", // Boolean Mandatory "field32": "String" // String Mandatory }, "field4": [ // array, Mandatory 123, 445, 3434 // Number Optional ], }Should send http status code 500 in case of error
Validation
```js router.post(‘/users/:id’, validateBody().isToBeRejected().sendErrorCode(500).addParams([ param(‘field1’).isRequired(), param(‘field2’).isRequired().isArray().isRequired().addChild( param(‘field2-array’).isObject().addChild( // field2-array is for tracking, you can give any name here param(‘field21’).isNumber().isRequired() ) ), param(‘field3’).isObject().addChildren([ param(‘field31’).isBoolean().isRequired(), param(‘field32’).isRequired() ]), param(‘field4’).isRequired().isArray().isRequired().addChild( param(‘field4-array’).isNumber() ), ]), validateParam().isToBeRejected().sendErrorCode(500).addParams([ param(‘field1’).isRequired(), ]), // define validateQuery(), // define validateHeader(), (req, res, next) => {
// Main Service Here
});
## API Documentation Generator
Automatically generate beautiful HTML documentation for your API endpoints. The generator extracts all routes with validation middlewares and creates an interactive documentation page.

### documentResponse()
Use `documentResponse()` to document the possible responses for an endpoint. This is a pass-through middleware that attaches response metadata for documentation generation. Place it after validation middleware but before your route handler.
```js
const { documentResponse } = require('expressjs-field-validator');
app.post('/users',
validateBody().isToBeRejected().addParams([
param('name').isRequired(),
param('email').isRequired().isEmail(),
]),
documentResponse({
201: {
description: 'User created successfully',
body: { message: 'User created', data: { id: 1, name: 'John' } },
headers: { 'X-Request-Id': 'uuid' }
},
422: {
description: 'Validation failed',
body: { error: 'Validation failed', details: [] }
}
}),
(req, res) => res.status(201).send({ message: 'User created' })
);
| Option | Type | Description |
|---|---|---|
description |
string |
Description of the response |
body |
any |
Sample response body |
headers |
object |
Response headers as key-value pairs |
Note: Status codes must be valid HTTP status codes (100-599). The middleware throws an error for invalid status codes.
const express = require('express');
const {
validateBody,
validateQuery,
param,
generateDocs
} = require('expressjs-field-validator');
const app = express();
// Define your routes with validation
app.post('/users',
validateBody().isToBeRejected().addParams([
param('name').isRequired(),
param('email').isRequired().isEmail(),
]),
(req, res) => res.status(201).send({ message: 'User created' })
);
app.get('/users/:id',
validateQuery().isToBeRejected().addParams([
param('include').shouldInclude(['profile', 'settings', 'posts']),
]),
(req, res) => res.send({ user: {} })
);
// Generate documentation AFTER all routes are registered
generateDocs(app, {
title: 'My API',
version: '1.0.0',
outputDir: './docs',
filename: 'api-docs.html'
});
app.listen(3000);
| Option | Type | Default | Description |
|---|---|---|---|
outputDir |
string |
'./docs' |
Output directory for the generated HTML file |
filename |
string |
'api-docs.html' |
Output filename |
title |
string |
'API Documentation' |
Documentation title |
version |
string |
- | API version to display |
You can serve the generated HTML documentation using Express static middleware:
const express = require('express');
const path = require('path');
const { generateDocs, validateBody, param } = require('expressjs-field-validator');
const app = express();
app.use(express.json());
// Define your API routes
app.post('/users',
validateBody().isToBeRejected().addParams([
param('name').isRequired(),
param('email').isRequired().isEmail(),
]),
(req, res) => res.status(201).send({ message: 'User created' })
);
// Generate documentation
generateDocs(app, {
title: 'My API',
version: '1.0.0',
outputDir: './docs'
});
// Serve the documentation at /api-docs
app.use('/api-docs', express.static(path.join(__dirname, 'docs')));
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
console.log('API Docs available at http://localhost:3000/api-docs/api-docs.html');
});
Alternative: Serve docs at root path
// Serve docs at /docs endpoint
app.use('/docs', express.static(path.join(__dirname, 'docs')));
// Or redirect /docs to the HTML file directly
app.get('/docs', (req, res) => {
res.sendFile(path.join(__dirname, 'docs', 'api-docs.html'));
});
Production Tips:
// Only serve docs in non-production
if (process.env.NODE_ENV !== 'production') {
app.use('/api-docs', express.static(path.join(__dirname, 'docs')));
}
end() removed from field definitionsend() is no longer required (or available) when defining fields with param().
- param('userName').isRequired().end()
+ param('userName').isRequired()
- param('field21').isNumber().isRequired().end()
+ param('field21').isNumber().isRequired()
done() removed from validation middlewaredone() is no longer required (or available) when creating validation middleware.
- validateBody().addParams([
- param('field1').isRequired().end(),
- ]).done()
+ validateBody().addParams([
+ param('field1').isRequired(),
+ ])
- validateParam().isToBeRejected().sendErrorCode(500).addParams([
- param('id').isNumber().end(),
- ]).done()
+ validateParam().isToBeRejected().sendErrorCode(500).addParams([
+ param('id').isNumber(),
+ ])
v3.x
router.post('/users/:id',
validateParam().addParams([
param('id').isNumber().end()
]).done(),
validateBody().isToBeRejected().sendErrorCode(500).addParams([
param('name').isRequired().end(),
param('email').isEmail().end(),
param('role').shouldInclude(['admin', 'user']).end(),
]).done(),
(req, res, next) => {
// Main Service
});
v4.x
router.post('/users/:id',
validateParam().addParams([
param('id').isNumber()
]),
validateBody().isToBeRejected().sendErrorCode(500).addParams([
param('name').isRequired(),
param('email').isEmail(),
param('role').shouldInclude(['admin', 'user']),
]),
(req, res, next) => {
// Main Service
});
The following features are new in v4.x:
defaultValue(value)Sets a default value for a field when the value is undefined, null, or ''.
param('status').defaultValue('active')
param('count').isNumber().defaultValue(0)
removeIfEmpty()Removes the field key from the request if the value is empty.
param('notes').removeIfEmpty()
param('tags').isArray().removeIfEmpty()
convertToFormat(format)Converts a validated date to a different format. The original value in the request is replaced with the converted value.
param('birthDate').isDate().dateFormat('DD/MM/YYYY').convertToFormat('YYYY-MM-DD')