express-joi-validation vs express-validator
Express Middleware for Validation
express-joi-validationexpress-validatorSimilar Packages:

Express Middleware for Validation

In the realm of Node.js web development, validation middleware is crucial for ensuring that incoming request data adheres to expected formats and constraints. This helps maintain data integrity and enhances security by preventing malformed or malicious data from being processed. Both 'express-joi-validation' and 'express-validator' serve this purpose, but they approach validation from different angles. 'express-joi-validation' leverages the Joi schema description language for defining validation rules, providing a powerful and expressive way to validate complex data structures. On the other hand, 'express-validator' is a set of middlewares that wraps around validator.js, offering a more straightforward API for common validation tasks, making it easy to implement basic validations quickly. Understanding the nuances of these packages can help developers choose the right tool for their specific validation needs.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
express-joi-validation010421.9 kB1010 months agoMIT
express-validator06,245146 kB844 months agoMIT

Feature Comparison: express-joi-validation vs express-validator

Validation Approach

  • express-joi-validation:

    Utilizes Joi, a powerful schema description language, allowing developers to define complex validation rules in a declarative manner. This approach supports nested objects, arrays, and custom validation methods, making it suitable for applications with intricate data requirements.

  • express-validator:

    Employs a middleware-based approach that wraps around validator.js, providing a set of pre-defined validation functions. It is straightforward to use for common validation tasks such as checking for required fields, email formats, and string lengths.

Error Handling

  • express-joi-validation:

    Offers detailed error messages through Joi's built-in error handling capabilities. Validation errors can be easily formatted and returned in a consistent manner, providing clear feedback to clients about what went wrong during validation.

  • express-validator:

    Provides a simple way to collect validation errors using the validationResult function. This allows developers to aggregate errors and return them in a structured format, but may require additional customization for more complex error handling.

Learning Curve

  • express-joi-validation:

    May have a steeper learning curve due to the need to understand Joi's schema definitions and how to effectively utilize its features. However, once learned, it can provide a powerful tool for complex validation scenarios.

  • express-validator:

    Generally easier to pick up for developers familiar with basic validation concepts. Its middleware approach allows for quick implementation of validation rules without needing to learn a new schema language.

Extensibility

  • express-joi-validation:

    Highly extensible, allowing developers to create custom validation methods and integrate them seamlessly into Joi schemas. This makes it adaptable to specific application needs and complex validation scenarios.

  • express-validator:

    Offers some extensibility through custom validators, but is primarily focused on using predefined validation functions. It may not be as flexible for complex validation requirements.

Performance

  • express-joi-validation:

    Performance can be impacted by the complexity of Joi schemas, especially with deeply nested objects. However, Joi is optimized for performance, and the impact is often negligible for most applications.

  • express-validator:

    Generally performs well for basic validation tasks, but may experience performance issues with a large number of validations due to the overhead of middleware processing.

How to Choose: express-joi-validation vs express-validator

  • express-joi-validation:

    Choose 'express-joi-validation' if you need a robust and flexible validation solution that can handle complex data structures and requires detailed schema definitions. It is particularly useful for applications where validation rules are intricate and need to be reused across different routes.

  • express-validator:

    Choose 'express-validator' if you prefer a simpler, more straightforward approach to validation that covers common use cases. It is ideal for projects that require quick implementation of basic validation rules without the overhead of defining extensive schemas.

README for express-joi-validation

express-joi-validation

TravisCI Coverage Status npm version TypeScript npm downloads Known Vulnerabilities

A middleware for validating express inputs using Joi schemas. Features include:

  • TypeScript support.
  • Specify the order in which request inputs are validated.
  • Replaces the incoming req.body, req.query, etc and with the validated result
  • Retains the original req.body inside a new property named req.originalBody. . The same applies for headers, query, and params using the original prefix, e.g req.originalQuery
  • Chooses sensible default Joi options for headers, params, query, and body.
  • Uses peerDependencies to get a Joi instance of your choosing instead of using a fixed version.

