Validating requests using validation pipes in Typescript
Tags: webdev, node, typescript, express
Published: 2022-11-13 10:29:14

When building APIs, validation of the request body is a must-have. One of the popular tools for validation in Node.js is JOI. I've used Joi for years, and it gets the job done. Recently, I've been exploring other efficient and cleaner ways of archiving this, especially with TS. If you've used Nest JS before, you might have stumbled on validation pipes, enabling you to implement validation using your DTOs and validation decorators. I'll look into how we can archive this in our projects.

Please note that the focus of this post is not folder structure. Instead, it is on how to validate requests. So I'm using a flat folder system in this post. Feel free to structure and nest your files as you please.

We'll start by installing two libraries we will use, class-transformers and class-validator. Class-transformer will transform our class-based dto or types into an object and map them to the request body. Class-validator will make it possible to add validation rules using decorators.

```sh
npm i class-validator class-tranformer
```
An assumption is that you're using express in building your API. Next, we'll create a file called validation.ts

```bash
touch validation.ts
```
Add the following code snippet to the just created file.

```js
import { plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';
export * from 'class-validator';

export const validationPipe = async (schema: new () => {}, requestObject: object) => {
  const transformedClass: any = plainToInstance(schema, requestObject);
  const errors = await validate(transformedClass);
  if (errors.length > 0) {
    return errors;
  }
  return true;
};
```
From the above snippet, we have a function that takes two parameters, the schema or dto or class-based types and the request object. First, we use `plainToInstace` to convert and map our dto into an object we can validate. Next, we use the validate function from the class-validator library to validate the request and return the errors, if any.

Simple right? Now we can use this function in our Node.js API server.

```js
...
import { validationPipe, IsString, IsNumber } from './validation';

class bookDto {
  @IsString()
  name: string;
  @IsNumber()
  year: number;
  @IsNumber
  price: number;
}

app.post('/books', async (req, res) => {
  const result = await validationPipe(booksDto, { ...req.body })
  if(result.errors) {
     res.status(400).json({ ...result })
  }
});
```
This way, we can use our class-based types for validating requests and type checking, making our code cleaner and more maintainable than using a separate schema for validation.

We can take it a step forward and use it as a middleware function.

## Using as a middleware
We could use the validation pipe as middleware, meaning cleaner code. To archive this, we'll create a new file named validate.ts or any other name you prefer to use.

```bash
touch validate.ts
```
Add the code snippet below into the file
```js
...
import { validationPipe, IsString, IsNumber } from './validation';

export const validationMiddleware (validationSchema) => async (req, res, next) => {
    const result: any = await validationPipe(validationSchema, { ...req.body, ...req.params }, { pretty: false });
      if (result.errors) {
        console.log(result);
        return res.status(400).json({
          success: false,
          ...result,
        });
      }

      next();
      return true;
}; 
```
We can now use our newly created middleware in our app.

```js
import { validationMiddleware } from './validate.ts';
import { validationPipe, IsString, IsNumber } from './validation';

class UserDto {
  id: number;
  @IsString()
  firstName: string;
  @IsString()
  @MaxLength(4)
  lastName: string;

  @IsBoolean()
  @IsOptional()
  verified: boolean;
}
...
app.post('/', validationMiddleware(UserDto), (req, res) => {
  ...
  # Your code goes here.
});
```
I've combined all the code in this post into an npm package called [pipe-validation](https://www.npmjs.com/package/pipe-validation). Like & comment if love the post or think there's any improvement that can be made.