I want to build a custom validator for NestJs (using v10) that will allow doing following
- Use Zod schema to validate
- Allows Creation of Dto from Zod Schema
- Works with File Upload (Nest's built-in FileInterceptor, UploadedFile & ParseFilePipe) etc
Following is current implementation
Validation Pipe
// zod-validation.pipe.ts (bound globally from app module)
import { Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { ZodDtoClass } from '../utils/zod-dto.interface';
@Injectable()
export class ZodValidationPipe {
transform(value: Record<string, unknown>, metadata: ArgumentMetadata) {
const schemaClass: typeof ZodDtoClass = metadata.metatype! as unknown as typeof ZodDtoClass;
if (!schemaClass.schema) return;
const result = schemaClass.schema.safeParse(value);
if (!result.success) {
throw new BadRequestException(result.error.flatten());
}
return { data: result.data };
}
}
Dto
// create-feed.dto.ts
import { z } from 'zod';
import { ZodDtoClass } from '../../utils/zod-dto.interface';
const schema = z
.object({
title: z.string().min(5).max(35).nullish(),
body: z.string().max(140).nullish(),
})
.refine(
(d) => {
return !!(d.title || d.body);
},
{
path: ['title'],
message: "Title, Body and attachment can't be empty at the same time",
},
);
export class CreateFeedDto extends ZodDtoClass<typeof schema> {
static override schema = schema;
}
Controller method
@UseInterceptors(FileInterceptor('attachment'))
@Post('/create')
create(
@Body() createFeedDto: CreateFeedDto,
@UploadedFile(
new ParseFilePipe({
validators: [
new MaxFileSizeValidator({ maxSize: 5000000 }),
new FileTypeValidator({ fileType: '(jpg|png)$' }),
],
}),
)
attachment: Express.Multer.File,
) {
console.log({
createFeedDto: createFeedDto,
attachment,
});
return this.feedsService.create(createFeedDto);
}
After request from postman with all fields (with jpg file). Following is the result I'm getting
{
createFeedDto: { data: { title: 'hello', body: 'This is body text' } },
attachment: undefined
}
FYI: I've already used all of the packages available that can do a similar thing & I found all of them have different problems and don't fulfill my requirements. So I want to create a new one