Условное правило в Nest
Пусть клиент выбирает режим записи, и от выбора зависит, какие ещё ключи должны быть в теле. Требовать все поля всегда значит отвергать корректные упрощённые варианты.
Условное правило смотрит на пару
полей после разбора. Если первое
поле равно заданному значению,
второе становится обязательным,
иначе его можно не присылать.
Такую связь задают через check
или refine на объекте схемы.
Покажем на примере постов:
import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common';
@Injectable()
export class SchemaPipe implements PipeTransform {
constructor(private schema: { '~standard': { validate: (value: unknown) => Promise<{ value?: unknown; issues?: { message: string }[] }> | { value?: unknown; issues?: { message: string }[] } } }) {}
async transform(value: unknown) {
const result = await this.schema['~standard'].validate(value);
if (result.issues) {
throw new BadRequestException(result.issues[0].message);
}
return result.value;
}
}
При режиме ссылки потребуем адрес, в простом режиме он не нужен:
import { z } from 'zod';
export const createPostSchema = z
.object({
kind: z.enum(['plain', 'link']),
url: z.string().optional(),
})
.check(
z.refine(
(data) => data.kind !== 'link' || data.url !== undefined,
{ error: 'URL is required for link posts' },
),
);
На маршруте создания передаём схему в канал и возвращаем проверенное тело клиенту:
import { Body, Controller, Post } from '@nestjs/common';
import { createPostSchema } from './create-post.schema.js';
import { SchemaPipe } from './schema.pipe.js';
@Controller('posts')
export class PostController {
@Post()
createPost(
@Body(new SchemaPipe(createPostSchema)) body: {
kind: 'plain' | 'link';
url?: string;
},
) {
return body;
}
}
Подключим контроллер в корневом модуле:
import { Module } from '@nestjs/common';
import { PostController } from './post.controller.js';
@Module({
controllers: [PostController],
})
export class AppModule {}
Сделайте создание работника, где
одно поле обязательно только при
выбранном значении другого, в
контроллере WorkerController
и подключите его к корневому модулю.