Quick Links

Install

You need to install joi with this module since it relies on it in peerDependencies.

npm i express-joi-validation joi --save

Example

A JavaScript and TypeScript example can be found in the example/ folder of this repository.

Usage (JavaScript)

const Joi = require('joi')
const app = require('express')()
const validator = require('express-joi-validation').createValidator({})

const querySchema = Joi.object({
  name: Joi.string().required()
})

app.get('/orders', validator.query(querySchema), (req, res) => {
  // If we're in here then the query was valid!  
  res.end(`Hello ${req.query.name}!`)
})

Usage (TypeScript)

For TypeScript a helper ValidatedRequest and ValidatedRequestWithRawInputsAndFields type is provided. This extends the express.Request type and allows you to pass a schema using generics to ensure type safety in your handler function.

import * as Joi from 'joi'
import * as express from 'express'
import {
  ContainerTypes,
  // Use this as a replacement for express.Request
  ValidatedRequest,
  // Extend from this to define a valid schema type/interface
  ValidatedRequestSchema,
  // Creates a validator that generates middlewares
  createValidator
} from 'express-joi-validation'

const app = express()
const validator = createValidator()

const querySchema = Joi.object({
  name: Joi.string().required()
})

interface HelloRequestSchema extends ValidatedRequestSchema {
  [ContainerTypes.Query]: {
    name: string
  }
}

app.get(
  '/hello',
  validator.query(querySchema),
  (req: ValidatedRequest<HelloRequestSchema>, res) => {
    // Woohoo, type safety and intellisense for req.query!
    res.end(`Hello ${req.query.name}!`)
  }
)

You can minimise some duplication by using joi-extract-type.

NOTE: this does not work with Joi v16+ at the moment. See this issue.

import * as Joi from 'joi'
import * as express from 'express'
import {
  // Use this as a replacement for express.Request
  ValidatedRequest,
  // Extend from this to define a valid schema type/interface
  ValidatedRequestSchema,
  // Creates a validator that generates middlewares
  createValidator
} from 'express-joi-validation'

// This is optional, but without it you need to manually generate
// a type or interface for ValidatedRequestSchema members
import 'joi-extract-type'

const app = express()
const validator = createValidator()

const querySchema = Joi.object({
  name: Joi.string().required()
})

interface HelloRequestSchema extends ValidatedRequestSchema {
  [ContainerTypes.Query]: Joi.extractType<typeof querySchema>

  // Without Joi.extractType you would do this:
  // query: {
  //   name: string
  // }
}

app.get(
  '/hello',
  validator.query(querySchema),
  (req: ValidatedRequest<HelloRequestSchema>, res) => {
    // Woohoo, type safety and intellisense for req.query!
    res.end(`Hello ${req.query.name}!`)
  }
)

API

Structure

createValidator(config)

Creates a validator. Supports the following options:

  • passError (default: false) - Passes validation errors to the express error hander using next(err) when true
  • statusCode (default: 400) - The status code used when validation fails and passError is false.

validator.query(schema, [options])

Creates a middleware instance that will validate the req.query for an incoming request. Can be passed options that override the config passed when the validator was created.

Supported options are:

  • joi - Custom options to pass to Joi.validate.
  • passError - Same as above.
  • statusCode - Same as above.

validator.body(schema, [options])

Creates a middleware instance that will validate the req.body for an incoming request. Can be passed options that override the options passed when the validator was created.

Supported options are the same as validator.query.

validator.headers(schema, [options])

Creates a middleware instance that will validate the req.headers for an incoming request. Can be passed options that override the options passed when the validator was created.

Supported options are the same as validator.query.

validator.params(schema, [options])

Creates a middleware instance that will validate the req.params for an incoming request. Can be passed options that override the options passed when the validator was created.

Supported options are the same as validator.query.

validator.response(schema, [options])

