【備忘録】データフェッチ時に zod を使ってフェッチしたデータのデータ型を保障する

# はじめに
この記事はデータフェッチ時に **zod** のバリデーションを使用することでフェッチしたデータ型を保障することから **フォームバリデーション** 以外での **zod** の使い方の備忘録です。

間違っている部分がありましたらコメントで教えてもらえると嬉しいです。

## parseメソッド
**zod** でバリデーションを行う時は
判定するバリデーションルールを記述した **zodスキーマ** を作成し
**zodスキーマ** の **parseメソッド** か **safeparseメソッド** を使います。

```js
const mySchema = z.string().min(1);
const parsedValue = mySchema.parse("abc");
console.log(parsedValue); //"abc"

const parsedValue2 = mySchema.parse(123);
console.log(parsedValue2); // エラーがスローされるのでログが流れない
```
**parseメソッド** では
バリデーション **成功時** は バリデーションの引数の値が戻り値となり
バリデーション **失敗時** は エラーがスローされます。

エラーの内容を取得するには
`try-catch` で **parseメソッド** を囲んで
**error.flatten()** で 取得できます。

**バリデーションエラー** の時のみエラー内容を取得する必要があるので、
**error instance of ZodError** で判定します。
```js
try {
    const parsedValue2 = mySchema.parse(123);
    console.log(parsedValue2); // エラーがスローされるのでログが流れない
} catch (e) {
    if (e instanceof ZodError) {
        console.log(e.flatten()); // { formErrors: [ 'Expected string, received number' ], fieldErrors: {} }
    }
}
```

## safeParseメソッド
**safeParseメソッド** では
バリデーション **成功時** は **success: true** で **data: バリデーションの引数** なオブジェクトが
バリデーション **失敗時** は **success: false** で **error: エラー内容が詰まったオブジェクト**
なオブジェクトが返されます。

```js
const mySchema = z.string().min(1);
const parsedValue = mySchema.safeParse("abc");
console.log(parsedValue); // { success: true, data: 'abc' }

const parsedValue2 = mySchema.safeParse(123);
console.log(parsedValue2); // { success: false, error: [Getter] }
console.log(parsedValue2.error.flatten()); // { formErrors: [ 'Expected string, received number' ], fieldErrors: {} }
```

エラーの内容を取得するには
**error.flatten()** で取得します。

ただし、**success: false** でないと **errorプロパティが存在しない** ので
**success: false** であることを判定してからでなければなりません。

なので、正しくはこのように記述します。
```js
const mySchema = z.string().min(1);
const parsedValue = mySchema.safeParse("abc");
console.log(parsedValue); // { success: true, data: 'abc' }

const parsedValue2 = mySchema.safeParse(123);
console.log(parsedValue2); // { success: false, error: [Getter] }
console.log(parsedValue2.success ? {} : parsedValue2.error.flatten()); // { formErrors: [ 'Expected string, received number' ], fieldErrors: {} }
```

## フェッチしたデータにバリデーションチェックを行うことでデータ型を保障する
`jsonplaceholder` からフェッチしたデータをバリデーションチェックします。

```js
import { z } from "zod"

const todoSchema = z.object({
    userId: z.number(),
    id: z.number(),
    title: z.string(),
    completed: z.boolean(),
});

const fetchTodos = await fetch("https://jsonplaceholder.typicode.com/todos");
const fetchTodo = await fetchTodos.json();

const parsedTodo = todoSchema.safeParse(fetchTodo[0]);
console.log(parsedTodo); 
/*{
    success: true,
    data: { userId: 1, id: 1, title: 'delectus aut autem', completed: false }
  }
  */
```
このようにしてフェッチしたデータのデータ型を保障します。

## zodスキーマからTSのデータ型を取得する
**zodスキーマ** からTSの型を取得する方法も記述しておきます。
```ts
import { z } from "zod"

const UserSchema = z.object({
  username: z.string(),
});

type User = z.infer<typeof UserSchema> 
/*
type User = {
    username: string;
}
*/
```
**zodスキーマ** を定義した後に
**z.infer\<typeof zodスキーマ>** で **zodスキーマのデータ型** を取得出来ます。

### さいごに
学習させていただいた先・ライブラリの公式ドキュメントです。

https://reffect.co.jp/react/zod-validation

https://zod.dev/?id=objects