<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Zod + Nestjs  (вместо сlass-validator)]]></title><description><![CDATA[<p dir="auto"><img src="/assets/uploads/files/1737983228812-1_otbtuba4_rtkoxcsz-ybkq.webp" alt="1_oTbTuBA4_RtKoXCsZ-ybKQ.webp" class=" img-fluid img-markdown" /><br />
Очень удобно внедрять проверку данных запроса  декларативно, в контроллерах. Так и учит нас  <a href="https://docs.nestjs.com/techniques/validation" target="_blank" rel="noopener noreferrer">официальная документация nestjs</a>, и для этого нам необходима библиотека class-validator</p>
<p dir="auto">Но многим не нравится обмазываться  декораторами, особенно когда их много решение кажется очень сомнительным:</p>
<pre><code class="language-ts">class CreateCompanyDto implements Dto {
    @IsString({message: 'Must be text format'})
    @MinLength(2, { message: "Must have at least 2 characters" })
    @MaxLength(20, { message: "Can't be longer than 20 characters" })
    @IsDefined({ message: 'Must specify a receiver' })
    public name!: string;

    @MaxLength(253, { message: "Can't be longer than 253 characters" })
    @IsFQDN({}, {message: 'Must be a valid domain name'})
    @IsDefined({ message: 'Must specify a domain' })
    public domain!: string;

    @MaxLength(30, { message: "Can't be longer than 30 characters" })
    @IsString({message: 'Must be text format'})
    @IsDefined({ message: 'Must specify a company size' })
    public size!: string;

    @IsPhoneNumber(null, {message: 'Must be a valid phone number'})
    @IsDefined({ message: 'Must specify a phone number' })
    public contact!: string;
}
</code></pre>
<p dir="auto"><a href="https://github.com/colinhacks/zod" target="_blank" rel="noopener noreferrer">Zod</a> - это удобная альтернатива библиотеке проверки классов для проверки тела запроса.</p>
<h1>Пример кода</h1>
<p dir="auto">Итак поехали…</p>
<p dir="auto">Создаем схему</p>
<pre><code class="language-ts">
import { z } from "zod";

export const createProductShema = z.object({
  title: z.string(),
  text: z.string(),
  active: z.boolean(),
});

type CreateProductDtoType = z.infer&lt;typeof createProductShema&gt;;

// или вместо типа можем использовать интерфейс
export interface CreateProductDto extends CreateProductDtoType {}
</code></pre>
<p dir="auto"><a href="https://zod.dev/?id=type-inference" target="_blank" rel="noopener noreferrer">z.infer()</a> позволит нам получить тип для  данных, которые будем валидировать</p>
<p dir="auto">далее создаем <a href="https://docs.nestjs.com/pipes" target="_blank" rel="noopener noreferrer">Pipe</a></p>
<pre><code class="language-ts">@Injectable()
export class ZodPipe implements PipeTransform {
  constructor(private readonly schema: any) {}

  transform(value: any, metadata: ArgumentMetadata) {
    this.schema.parse(value);
    return value;
  }
}
</code></pre>
<p dir="auto">и обмазываем наш контролер  ранее созданным пайпом</p>
<pre><code class="language-ts">@Controller()
export class AppController {
  constructor(private readonly productUseCaseService: ProductUseCaseService) {}

  @Post()
  createProduct(@Body(new ZodPipe(schema)) body:  CreateProductDto) {
    return this.productUseCaseService.create(body);
  }
}
</code></pre>
<p dir="auto">Ну и для того, чтобы мы отлавливали ошибки валидации, и формировали ответ запроса как нам нужно<br />
напишем фильтр и подключим его</p>
<pre><code class="language-ts">@Catch(ZodError)
export class ZodFilter&lt;T extends ZodError&gt; implements ExceptionFilter {
  catch(exception: T, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const status = 400;
    response.status(status).json({
      errors: exception.errors,
      message: exception.message,
      statusCode: status,
    });
  }
}
</code></pre>
<pre><code class="language-ts">async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new ZodFilter());
  await app.listen(3000);
}
</code></pre>
<p dir="auto">Такая схема кажется гибче, и вообще zod закрывает  проблемные места typescript связанные с отсутствием  проверки типов в runtime</p>
]]></description><link>https://forum.exlends.ru/topic/18/zod-nestjs-vmesto-slass-validator</link><generator>RSS for Node</generator><lastBuildDate>Wed, 20 May 2026 08:16:04 GMT</lastBuildDate><atom:link href="https://forum.exlends.ru/topic/18.rss" rel="self" type="application/rss+xml"/><pubDate>Tue, 09 Jul 2024 17:45:40 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Zod + Nestjs  (вместо сlass-validator) on Thu, 18 Jul 2024 12:24:36 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/manul" aria-label="Profile: Manul">@<bdi>Manul</bdi></a> поэтому и приходится велосипедить)</p>
]]></description><link>https://forum.exlends.ru/post/48</link><guid isPermaLink="true">https://forum.exlends.ru/post/48</guid><dc:creator><![CDATA[Aladdin]]></dc:creator><pubDate>Thu, 18 Jul 2024 12:24:36 GMT</pubDate></item><item><title><![CDATA[Reply to Zod + Nestjs  (вместо сlass-validator) on Thu, 18 Jul 2024 12:24:03 GMT]]></title><description><![CDATA[<p dir="auto">жаль сейчас нет библиотеки поддерживаемой  с zod для Nest,<br />
есть <code>nestjs-zod</code>, но автор на нее забил</p>
<p dir="auto"><img src="/assets/uploads/files/1721305439671-db2fc411-c901-4f0a-a251-f2d28fe377cc-image.png" alt="db2fc411-c901-4f0a-a251-f2d28fe377cc-image.png" class=" img-fluid img-markdown" /></p>
]]></description><link>https://forum.exlends.ru/post/47</link><guid isPermaLink="true">https://forum.exlends.ru/post/47</guid><dc:creator><![CDATA[Manul]]></dc:creator><pubDate>Thu, 18 Jul 2024 12:24:03 GMT</pubDate></item></channel></rss>