npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

expressjs-field-validator

v4.0.7

Published

A powerful, lightweight Express.js middleware for validating request fields (body, query, params, headers) with a fluent, chainable API. Zero dependencies, TypeScript support, and auto API docs generation.

Readme

expressjs-field-validator

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:

  • 🔒 Comprehensive Validation - Validate strings, numbers, emails, UUIDs, dates, booleans, arrays, and nested objects
  • ⛓️ Fluent API - Chain validation rules for readable, maintainable code
  • 📝 Auto-generated API Docs - Generate beautiful HTML documentation from your validation rules
  • 🧹 Data Sanitization - Clean up requests with cleanUp() and removeIfEmpty()
  • 🎯 Flexible Error Handling - Reject requests or forward errors to your handlers
  • 📦 Zero Dependencies - Lightweight with no external runtime dependencies
  • 💪 TypeScript Ready - Full type definitions included

🛠️ Config Builder Tool - Generate validation configs visually!

Config Builder Screenshot

Reliability Rating Bugs code_smells coverage duplicated_lines_density ncloc sqale_rating alert_status security_rating security_rating sqale_index vulnerabilities

Installation

$ npm install expressjs-field-validator

How To Use

 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

});

Getting Started

Defining a Field

Use param(<field Name>) to define a field.

param('userName').isRequired()

Defines a field userName which is mandatory.

Available Options

isRequired()

Field is mandatory

isArray()

Expects array

isObject()

Expects object

isNumber()

Expects number

isEmail()

Expects email

isUUID()

Expects UUID string

isMatching(regex)
  • regex Mandatory — A RegExp object or a regex pattern string

Validates 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}$')
isBoolean()

Expects boolean value

isDate()

Expects a date with default format YYYY-MM-DD

dateFormat(format)
  • 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
convertToFormat(format)
  • 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"
minimumNumber(min)
  • min Mandatory Number Expects number and must be greater than or equal to min
maximumNumber(max)
  • max Mandatory Number Expects number and must be less than or equal to max
minimumLength(min)
  • min Mandatory Number Expects number/string and length must be less than or equal to min
maximumLength(max)
  • max Mandatory Number Expects number/string and length must be less than or equal to max
shouldInclude(inclues)
  • inclues Mandatory Array Expects number/string and must be one of given array includes
shouldExclude(excludes)
  • excludes Mandatory Array Expects number/string and must not be one of given array excludes
isMobileNumberWithCountryCode(countryCode)
  • countryCode Mandatory String Expects mobile number with or without countryCode
isMobileNumberWithCountryCodeMandatory()

Expects mobile number which should starts with the country code set with isMobileNumberWithCountryCode

isMobileNumberWithMinimumLength(min)
  • min Mandatory Number Minimum length of mobile number without country code
isMobileNumberWithMaximumLength(max)
  • max Mandatory Number Maximum length of mobile number without country code
customValidator(function)
  • 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');
  }
}
addChild(child)
  • child Mandatory field definition object Add a child object for arrays and objects
addChildren(children)
  • children Mandatory Array of field definition objects Add a list of children objects for arrays and objects
sendErrorMessage(message)
  • 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>"
        }
    ]
}
defaultValue(value)
  • 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)
removeIfEmpty()

Removes the field key from the request if the value is considered empty.

  • For arrays: removes if []
  • For objects: removes if {}
  • For other fields: removes if undefined, null, or ''
param('status').removeIfEmpty()
param('items').isArray().removeIfEmpty()
param('meta').isObject().removeIfEmpty()

Note: When both defaultValue and removeIfEmpty are used together, defaultValue is 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 body
  • validateParam() Validate param
  • validateQuery() Validate query
  • validateHeader() Validate header

Available Options

isToBeRejected()

Defines 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"
        }
    ]
}
isToBeForwarded()

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"
        }
    ]
}
checkService
  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 
  
});
skipService

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 
  
});
sendErrorCode(errorCode)
  • errorCode Mandatory Error code which should be rejected
debug(isDebugEnabled)
  • 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
        }
    ]
}
removeIfEmpty()

Applies removeIfEmpty behavior to all fields in the validation middleware. When enabled:

  • Arrays: removed if []
  • Objects: removed if {}
  • Other fields: removed if 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: "[email protected]", hackAttempt: "malicious", extra: "data" }
validateBody().cleanUp().addParams([
  param('name').isRequired(),
  param('email').isEmail(),
])
// After validation, req.body will only contain: { name: "John", email: "[email protected]" }
// The "hackAttempt" and "extra" keys are removed

This 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 with addChild() or addChildren().

addParams(paramList)
  • paramList Mandatory Array of field definition objects
validateBody().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

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.

API Documentation Screenshot

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.

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' })
);

Response Configuration Options

| 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.

Usage

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);

Options

| 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 |

Features

  • 🎨 Beautiful dark theme with syntax highlighting
  • 🔍 Search functionality to filter endpoints
  • 📊 Statistics showing endpoint counts by method
  • 📋 Detailed field information including types, constraints, and nesting
  • 📝 Sample request body with copy-to-clipboard functionality
  • 🎯 Response config display (mode, error codes, cleanUp, etc.)
  • 📱 Responsive design for mobile viewing

Hosting API Documentation

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:

  • Add caching headers for better performance
  • Use environment variables to conditionally enable docs (disable in production if needed)
  • Consider adding authentication to protect internal API documentation
// Only serve docs in non-production
if (process.env.NODE_ENV !== 'production') {
  app.use('/api-docs', express.static(path.join(__dirname, 'docs')));
}

Migration Guide

Migrating from v3.x to v4.x

Breaking Changes

1. end() removed from field definitions

end() 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()
2. done() removed from validation middleware

done() 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(),
+ ])
Full Before / After Example

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
});

New Features

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')