日曜日, 6月 15, 2025
- Advertisment -
ホームニューステックニュースNestJSでzodを使う!基本的な使い方を整理してみた。 #TypeScript - Qiita

NestJSでzodを使う!基本的な使い方を整理してみた。 #TypeScript – Qiita



NestJSでzodを使う!基本的な使い方を整理してみた。 #TypeScript - Qiita

はじめに

TypeScriptでのフルスタック開発ではzodを使うことがデファクトっぽい。(?)
初めてzodを使ったので、備忘録として基本的な使い方をまとめてみました。

目次

  • スキーマと型の一元管理
  • DTOの自動生成
  • バリデーションの実装
  • PrismaなどのORMとの連携

1. 用語 スキーマと型の違い

用語 役割・目的 使われるタイミング
スキーマ データ構造とバリデーションルール定義 実行時(バリデーション)
データの型安全性を保証 開発時(型チェック)
  • スキーマはZodなどで定義し、実行時にデータの検証や変換を行います。
  • はTypeScriptの型システムで、開発時の型安全性を担保します。

import { z } from 'zod';

const userSchema = z.object({
  id: z.string(),
  name: z.string(),
});

type User = z.infertypeof userSchema>;

2. DTOとスキーマの違い

  • **DTO(Data Transfer Object)**は、層間やAPIの入出力で使う「データの型定義」。
  • スキーマは、DTOの型を生成しつつ、バリデーションも担う「実行時の検証ルール」。

現代的な設計では、ZodスキーマからDTO型を自動生成し、型とバリデーションを一元管理するのがベストプラクティスです。


3. Zodの便利なメソッド

pick

指定したフィールドだけ抜き出す

const createUserSchema = userSchema.pick({ name: true });
type CreateUserDto = z.infertypeof createUserSchema>;
// => { name: string }

omit

指定したフィールドを除外

const userWithoutIdSchema = userSchema.omit({ id: true });
type UserWithoutId = z.infertypeof userWithoutIdSchema>;
// => { name: string }

partial

全フィールドを「任意(optional)」に

const userPartialSchema = userSchema.partial();
type UserPartial = z.infertypeof userPartialSchema>;
// => { id?: string; name?: string }

4. サンプル:UserとProfileのCRUD API設計

1. スキーマ定義と型生成

userWithProfileSchemaは、User型にprofileプロパティを追加した複合型として定義しています。
profileSchema.omit({ userId: true })で、結合時に不要なuserIdを除外しています。

import { z } from 'zod';

export const userSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
});

export const profileSchema = z.object({
  id: z.string(),
  userId: z.string(),
  age: z.number().min(0),
  bio: z.string().optional(),
});

export const userWithProfileSchema = userSchema.extend({
  profile: profileSchema.omit({ userId: true }),
});

export type User = z.infertypeof userSchema>;
export type Profile = z.infertypeof profileSchema>;
export type UserWithProfile = z.infertypeof userWithProfileSchema>;

2. DTO生成

APIのリクエストやレスポンスで使うDTO(Data Transfer Object)は、
必要なフィールドだけを抜き出したり、バリデーションルールを追加したりしてスキーマを作成します。

export const createUserProfileSchema = z.object({
  name: z.string(),
  email: z.string().email(),
  age: z.number().min(0),
  bio: z.string().optional(),
});
export type CreateUserProfileDto = z.infertypeof createUserProfileSchema>;

export const updateUserProfileSchema = createUserProfileSchema.partial();
export type UpdateUserProfileDto = z.infertypeof updateUserProfileSchema>;

3. Zodバリデーションパイプ

コントローラーで@UsePipes(new ZodValidationPipe(schema))と使うことで、
そのエンドポイントのリクエストボディがZodスキーマでバリデーションされます。
バリデーションエラー時は自動で400エラーが返ります。

import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
import { ZodSchema, ZodError } from 'zod';

@Injectable()
export class ZodValidationPipe implements PipeTransform {
  constructor(private schema: ZodSchemaany>) {}

  transform(value: unknown) {
    try {
      return this.schema.parse(value);
    } catch (error) {
      if (error instanceof ZodError) {
        throw new BadRequestException(error.errors.map(e => e.message).join(', '));
      }
      throw error;
    }
  }
}

4. コントローラーの実装サンプル

NestJSのコントローラーで、DTOとバリデーションパイプを使ってCRUD APIを実装するサンプルです。

import { Controller, Get, Post, Body, Param, Patch, Delete, UsePipes } from '@nestjs/common';
import { UserProfileService } from '../services/user-profile.service';
import {
  createUserProfileSchema,
  CreateUserProfileDto,
  updateUserProfileSchema,
  UpdateUserProfileDto,
} from '../dto/user-profile.dto';
import { ZodValidationPipe } from '../pipes/zod-validation.pipe';

@Controller('users')
export class UserProfileController {
  constructor(private readonly service: UserProfileService) {}

  @Post()
  @UsePipes(new ZodValidationPipe(createUserProfileSchema))
  create(@Body() dto: CreateUserProfileDto) {
    return this.service.create(dto);
  }

  @Get()
  findAll() {
    return this.service.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.service.findOne(id);
  }

  @Patch(':id')
  @UsePipes(new ZodValidationPipe(updateUserProfileSchema))
  update(@Param('id') id: string, @Body() dto: UpdateUserProfileDto) {
    return this.service.update(id, dto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.service.remove(id);
  }
}

5. まとめ

基本的な使い方を整理してみました。
いろんな記事を参照しつつAIにサンプルコードを実装してもらうことで効率よく学習ができました。
参考記事はこちら





Source link

Views: 0

RELATED ARTICLES

返事を書く

あなたのコメントを入力してください。
ここにあなたの名前を入力してください

- Advertisment -