😊

zodでスキーマが可変のときのバリデーションを実装する

に公開

テナントごとにデータのスキーマが違ったりするときに、zodでバリデーションを行えるのでは?と思い立ち試しにやってみたそれの備忘録です。

key-valueのペアからオブジェクトを作る

最初にkey-valueのペアの配列からオブジェクトを作れることを確認しました。以下のようにコードを書きました。

const inputs = [
  { key: "name", value: "Hanako" },
  { key: "age", value: 25 },
  { key: "email", value: "hanako@example.com" },
];
  
const obj = inputs.reduce((acc, { key, value }) => {
  acc[key] = value;
  return acc;
}, {} as Record<string, unknown>);

console.log(obj);

出力結果は以下です。console.log(obj.name);このように指定することもできたので、オッケーです。

{
  name: "Hanako",
  age: 25,
  email: "hanako@example.com",
}

zodで可変なz.objectを作る

以下のようにコードを書きました。key-valueのペアのkeyはさっきと変わらずで、valueにzodの型スキーマを指定しました。また、z.object()内で、reduceを利用してオブジェクトの型スキーマを構築しています。

const inputsType = [
  { key: "name", value: z.string() },
  { key: "age", value: z.number().int() },
  { key: "email", value: z.email() },
];

const schema = z.object(
  inputsType.reduce((acc, { key, value }) => {
    acc[key] = value;
    return acc;
  }, {} as Record<string, z.ZodTypeAny>)
);

const inputData = {
  name: "Hanako",
  age: 25,
  email: "hanako@example.com",
}

const result = schema.safeParse(inputData);

console.log(result);
console.log(result.success);
console.log(result.data);
console.log(result.error);

実行結果は以下です。

{
  success: true,
  data: {
    name: "Hanako",
    age: 25,
    email: "hanako@example.com",
  },
}
true
{
  name: "Hanako",
  age: 25,
  email: "hanako@example.com",
}
undefined

こんな感じの書き方もできます。

const entries = [
  ["name",  z.string()],
  ["age",   z.number().int()],
  ["email", z.email()],
] as const;
  
type Entry = typeof entries[number];
type Shape = { [K in Entry as K[0]]: K[1] };
  
const shape = Object.fromEntries(entries) as unknown as Shape;
const schema = z.object(shape);

const inputData = {
  name: "Hanako",
  age: 25,
  email: "hanako@example.com",
}

const result = schema.safeParse(inputData);

console.log(result);
console.log(result.success);
console.log(result.data);
console.log(result.error);

この書き方だとキーを保持できるのですが、テナントごとにスキーマが違うとなればキーを保持できるというメリットは少ないと思います。(ロマンは感じますが)

最後に

これができるとマルチテナントなアプリケーションでテナントごとに入力が違うシーンなどで役立つかもしれません。

Discussion