Application - Server - Validation
# Validation:
Để validate input có thể validate tại graphql schema hoặc dùng class-validator
tại resolver.
# Graphql Schema:
Graphql Schema có 1 số scalar có thể dùng để validation cho input.
input UserCreateInput {
name: String!
password: String!
email: String!
}
2
3
4
5
vd: Không nhập field name.
"errors": [
{
"message": "Field UserCreateInput.name of required type String! was not provided.",
"locations": [
{
"line": 2,
"column": 21
}
],
"extensions": {
"code": "GRAPHQL_VALIDATION_FAILED"
}
}
]
2
3
4
5
6
7
8
9
10
11
12
13
14
Nhưng graphql mặc định chỉ có 1 vài scalar để validate định dạng dữ liệu để sử dụng những rule khác cần install thêm package (opens new window) dùng các directive để validate input hoặc tự viết các directive kết hợp scalar.
# Resolver:
Dùng các class dto (opens new window) để validate input.
Xử lí handle error của class-validator
để đồng nhất với error của graphql.
Tạo custom ValidationPipe:
Nếu có lỗi thì throw ValidationError của
apollo-server-express
import { Injectable, PipeTransform, ArgumentMetadata, Type } from '@nestjs/common'; import { validate } from 'class-validator'; import { plainToClass } from 'class-transformer'; import { ValidationError } from 'apollo-server-express'; @Injectable() export class ValidationPipe implements PipeTransform { async transform(value: any, {metatype}: ArgumentMetadata) { if (!metatype || !this.toValidate(metatype)) { return value; } const object = plainToClass(metatype, value); const errors = await validate(object); if (errors.length > 0) { throw new ValidationError( `${this.formatErrors(errors)}`, ); } return value; } private toValidate(metatype: Type<any> | undefined): boolean { const types: any[] = [String, Boolean, Number, Array, Object]; return !types.includes(metatype); } private formatErrors(errors: any[]) { return errors .map(err => { for (const property in err.constraints) { return err.constraints[property]; } }) .join(', '); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36Binding ValidationPipe tại main.ts
app.useGlobalPipes(new ValidationPipe());
1
# Tạo custom validation class:
Tạo 1 custom validation class để validate unique cho field email. Code tham khảo tại
https://gist.github.com/zarv1k/3ce359af1a3b2a7f1d99b4f66a17f1bc (opens new window)
Xử lí validate: dùng MongoRepository count dữ liệu theo điều kiện.
public async validate<E>(value: string, args: UniqueValidationArguments<E>) {
const [EntityClass, findCondition = args.property] = args.constraints;
const cond = typeof findCondition === 'function'
? findCondition(args)
: {
[findCondition || args.property]: value,
};
return (
(await this.connection.getRepository(EntityClass).count(cond)) <= 0
);
}
2
3
4
5
6
7
8
9
10
11
12
Theo hướng dẫn trên, khi dùng thì sẽ phát sinh lỗi connection
underfined, vì không inject được connection.
@ValidatorConstraint({ name: 'unique', async: true })
@Injectable()
export class Unique extends UniqueValidator {
constructor(@InjectConnection() protected readonly connection: Connection) {
super(connection);
}
}
2
3
4
5
6
7
Cần thêm set DI-container của class-validator
tại main.ts
useContainer(app.select(AppModule), { fallbackOnErrors: true });
Sử dụng tại Dto:
export class UserCreateInputDto extends UserCreateInput {
...
@IsEmail()
@Validate(Unique, [User, 'email'])
email: string;
}
2
3
4
5
6
7
Series:
- Preview (opens new window)
- Server - NestJs & GraphQL (opens new window)
- Server - Module (opens new window)
- Server - Validation (opens new window)
- Server - Authentication (opens new window)
- Server - Supscription & Upload file (opens new window)
- Client - Angular & GraphQL (opens new window)
GitHub: https://github.com/ninhnguyen22/nestjs-angular-graphql (opens new window)