Validation in expressjs
medium · Express.js — Web APIs & middleware
Schema validation is the defensive wall of your application . It ensures that before your Service Layer ever touches a piece of data, that data has been strictly vetted for type, length, and format . Schema Validation: Theory and Best Practices 1. The Core Theory: Data Integrity & Security Theory: Without validation, your server is "blind" . If a user sends a string where your database expects a number, your app may crash or, worse, become vulnerable to injection attacks . Fail Fast: Validation allows the request to "fail fast" at the Middleware level . This saves server resources because the request never reaches the Controller or Database if the data is malformed . Declarative vs. Imperative: Instead of writing dozens of if (req.body.name === undefined) checks (Imperative), you define a Schema (Declarative) that describes what the data should look like . 2. Using Joi: The Validation Engine Theory: Joi allows you to build a blueprint for your JavaScript objects . Chaining: You can chain rules (e.g., .string().min(3).required() ) to create complex constraints in a single line . Type Safety: Joi automatically checks if the input is the correct type (string, number, array, etc.) and can even perform "coercion" (turning a string "18" into a number 18) . Implementation: The Schema Blueprint const Joi = require('joi'); const userSchema = Joi.object({ name: Joi.string().min(3).required(), email: Joi.string().email().required(), age: Joi.number().min(18), role: Joi.string().valid('user', 'admin'), // Limits input to specific values tags: Joi.array().items(Joi.string()).min(1) // Ensures array has at least one string }); 3. The Validation Middleware (The Gatekeeper) Theory: To keep your Controllers "thin" and focused on coordination, validation should live in a reusable Middleware . This middleware acts as a gatekeeper in the Request Lifecycle . The Execution Flow: Client: Sends a POST request with data . Middleware: Intercepts the request and runs schema.validate(req.body) . Decision: Success: Calls next() , allowing the Controller to take over . Failure: Returns a 400 Bad Request immediately with a clear error message . The Process: const validate = (schema) => (req, res, next) => { const { error } = schema.validate(req.body, { abortEarly: false }); // abortEarly: false shows ALL errors, not just the first if (error) { const errorMessage = error.details.map(detail => detail.message).join(', '); return res.status(400).json({ success: false, message: errorMessage });[cite: 1] } next();[cite: 1] }; 4. Why Use It in Routes? Theory: By placing validate(schema) inside your route definition, you make the requirements for that endpoint explicit and easy to read . router.post( '/register', validate(userSchema), // Theory: The request must pass this check to reach the next function userController.register ); .