Data Validation in Node.js with Joi
In modern web applications on Node.js, data validation is a critically important stage of request processing. Errors in data can lead to vulnerabilities, incorrect operation, or server crashes. One of the most powerful and popular libraries for this task is Joi (pronounced "joy"). It allows you to describe data schemas and check their compliance with specified rules.
What is Joi?
Joi is a library for describing schemas and validating JavaScript objects. It is especially popular in the Node.js ecosystem, where it is often used together with Express, Hapi, or Fastify. Joi provides a simple and expressive API for specifying types, required fields, ranges, regular expressions, and custom checks.
Installation
Installing Joi via npm:
npm install joiBasic Example
Let's create a schema for validating user data:
const Joi = require('joi');
const schema = Joi.object({ username: Joi.string() .alphanum() .min(3) .max(30) .required(), password: Joi.string() .pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')), email: Joi.string() .email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } })});
const data = { username: 'user123', password: 'pass123', email: 'user@example.com'};
const { error, value } = schema.validate(data);
if (error) { console.log('Validation error:', error.details);} else { console.log('Data is valid:', value);}Basic Methods and Rules
Joi supports many types and modifiers:
- Data types:
Joi.string(),Joi.number(),Joi.boolean(),Joi.date(),Joi.array(),Joi.object(). - Required:
.required()— field is mandatory. - Constraints:
.min(),.max(),.length()— for strings and arrays. - Regular expressions:
.pattern(regex)— pattern matching. - Email and URL:
.email(),.uri()— built-in validators. - Custom messages:
.messages()— override default errors.
Validating Nested Objects and Arrays
Joi handles complex structures well:
const addressSchema = Joi.object({ street: Joi.string().required(), city: Joi.string().required(), zip: Joi.string().pattern(/^\\d{5}$/)});
const userSchema = Joi.object({ name: Joi.string().required(), age: Joi.number().integer().min(18).max(120), addresses: Joi.array().items(addressSchema).min(1)});
const userData = { name: 'Anna', age: 25, addresses: [ { street: 'Nevsky Ave.', city: 'SPb', zip: '191011' } ]};
const result = userSchema.validate(userData);console.log(result.error ? result.error.details : 'OK');Error Handling
On validation error, Joi returns an error.details object — an array with a detailed description of each error:
if (result.error) { result.error.details.forEach(detail => { console.log(`Field: ${detail.path.join('.')}, message: ${detail.message}`); });}Using with Express
Joi is often used in middleware for request validation:
const express = require('express');const Joi = require('joi');const app = express();
app.use(express.json());
const validateUser = (req, res, next) => { const schema = Joi.object({ name: Joi.string().required(), email: Joi.string().email().required() }); const { e