📚

Zodがとても便利だった話

2025/01/15に公開

始めに

株式会社ソニックムーブでバックエンドエンジニアをやっている新卒2年目の中崎です。
今回は案件で使用したZodが想像以上に便利だったため、活用例を交えつつ紹介していければと思います。

Zodとは

https://zod.dev/
Zodとはスキーマの宣言およびバリデーションを行うOSSのライブラリです。
フレームワークであるhonoのv3.0.0でのバリデーションミドルウェアに導入されていたりします。

活用例

バリデーション

オブジェクト作成時に各カラムのバリデーションを行いたい時があると思います。
ただ、入力された値を全て確認するのはめんどくさいと思います。
Zodを使えばスキーマの定義をしておけば、ほぼほぼバリデーションを完了させることができます。
数値のみの簡単な例は以下のようなものです。

import { z } from 'zod';

const number = z.number().safe();
number.parse(1);
=> 1
number.parse('aaa');
=> throw ZodError

また条件付きバリデーションを追加することも可能です。

import { z } from 'zod';

const number = z.number().gt(3).lt(5);
number.parse(2);
=> throw ZodError

number.parse(4);
=> 4

number.parse(6);
=> throw ZodError

このような条件付きバリデーションはz.string()にも存在し文字数やメールアドレスなどを含むことができます。
詳しくはこちら

ということで本題です。DBやフォームからの入力などからオブジェクトを作成する際にバリデーションを行います。
今回はブログサイトのユーザーのデータを想定してみます。

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

オブジェクトはz.object()を使って宣言できます。
ただ、この状態ではTypeScriptの型として使用することができません。これはz.inferを使って解決できます。

type UserType = z.infer<typeof User>;
// => type UserType = {
//        id: number;
//        name: string;
//        email: string;
//    }

スキーマの定義ができたのでバリデーションをしてみます。

// 正常なやつ
const userObject = {
    id: 1,
    name: 'John',
    email: 'test@test.com',
};

const user = User.parse(userObject);
console.log(user);
// => { id: 1, name: 'John', email: 'test@test.com' }

// エラー出るやつ
const userObject = {
    id: 1,
    name: 'John',
    email: 'test',
};

const user = User.parse(userObject);
console.log(user);
// => throw ZodError

こんな感じでバリデーションできます。
バリデーションに関してはまだまだ紹介しきれていませんが、かなりの種類や方法が存在するのでドキュメントを確認して便利に使いましょう。

JSON.parse

JSON.parseでjsonを取得すると型がanyになります。
ソニックムーブではeslintでanyを排除しているためJSON.parseを行った値に対して型アサーションを行っていました。
ただこの方法は型安全的な観点でよくないので色々と模索している時にZodを見つけました。元々はバリデーション目的ではありませんでしたw
Zodを使うとJSON.parseで取得したオブジェクトに型を付与できます。

const jsonString = '{"id":1,"name":"John","email":"test@test.com"}';
const json = JSON.parse(jsonString);
// => Unsafe assignment of an `any` value.eslint@typescript-eslint/no-unsafe-assignment
// eslintの警告が出る

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

type UserType = z.infer<typeof User>;

const user = User.parse(JSON.parse(jsonString));
// => const user: {
//  id: number;
//  name: string;
//  email: string;
// }
// しっかりと型が付与されている

まとめ

anyを排除したいために導入したZodでしたが、とても使い勝手の良いバリデーションのおかげで完全に使い目的が変わりましたw
Enumやエラー文言変更など他にも使い道があり、とても便利なライブラリーでした。
これからも使い勝手の良いライブラリーなどがあれば発信していきたいです!

株式会社ソニックムーブ

Discussion