Creates a middleware instance that will validate the outgoing response. Can be passed options that override the options passed when the instance was created.

Supported options are the same as validator.query.

validator.fields(schema, [options])

Creates a middleware instance that will validate the fields for an incoming request. This is designed for use with express-formidable. Can be passed options that override the options passed when the validator was created.

The instance.params middleware is a little different to the others. It must be attached directly to the route it is related to. Here's a sample:

const schema = Joi.object({
  id: Joi.number().integer().required()
});

// INCORRECT
app.use(validator.params(schema));
app.get('/orders/:id', (req, res, next) => {
  // The "id" parameter will NOT have been validated here!
});

// CORRECT
app.get('/orders/:id', validator.params(schema), (req, res, next) => {
  // This WILL have a validated "id"
})

Supported options are the same as validator.query.

Behaviours

Joi Versioning

This module uses peerDependencies for the Joi version being used. This means whatever joi version is in the dependencies of your package.json will be used by this module.

Validation Ordering

Validation can be performed in a specific order using standard express middleware behaviour. Pass the middleware in the desired order.

Here's an example where the order is headers, body, query:

route.get(
  '/tickets',
  validator.headers(headerSchema),
  validator.body(bodySchema),
  validator.query(querySchema),
  routeHandler
);

Error Handling

When validation fails, this module will default to returning a HTTP 400 with the Joi validation error as a text/plain response type.

A passError option is supported to override this behaviour. This option forces the middleware to pass the error to the express error handler using the standard next function behaviour.

See the Custom Express Error Handler section for an example.

Joi Options

It is possible to pass specific Joi options to each validator like so:

route.get(
  '/tickets',
  validator.headers(
    headerSchema,
    {
      joi: {convert: true, allowUnknown: true}
    }
  ),
  validator.body(
    bodySchema,
    {
      joi: {convert: true, allowUnknown: false}
    }
  )
  routeHandler
);

The following sensible defaults for Joi are applied if none are passed:

Query

  • convert: true
  • allowUnknown: false
  • abortEarly: false

Body

  • convert: true
  • allowUnknown: false
  • abortEarly: false

Headers

  • convert: true
  • allowUnknown: true
  • stripUnknown: false
  • abortEarly: false

Route Params

  • convert: true
  • allowUnknown: false
  • abortEarly: false

Fields (with express-formidable)

  • convert: true
  • allowUnknown: false
  • abortEarly: false

Custom Express Error Handler

const validator = require('express-joi-validation').createValidator({
  // This options forces validation to pass any errors the express
  // error handler instead of generating a 400 error
  passError: true
});

const app = require('express')();
const orders = require('lib/orders');

app.get('/orders', validator.query(require('./query-schema')), (req, res, next) => {
  // if we're in here then the query was valid!
  orders.getForQuery(req.query)
    .then((listOfOrders) => res.json(listOfOrders))
    .catch(next);
});

// After your routes add a standard express error handler. This will be passed the Joi
// error, plus an extra "type" field so we can tell what type of validation failed
app.use((err, req, res, next) => {
  if (err && err.error && err.error.isJoi) {
    // we had a joi error, let's return a custom 400 json response
    res.status(400).json({
      type: err.type, // will be "query" here, but could be "headers", "body", or "params"
      message: err.error.toString()
    });
  } else {
    // pass on to another error handler
    next(err);
  }
});

In TypeScript environments err.type can be verified against the exported ContainerTypes:

import { ContainerTypes } from 'express-joi-validation'

app.use((err: any|ExpressJoiError, req: express.Request, res: express.Response, next: express.NextFunction) => {
  // ContainerTypes is an enum exported by this module. It contains strings
  // such as "body", "headers", "query"...
  if (err && 'type' in err && err.type in ContainerTypes) {
    const e: ExpressJoiError = err
    // e.g "You submitted a bad query paramater"
    res.status(400).end(`You submitted a bad ${e.type} paramater`)
  } else {
    res.status(500).end('internal server error')
  }
})