Skip to main content

Validation

Introduction

It's best practice to validate the incoming data, therefore you ensure that it complies with the requirements of your application. We provide several built-in pipes to validate incoming values. However, we are only going to focus on the ValidationPipe here. You can read about the others in Pipes section.

class vs interface

If you're a TypeScript user, in theory you can use interfaces to determine your request body shapes, but since interfaces are removed during compile time, it's not possible to gather information about them. On the other hand, classes are part of the ES6 standard which means they don't get removed during compile time, therefore they exist in the compiled Javascript and we can collect information about them and their properties, which ValidationPipe heavily relies on.

ValidationPipe

The ValidationPipe uses class-validator to validate and class-transformer to transform the incoming request body.

Usage

We first create our CreateUserDTO DTO (data transfer object) to define the rules we need.

import { IsNotEmpty, IsEmail } from 'class-validator';

export class CreateUserDTO {
@IsEmail()
email: string;

@IsNotEmpty()
fullName: string;
}

And, later we make use of the CreateUserDTO in conjunction with ValidationPipe in our route handler.

// pages/api/user.ts
class UserHandler {
@Post()
async createUser(@Body(ValidationPipe) body: CreateUserDTO) {
return await DB.createUser(body);
}
}

export default createHandler(UserHandler);

Nested data

When your application expects a nested JSON object, you can easily define its shape in your DTOs and validate the incoming data against it.

import { createHandler, Body, Post, ValidationPipe } from 'next-api-decorators';
import { Type } from 'class-transformer';
import { IsNotEmpty, IsNumber, MinLength, ValidateNested } from 'class-validator';

class Coordinate {
@IsNotEmpty()
@IsNumber()
lat: number;

@IsNotEmpty()
@IsNumber()
lng: number;
}

class MapMarker {
@IsNotEmpty()
@MinLength(3)
label: string;

@Type(() => Coordinate)
@ValidateNested()
@IsNotEmpty()
coordinates: Coordinate;
}

class LocationHandler {
@Post()
saveLocation(@Body(ValidationPipe) body: MapMarker) {
// Do something with the data.
return `Location "${body.label}" saved.`;
}
}

export default createHandler(LocationHandler);

Configuration

The options you can pass into ValidationPipe are inherited from class-validator with an additional transformerOptions property, which inherits class-transformer's plainToClass options.

🔗 class-validator options

🔗 class-transformer options