Custom validation rules help you check user input in ways built-in checks can't. This keeps your app safe and working right.
Custom validation rules in Express
Start learning this pattern below
Jump into concepts and practice - no test required
or
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Introduction
Syntax
Express
const { body, validationResult } = require('express-validator');
app.post('/route', [
body('fieldName').custom(value => {
if (!yourCheck(value)) {
throw new Error('Custom error message');
}
return true;
})
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
res.send('Success');
});The custom method takes a function that returns true if valid or throws an error if invalid.
Always return true when validation passes to signal success.
Examples
Express
body('age').custom(value => { if (value < 18) { throw new Error('Must be at least 18'); } return true; })
Express
body('username').custom(async value => { const userExists = await checkUserInDatabase(value); if (userExists) { throw new Error('Username already taken'); } return true; })
Sample Program
This Express app has a route '/register' that checks if the password includes at least one number. If not, it sends an error. Otherwise, it confirms success.
Express
const express = require('express'); const { body, validationResult } = require('express-validator'); const app = express(); app.use(express.json()); // Custom validation to check if password has a number app.post('/register', [ body('password').custom(value => { if (!/\d/.test(value)) { throw new Error('Password must contain a number'); } return true; }) ], (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } res.send('Registration successful'); }); app.listen(3000);
Important Notes
Custom validators can be synchronous or asynchronous (return a promise).
Always handle validation errors by checking validationResult(req).
Use clear error messages to help users fix input mistakes.
Summary
Custom validation rules let you check inputs beyond built-in checks.
Use custom() with a function that throws errors on invalid input.
Always return true when validation passes.
Practice
1. What is the main purpose of using
custom() in Express validation?easy
Solution
Step 1: Understand the role of
Thecustom()custom()method allows you to write your own validation logic beyond built-in checks.Step 2: Identify the purpose in input validation
It is used to check inputs with rules you define, like checking a password strength or a special format.Final Answer:
To create your own rules for checking input values -> Option DQuick Check:
Custom validation = custom rules [OK]
Hint: Custom means you write your own check function [OK]
Common Mistakes:
- Thinking custom() sanitizes inputs automatically
- Confusing custom() with response formatting
- Assuming custom() connects to databases
2. Which of the following is the correct syntax to add a custom validation rule using Express Validator?
easy
Solution
Step 1: Review correct custom validation syntax
The function insidecustom()should throw an error if validation fails and return true if it passes.Step 2: Analyze each option
check('age').custom(value => { if(value < 18) throw new Error('Too young'); return true; }) throws an error when value is less than 18 and returns true otherwise, which is correct. check('age').custom(value => value < 18 ? true : false) returns true when value is less than 18, which is opposite logic. check('age').custom(value => { return false; }) always returns false, which fails validation. check('age').custom(value => { throw 'Error'; }) throws an error unconditionally, so it always fails.Final Answer:
check('age').custom(value => { if(value < 18) throw new Error('Too young'); return true; }) -> Option AQuick Check:
Throw error on fail, return true on pass [OK]
Hint: Throw error to fail, return true to pass [OK]
Common Mistakes:
- Returning false instead of throwing error
- Throwing error without condition
- Returning true on invalid input
3. Given this code snippet, what will be the validation result if
req.body.username is "abc"?
check('username').custom(value => {
if(value.length < 5) throw new Error('Too short');
return true;
})medium
Solution
Step 1: Check the input value length
The input "abc" has length 3, which is less than 5.Step 2: Apply the custom validation logic
The function throws an error 'Too short' if length is less than 5, so it throws an error here causing validation to fail.Final Answer:
Validation fails with 'Too short' error -> Option AQuick Check:
Input too short = error thrown [OK]
Hint: Check input length against condition in custom() [OK]
Common Mistakes:
- Assuming validation passes for short input
- Confusing error throwing with warnings
- Expecting syntax errors from valid code
4. Identify the error in this custom validation code:
check('email').custom(value => {
if(!value.includes('@'))
return new Error('Invalid email');
return true;
})medium
Solution
Step 1: Understand error signaling in custom validation
Custom validators must throw an error to indicate failure, not return an Error object.Step 2: Analyze the given code
The code returnsnew Error('Invalid email')instead of throwing it, so validation will not fail as expected.Final Answer:
It should throw an error, not return it -> Option CQuick Check:
Throw error to fail validation [OK]
Hint: Throw errors, don't return them in custom() [OK]
Common Mistakes:
- Returning Error object instead of throwing
- Checking wrong condition for email
- Returning false instead of throwing error
5. You want to create a custom validation rule that checks if a password contains at least one uppercase letter, one number, and is at least 8 characters long. Which of these implementations correctly achieves this?
hard
Solution
Step 1: Check each condition with proper error throwing
check('password').custom(value => { if(!/[A-Z]/.test(value)) throw new Error('Missing uppercase'); if(!/\d/.test(value)) throw new Error('Missing number'); if(value.length < 8) throw new Error('Too short'); return true; }) checks each condition separately and throws a specific error if it fails, returning true only if all pass.Step 2: Compare other options for correctness
check('password').custom(value => { if(value.length < 8) return false; if(!/[A-Z]/.test(value)) return false; if(!/\d/.test(value)) return false; return true; }) returns false instead of throwing errors, which is incorrect. check('password').custom(value => { if(value.length < 8) throw 'Too short'; if(!/[A-Z]/.test(value)) throw 'Missing uppercase'; if(!/\d/.test(value)) throw 'Missing number'; return false; }) throws string errors and returns false at the end, which breaks the rule of returning true on success. check('password').custom(value => { if(value.length >= 8 && /[A-Z]/.test(value) && /\d/.test(value)) return true; else return false; }) returns false instead of throwing an error if conditions fail, which is incorrect.Final Answer:
check('password').custom(value => { if(!/[A-Z]/.test(value)) throw new Error('Missing uppercase'); if(!/\d/.test(value)) throw new Error('Missing number'); if(value.length < 8) throw new Error('Too short'); return true; }) -> Option BQuick Check:
Throw specific errors, return true if all pass [OK]
Hint: Throw specific errors for each fail, return true if all pass [OK]
Common Mistakes:
- Returning false instead of throwing errors
- Throwing strings instead of Error objects
- Returning false on success
