best practice for api data validation

4.6k views Asked by At

I'm working on creating api with nestjs , but when it comes to data validation it becomes so confused because of many options existing , for example should i validate data in rout level using schema validation (like hapi@joi) or validate data in class level class-validator

`

use(req: Request, res: Response, next: Function) {
    console.log('the method is', req.method);

const userSchema = joi.object({
  name: joi
    .string()
    .alphanum()
    .min(3)
    .max(15)
    .required(),
  phone: joi
    .number()
    .min(11)
    .required(),
  password: joi
    .string()
    .min(8)
    .max(100)
    .required()
    .regex(/((?=.*\d)|(?=.*\w+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/),
});
const { error } = userSchema.validate(req.body);
if (error) {
  throw new BadRequestException(error.details);
}
next();

`

validation on class level

`

export class CreateUserDto {
  @IsString()
  @IsNotEmpty()
  name: string;

  @IsNotEmpty()
  @IsString()
  @Matches(/((?=.*\d)|(?=.*\w+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/)
  password: string;

  @IsNumber()
  @Length(11, 15)
  @IsNotEmpty()
  phone: number;
}

`

2

There are 2 answers

0
JustHeath On

The NestJs Pipes documentation page gives examples using both Schema based validation and Class validator techniques and doesn't appear to prefer one over the other. In most cases, following NestJs convention is preferred over the middleware technique used in your first example.

Depending on the number of entities and how often the requirements change, it could be difficult to keep your schemas synchronized using the first technique.

Your second example will likely be easier to maintain since your validation is defined with your entity. You don't have to remember to edit another file when the entity changes.

If you're using any 3rd-party packages it may have built-in support for one or the other. The @nestjsx/crud package, for example, uses NestJs's built-in ValidationPipe for Body request validation, making use of the class-validator and class-transformer libraries.

As @iván-sánchez suggested, this may depend significantly on your project structure. Your comment to his answer suggests several scenarios, so the best practice may vary.

3
Iván E. Sánchez On

It's a personal preference and it depends a lot on your project structure, but...

Using DTOs to validate your objects makes your code cleaner IMHO and simplifies the usage of the nest OpenAPI plugin (used for documentation). And also, since if we remove the decorators, it is nothing more than a simple class with properties, you could also use it as your type on the parameters of your functions just like -> https://docs.nestjs.com/openapi/types-and-parameters allowing typescript to infer the type of the object and allowing you to be sure it's already validated