Cómo elegir qué validador usar: una comparación entre Joi y express-validator

Imagine que tiene un sitio web de comercio electrónico y permite a los usuarios crear cuentas con su nombre y correo electrónico. Desea asegurarse de que se registren con nombres reales, no con algo como cool_dud3.

Ahí es donde usamos la validación para validar las entradas y asegurarnos de que los datos de entrada sigan ciertas reglas.

En el mercado, ya tenemos un montón de bibliotecas de validación, pero compararé dos bibliotecas de validación importantes: Joi y express-validator para aplicaciones basadas en express.js .

Esta comparación es útil cuando ha decidido usar la biblioteca de validación de entrada externa para su aplicación construida en expressjs y no está seguro de cuál usar.

¿Quién es qué?

Joi

Joi le permite crear planos o esquemas para objetos JavaScript (un objeto que almacena información) para garantizar la validación de la información clave.

Validador expreso

express-validator es un conjunto de middlewares express.js que envuelve las funciones de validación y desinfección de validator.js.

Entonces, por definición, podemos decir que:

  • Joi se puede usar para crear esquemas (al igual que usamos mangosta para crear esquemas NoSQL) y puede usarlo con objetos simples de Javascript. Es como una biblioteca plug and play y es fácil de usar.
  • Por otro lado, express-validator usa validator.js para validar rutas expressjs, y está construido principalmente para aplicaciones express.js. Esto hace que esta biblioteca sea más específica y proporciona validación y desinfección personalizadas listas para usar. Además, me resulta fácil de entender personalmente :)

Demasiados métodos y API para realizar cierta validación en Joi pueden hacer que se sienta abrumado, por lo que podría terminar cerrando la pestaña.

Pero puedo estar equivocado, así que dejemos de lado las opiniones y comparemos ambas bibliotecas.

Instanciación

Joi

EnJoi , necesitas usarJoi.object()para crear una instancia de un objeto de esquema Joi para trabajar.

Todos los esquemas requieren Joi.object()un proceso de validación y otras características de Joi.

Es necesario leer por separado req.body, req.params, req.queryal cuerpo de la petición, params y consulta.

