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:
- https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
- https://56faisal.medium.com/c69f2494cf18
- 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:
- https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
- https://56faisal.medium.com/c69f2494cf18
- https://www.npmjs.com/package/class-validator
- 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:
- https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
- https://56faisal.medium.com/c69f2494cf18
- https://www.npmjs.com/package/class-validator
- 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:
- https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
- https://56faisal.medium.com/c69f2494cf18
- https://www.npmjs.com/package/class-validator
- 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:
- https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
- https://56faisal.medium.com/c69f2494cf18
- https://www.npmjs.com/package/class-validator
- 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:
- https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
- https://56faisal.medium.com/c69f2494cf18
- https://www.npmjs.com/package/class-validator
- 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:
- https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
- https://56faisal.medium.com/c69f2494cf18
- https://www.npmjs.com/package/class-validator
- 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:
- https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
- https://56faisal.medium.com/c69f2494cf18
- https://www.npmjs.com/package/class-validator
- 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:
- https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
- https://56faisal.medium.com/c69f2494cf18
- https://www.npmjs.com/package/class-validator
- 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:
- https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
- https://56faisal.medium.com/c69f2494cf18
- https://www.npmjs.com/package/class-validator
- 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:
- https://github.com/Mohammad-Faisal/professional-express-sequelize-docker-boilerplate
- https://56faisal.medium.com/c69f2494cf18
- https://www.npmjs.com/package/class-validator
- https://www.mohammadfaisal.dev/blog/error-handling-nodejs-express