Mohammad Faisal

How to Validate Incoming Requests in ExpressJS

Protect your application from invalid requests.

Add to Pieces

Security is a great concern for any backend application. And one of the most important things is not letting bad things get into the application in the first place.

Today we will learn how to validate incoming requests in an express application.

Manual Validation of Incoming Requests

There are manual ways to do it by checking every parameter of the body and making sure that they are valid.

const { body } = req;
if (!body.name && body.name.length === 0) {
  throw new Error(400, "Name is required");
}
// do this for every required key

Tags: 400, javascript, node.js

Related links:

  1. https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
  2. https://56faisal.medium.com/c69f2494cf18
  3. https://www.npmjs.com/package/class-validator

Get a Basic Express Application with Error Handling Setup

Tags: express, git

Git clone the repository to get a basic application with error handling already setup to start the project.

git clone https://github.com/Mohammad-Faisal/nodejs-expressjs-error-handling

Related links:

  1. https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
  2. https://56faisal.medium.com/c69f2494cf18
  3. https://www.npmjs.com/package/class-validator
  4. https://www.mohammadfaisal.dev/blog/error-handling-nodejs-express

Add the Required Dependencies

Tags: yarnpgk, npm, webpack

Install the required dependencies class-validator and class-transformer.

yarn add class-validator class-transformer

Related links:

  1. https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
  2. https://56faisal.medium.com/c69f2494cf18
  3. https://www.npmjs.com/package/class-validator
  4. https://www.mohammadfaisal.dev/blog/error-handling-nodejs-express

Removing the Warning for Adding Decorators

Tags: decorators, javascript, config

You will get a warning for adding decorators. Open up your tsconfig.json file and add the following value to get rid of that warning.

"compilerOptions": {
  "experimentalDecorators": true
},

Related links:

  1. https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
  2. https://56faisal.medium.com/c69f2494cf18
  3. https://www.npmjs.com/package/class-validator
  4. https://www.mohammadfaisal.dev/blog/error-handling-nodejs-express

Add CreateUserRequest.ts File

Tags: typescript, decorators

Adding a decorator for our CreateUserRequest class inside of the file.

import { IsDefined } from 'class-validator';
export class CreateUserRequest {
  @IsDefined()
  userName!: string;
}

Related links:

  1. https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
  2. https://56faisal.medium.com/c69f2494cf18
  3. https://www.npmjs.com/package/class-validator
  4. https://www.mohammadfaisal.dev/blog/error-handling-nodejs-express

Convert the JSON Request Body Into Class Using class-transformer

Tags: errors, converter, typescript

We know that in a post request, the request body comes in as a JSON object. We first need to convert that into a class.

import { plainToInstance, Expose } from "class-transformer";
import { validate, Matches, IsDefined } from "class-validator";
const converterObject = plainToInstance(req.body);
validate(convertedObject).then((errors) => {
  console.log(errors);
});

Related links:

  1. https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
  2. https://56faisal.medium.com/c69f2494cf18
  3. https://www.npmjs.com/package/class-validator
  4. https://www.mohammadfaisal.dev/blog/error-handling-nodejs-express

Add Code to a Route

Tags: 200, request, errors, typescript

You will notice we are passing the error handling capability into the error handling middleware by calling the next() function. We are also using a custom Error object named `BadRequest`` error.

app.post("/create-user", async (req: Request, res: Response, next: NextFunction) => {
  const convertedObject = plainToInstance(CreateUserRequest, req.body);
  validate(convertedObject).then((errors) => {
    if (errors.length > 0) {
      let rawErrors: string[] = [];
      for (const errorItem of errors) {
        rawErrors = rawErrors.concat(...rawErrors, Object.values(errorItem.constraints ?? []));
      }
      const validationErrorText = "Request validation failed!";
      next(new BadRequestError(validationErrorText, rawErrors));
    } else {
      res.status(200).send({
        message: "Hello World from create user!",
      });
    }
  });
});

Related links:

  1. https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
  2. https://56faisal.medium.com/c69f2494cf18
  3. https://www.npmjs.com/package/class-validator
  4. https://www.mohammadfaisal.dev/blog/error-handling-nodejs-express

Add New Parameter Named rawErrors

Tags: status, error, angular, typescript

Add rawErrors into our ApiError class which is our custom error class, to add the list of errors.

export class ApiError extends Error {
  statusCode: number;
  rawErrors: string[] = [];
  constructor(statusCode: number, message: string, rawErrors?: string[]) {
    super(message);
    this.statusCode = statusCode;
    if (rawErrors) this.rawErrors = rawErrors;
    Error.captureStackTrace(this, this.constructor);
  }
}
// new optional class for bad requests
export class BadRequestError extends ApiError {
  constructor(message: string, errors: string[]) {
    super(StatusCodes.BAD_REQUEST, message, errors);
  }
}

Related links:

  1. https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
  2. https://56faisal.medium.com/c69f2494cf18
  3. https://www.npmjs.com/package/class-validator
  4. https://www.mohammadfaisal.dev/blog/error-handling-nodejs-express

Invalid Response from Body Without userName

Tags: request, json

If you request the http://localhost:3001/create-user URL with a body without userName you will receive this response.

{
  "success": false,
  "message": "Request validation failed!",
  "rawErrors": ["userName should not be null or undefined"]
}

Related links:

  1. https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
  2. https://56faisal.medium.com/c69f2494cf18
  3. https://www.npmjs.com/package/class-validator
  4. https://www.mohammadfaisal.dev/blog/error-handling-nodejs-express

Create Request Validator Class

Tags: validator, typescript, angular

Now we can validate our incoming requests very easily. Let’s make our code better by creating a RequestValidator class.

import { Request, Response, NextFunction } from 'express';
import { ClassConstructor, plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';
import { BadRequestError } from './ApiError';
export default class RequestValidator {
  static validate = (classInstance: ClassConstructor) => {
    return async (req: Request, res: Response, next: NextFunction) => {
      const convertedObject = plainToInstance(classInstance, req.body);
      await validate(convertedObject).then((errors) => {
        if (errors.length > 0) {
          let rawErrors: string[] = [];
          for (const errorItem of errors) {
            rawErrors = rawErrors.concat(...rawErrors, Object.values(errorItem.constraints ?? []));
          }
          const validationErrorText = 'Request validation failed!';
          console.log('error found!', rawErrors);
          next(new BadRequestError(validationErrorText, rawErrors));
        }
      });
      next();
    };
  };
}

Related links:

  1. https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
  2. https://56faisal.medium.com/c69f2494cf18
  3. https://www.npmjs.com/package/class-validator
  4. https://www.mohammadfaisal.dev/blog/error-handling-nodejs-express

Using Validator Logic Inside of the Routes

Tags: typescript, express, 200

Now we can just create our validation objects and pass them to our route handlers to validate incoming requests.

app.post("/create-user", RequestValidator.validate(CreateUserRequest), async (req: Request, res: Response) => {
  res.status(200).send({
    message: "Hello World from post!",
  });
});

Related links:

  1. https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
  2. https://56faisal.medium.com/c69f2494cf18
  3. https://www.npmjs.com/package/class-validator
  4. https://www.mohammadfaisal.dev/blog/error-handling-nodejs-express