const Joi = require('joi'); const schema = Joi.object().keys({ // validate fields here })

Validador expreso

Solo puede requerir un validador expreso yempezar a utilizar sus métodos. No es necesario para leer los valores de req.body, req.paramsy req.querypor separado.

Solo necesita usar los param, query, bodymétodos a continuación para validar las entradas respectivamente, como puede ver aquí:

const { param, query, cookies, header body, validationResult } = require('express-validator/check') app.post('/user', [ // validate fields here ], (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(422).json({ errors: errors.array() }); } }

Se requiere campo

Tomemos un ejemplo muy básico en el que queremos asegurarnos de que usernamese requiera a stringy esté alphaNumericcon los caracteres miny max.

  • Joi:
const Joi = require('joi'); const schema = Joi.object().keys({ username: Joi.string().alphanum().min(3).max(30).required() }) app.post('/user', (req, res, next) => { const result = Joi.validate(req.body, schema) if (result.error) { return res.status(400).json({ error: result.error }); } });
  • Validador expreso
const { body, validationResult } = require('express-validator/check') app.post('/user', [ body('username') .isString() .isAlphanumeric() .isLength({min: 3, max: 30}) .exists(), ], (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(422).json({ errors: errors.array() }); } }

Higienización

La desinfección consiste básicamente en verificar la entrada para asegurarse de que esté libre de ruido, por ejemplo, todos hemos usado una .trim()cadena para eliminar espacios.

O si se ha enfrentado a una situación en la que aparece un número como "1"tal en esos casos, queremos desinfectar y convertir el tipo durante el tiempo de ejecución.

Lamentablemente, Joi no proporciona desinfección de fábrica, pero express-validator sí.

Ejemplo: conversión al ObjectID de MongoDB

const { sanitizeParam } = require('express-validator/filter'); app.post('/object/:id', sanitizeParam('id') .customSanitizer(value => { return ObjectId(value); }), (req, res) => { // Handle the request });

Validación personalizada

Joi: .extend ( extension)

Esto crea una nueva instancia de Joi personalizada con las extensiones que proporciones incluidas.

La extensión hace uso de algunas estructuras comunes que deben describirse primero:

  • value - el valor que está procesando Joi.
  • state - un objeto que contiene el contexto actual de validación.
  • key - la clave del valor actual.
  • path - la ruta completa del valor actual.
  • parent - el padre potencial del valor actual.
  • options- objeto de opciones proporcionado a través de any().options()o Joi.validate().

Extensión

extension puede ser:

  • un solo objeto de extensión
  • una función de fábrica que genera un objeto de extensión
  • o una variedad de esos

Los objetos de extensión utilizan los siguientes parámetros:

  • name- nombre del nuevo tipo que está definiendo, puede ser un tipo existente. Necesario.
  • base- un esquema Joi existente en el que basar su tipo. Por defecto es Joi.any().
  • coerce- una función opcional que se ejecuta antes de la base, generalmente sirve cuando desea coaccionar valores de un tipo diferente al de su base. Toma 3 argumentos value, statey options.
  • pre- una función opcional que se ejecuta primero en la cadena de validación, generalmente sirve cuando necesita emitir valores. Toma 3 argumentos value, statey options.
  • language- un objeto opcional para agregar definiciones de error. Cada clave tendrá el prefijo del nombre del tipo.
  • describe - una función opcional que toma la descripción completa para procesarla posteriormente.
  • rules - una matriz opcional de reglas para agregar.
  • name- nombre de la nueva regla. Necesario.
  • params- un objeto opcional que contiene esquemas Joi de cada parámetro ordenado. También puede pasar un solo esquema Joi siempre que sea un Joi.object(). Por supuesto, algunos métodos como patterno renameno serán útiles o no funcionarán en absoluto en este contexto dado.
  • setup- una función opcional que toma un objeto con los parámetros proporcionados para permitir la manipulación interna del esquema cuando se establece una regla. Opcionalmente, puede devolver un nuevo esquema Joi que se tomará como la nueva instancia de esquema. Se debe proporcionar al menos uno de los dos setupo validate.
  • validate - an optional function to validate values that takes 4 parameters params, value, state and options. At least one of setup or validate must be provided.
  • description - an optional string or function taking the parameters as an argument to describe what the rule is doing.

Example:

joi.extend((joi) => ({ base: joi.object().keys({ name: joi.string(), age: joi.number(), adult: joi.bool().optional(), }), name: 'person', language: { adult: 'needs to be an adult', }, rules: [ { name: 'adult', validate(params, value, state, options) { if (!value.adult) { // Generate an error, state and options need to be passed return this.createError('person.adult', {}, state, options); } return value; // Everything is OK } } ] })

Express-validator

A custom validator may be implemented by using the chain method .custom(). It takes a validator function.

Custom validators may return Promises to indicate an async validation (which will be awaited upon), or throw any value/reject a promise to use a custom error message.

const { param, query, cookies, header body, validationResult } = require('express-validator/check') app.get('/user/:userId', [ param('userId') .exists() .isMongoId() .custom(val => UserSchema.isValidUser(val)), ], (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(422).json({ errors: errors.array() }); } }

Conditional Validation

express-validator does not support conditional validation as of now, but there is a PR for that already you can check //github.com/express-validator/express-validator/pull/658

Let’s see how it works in Joi:

any.when(condition, options)

any:Generates a schema object that matches any data type.

const schema = Joi.object({ a: Joi.any().valid('x'), b: Joi.any() }).when( Joi.object({ b: Joi.exist() }) .unknown(), { then: Joi.object({ a: Joi.valid('y') }), otherwise: Joi.object({ a: Joi.valid('z') }) });

alternatives.when(condition, options)

Adds a conditional alternative schema type, either based on another key (not the same as any.when()) value, or a schema peeking into the current value, where:

  • condition - the key name or reference, or a schema.
  • options - an object with:
  • is - the required condition joi type. Forbidden when condition is a schema.
  • then - the alternative schema type to try if the condition is true. Required if otherwise is missing.
  • otherwise - the alternative schema type to try if the condition is false. Required if then is missing.
const schema = Joi .alternatives() .when(Joi.object({ b: 5 }).unknown(), { then: Joi.object({ a: Joi.string(), b: Joi.any() }), otherwise: Joi.object({ a: Joi.number(), b: Joi.any() }) });

Nested Validation

When you want to validate an array of objects/items or just object keys

Both libraries support nested validation

Now what about express-validator?

Wildcards

Wildcards allow you to iterate over an array of items or object keys and validate each item or its properties.

The * character is also known as a wildcard.

const express = require('express'); const { check } = require('express-validator/check'); const { sanitize } = require('express-validator/filter'); const app = express(); app.use(express.json()); app.post('/addresses', [ check('addresses.*.postalCode').isPostalCode(), sanitize('addresses.*.number').toInt() ], (req, res) => { // Handle the request });

Joi

const schema = Joi.object().keys({ addresses: Joi.array().items( Joi.object().keys({ postalCode: Joi.string().required(), }), ) });

Custom Error Messages

Joi

any.error(err, [options])

Overrides the default joi error with a custom error

let schema = Joi.string().error(new Error('Was REALLY expecting a string'));

Express-validator

const { check } = require('express-validator/check'); app.post('/user', [ // ...some other validations... check('password') .isLength({ min: 5 }).withMessage('must be at 5 chars long') .matches(/\d/).withMessage('must contain a number') ], (req, res) => { // Handle the request somehow });

Conclusion

I covered the most important parts of both libraries and you can decide yourself which one you want to use. Please let me know in the comments below if I left out anything important in the comparison.

I hope you find it helpful when deciding the next input validation module for your express.js application.

I wrote an in-depth article on it here: how to validate inputs. Do check it out.

Don’t hesitate to clap if you considered this a worthwhile read!

Originally published at 101node.io on March 31, 2019.