@th3hero/request-validator
Version:
🚀 Lightweight, fast & flexible request validation library for Node.js/Express/Next.js with TypeScript support. Features 20+ validation rules, database integration, file upload validation, and zero external dependencies. Perfect for API validation, form v
238 lines • 11.3 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateInput = exports.getDatabase = exports.setDatabase = void 0;
const fs_1 = __importDefault(require("fs"));
const validators_1 = require("./utils/validators");
let dbPool = null;
const setDatabase = (pool) => {
dbPool = pool;
};
exports.setDatabase = setDatabase;
const getDatabase = () => dbPool;
exports.getDatabase = getDatabase;
/**
* Validate the input provided by the user according to a set of validation rules.
* @param req - The Express request object
* @param rules - An object containing the validation rules for each input field
* @returns Promise<ValidationResult> - An object containing validation status and errors
*/
const validateInput = async (req, rules) => {
const errors = {};
const db = (0, exports.getDatabase)();
for (const [field, rule] of Object.entries(rules)) {
const value = req.body[field];
const ruleParts = Array.isArray(rule) ? rule : rule.split('|');
for (const part of ruleParts) {
const [ruleName, ...params] = part.split(':');
switch (ruleName) {
case 'file': {
if (!req.files || !req.files[field] || req.files[field].length === 0) {
errors[field] = `${field} is required`;
}
break;
}
case 'mimetype': {
if (req.files && req.files[field] && req.files[field].length > 0) {
const allowedMimetypes = params[0].split(',');
const files = Array.isArray(req.files[field]) ? req.files[field] : [req.files[field]];
for (const file of files) {
if (!allowedMimetypes.includes(file.mimetype)) {
errors[field] =
`Invalid file format for ${field}. Supported media types are ${allowedMimetypes.join(', ')}`;
try {
await new Promise((resolve, reject) => {
fs_1.default.unlink(file.path, (err) => {
if (err)
reject(err);
else
resolve();
});
});
}
catch (error) {
// Ignore file cleanup errors
}
break;
}
}
}
break;
}
case 'required':
if (value === null || value === undefined || (typeof value === 'string' && value.trim() === '')) {
errors[field] = `${field} is required`;
}
break;
case 'not-empty':
if (value === '' || value === null || value === undefined) {
errors[field] = `${field} cannot be empty`;
}
break;
case 'digits':
if (value.length !== parseInt(params[0])) {
errors[field] = `${field} must be exactly ${params[0]} digits long`;
}
break;
case 'min':
if (value && value.length < parseInt(params[0])) {
errors[field] = `${field} must be at least ${params[0]} characters long`;
}
break;
case 'max':
if (value && value.length > parseInt(params[0])) {
errors[field] = `${field} must not exceed ${params[0]} characters`;
}
break;
case 'unique':
if (!db) {
throw new Error('Database connection not set. Please install mysql package and set up database connection using setDatabase()');
}
if (value) {
const [table, column] = params[0].split(',');
await new Promise((resolve, reject) => {
db.query(`SELECT COUNT(*) as count
FROM ${table}
WHERE ${column} = ?`, [value], (err, results) => {
if (err)
reject(err);
else if (results[0].count > 0) {
errors[field] = `${field} already exists`;
}
resolve(null);
});
});
}
break;
case 'exists':
if (!db) {
throw new Error('Database connection not set. Please install mysql package and set up database connection using setDatabase()');
}
if (value) {
const [table, column] = params[0].split(',');
await new Promise((resolve, reject) => {
db.query(`SELECT COUNT(*) as count
FROM ${table}
WHERE ${column} = ?`, [value], (err, results) => {
if (err)
reject(err);
else if (results[0].count === 0) {
errors[field] = `${field} does not exist`;
}
resolve(null);
});
});
}
break;
case 'email':
if (value && !(0, validators_1.isEmail)(value)) {
errors[field] = `Invalid email address for ${field}`;
}
break;
case 'date':
if (value && !(0, validators_1.isDate)(value, params[0])) {
errors[field] =
`${field} must be a valid date with format ${params[0] || 'YYYY-MM-DD'}`;
}
break;
case 'in':
if (value && !params[0].split(',').includes(value)) {
errors[field] =
`${field} must be one of the following values: ${params[0].split(',').join(', ')}`;
}
break;
case 'nullable':
if (value === null || value === undefined) {
break;
}
break;
case 'required_if': {
// params[0] = 'type,business'
if (!params[0] || params[0].split(',').length < 2) {
errors[field] = 'Invalid required_if rule format';
break;
}
const [conditionField, conditionValue] = params[0].split(',');
if (req.body[conditionField] === conditionValue && (!value || value === '')) {
errors[field] = `${field} is required when ${conditionField} is ${conditionValue}`;
}
break;
}
case 'string':
if (value !== null && value !== undefined && typeof value !== 'string') {
errors[field] = `${field} must be a string`;
}
break;
case 'integer':
if (value !== null && value !== undefined && !Number.isInteger(Number(value))) {
errors[field] = `${field} must be an integer`;
}
break;
case 'boolean':
if (value !== null && value !== undefined && typeof value !== 'boolean') {
errors[field] = `${field} must be a boolean`;
}
break;
case 'regex':
if (value && params[0]) {
// Remove leading and trailing slashes if present
const pattern = params[0].replace(/^\/|\/$/g, '');
if (!new RegExp(pattern).test(value)) {
errors[field] = `${field} format is invalid`;
}
}
break;
case 'url':
if (value && !(0, validators_1.isURL)(value)) {
errors[field] = `${field} must be a valid URL`;
}
break;
case 'alpha':
if (value && !/^[a-zA-Z]+$/.test(value)) {
errors[field] = `${field} must contain only letters`;
}
break;
case 'alphanumeric':
if (value && !/^[a-zA-Z0-9]+$/.test(value)) {
errors[field] = `${field} must contain only letters and numbers`;
}
break;
case 'array':
if (value && !Array.isArray(value)) {
errors[field] = `${field} must be an array`;
}
break;
case 'object':
if (value && (typeof value !== 'object' || Array.isArray(value))) {
errors[field] = `${field} must be an object`;
}
break;
case 'phone':
if (value && !/^\+?[1-9]\d{1,14}$/.test(value)) {
errors[field] = `${field} must be a valid phone number`;
}
break;
default:
if (req.customValidators && req.customValidators[ruleName]) {
const result = req.customValidators[ruleName](value, req);
if (result !== true) {
errors[field] = typeof result === 'string' ? result : `${field} validation failed`;
}
}
else if (ruleName !== '') {
errors[field] = `Custom validator ${ruleName} not found`;
}
}
if (errors[field])
break;
}
}
return {
failed: Object.keys(errors).length > 0,
errors: Object.keys(errors).length > 0 ? errors : null,
};
};
exports.validateInput = validateInput;
//# sourceMappingURL=validator.js.map