Kritim Yantra
Aug 02, 2025
Ever built an API only to realize users are sending garbage data? π€
If youβre tired of writing endless if
statements to validate requests, Joi is your new best friend.
In this guide, youβll learn:
β
How to validate ANY data type (strings, numbers, arrays, even nested objects)
β
Custom error messages (so users know why their input failed)
β
Advanced tricks (conditional validation, regex, sanitization)
By the end, youβll be a Joi validation ninjaβsaving hours of debugging bad data.
Letβs dive in!
Joi is a powerful schema validation library for JavaScript. Instead of manually checking every field in a request, you define a validation schemaβand Joi handles the rest.
Manual Validation | Joi Validation |
---|---|
if (!email.includes("@")) β Error-prone |
email: Joi.string().email() β Bulletproof |
Nested object checks = messy code | Handles deep validation easily |
Hard to maintain as app grows | Clean, reusable schemas |
First, install Joi:
npm install joi
Letβs validate a user registration:
const Joi = require('joi');
const schema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(6).required(),
age: Joi.number().integer().min(18).max(120),
});
const userInput = {
email: "test@example.com",
password: "123456",
age: 25
};
const { error, value } = schema.validate(userInput);
if (error) {
console.log(error.details); // Detailed error messages
} else {
console.log("Validation passed! β
");
}
Key Methods:
.string()
, .number()
, .boolean()
β Data type checks .required()
β Field must exist .min()
, .max()
β Length/Value limitsJoi.string()
.min(3)
.max(30)
.pattern(/^[A-Za-z]+$/) // Letters only
.trim() // Auto-trim whitespace
.lowercase() // Convert to lowercase
Joi.number()
.integer() // No decimals
.positive()
.precision(2) // 2 decimal places
// Array of strings
Joi.array().items(Joi.string())
// Nested object
Joi.object({
address: Joi.object({
street: Joi.string().required(),
city: Joi.string().required()
})
})
Joi.date()
.min('1-1-2000')
.max('now') // Must be in the past
Make errors user-friendly:
const schema = Joi.object({
email: Joi.string()
.email({ tlds: { allow: false } }) // Disallow .biz, .info etc
.required()
.messages({
'string.email': 'Please enter a valid email',
'any.required': 'Email is required!'
})
});
Output:
{
"error": "ValidationError",
"message": "Please enter a valid email"
}
"Require password confirmation only if password exists"
Joi.object({
password: Joi.string(),
confirmPassword: Joi.string().valid(Joi.ref('password'))
.when('password', {
is: Joi.exist(),
then: Joi.required()
})
})
Joi.object({
username: Joi.string()
.trim()
.replace(/[^\w]/g, '') // Remove special chars
.lowercase()
})
Joi.string().custom((value, helpers) => {
if (value === 'admin') {
return helpers.error('any.invalid'); // Block reserved words
}
return value;
})
// middleware/validate.js
const validate = (schema) => (req, res, next) => {
const { error } = schema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
next();
};
module.exports = validate;
const validate = require('./middleware/validate');
const registerSchema = Joi.object({ /* ... */ });
router.post('/register', validate(registerSchema), (req, res) => {
// Proceed if validation passes
});
Feature | Joi | Express-Validator |
---|---|---|
Schema Style | β (Cleaner) | β (Chain-based) |
Custom Errors | β | β |
Sanitization | β | β |
Express Integration | Requires middleware | Built for Express |
Use Joi if: You prefer declarative schemas or need complex validation.
schemas/
folder. Joi.assert(value, schema)
in unit tests. Joi turns validation headaches into a single, readable schema. No more:
Now itβs your turn!
π Whatβs the most complex validation youβve had to implement?
π Any Joi tricks I missed? Share below!
Happy coding! π
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google