🍮
【Next.js】Route Handlersを使用する際にzodを組み込んでみる
概要
Next.jsのAPIでzodを使用する場合の実装例としてはNext.js API Routes に Zod を組み込むが参考になると思います。Next.js13.2においてAPIでRoute Handlers
の機能が実装されました、機能の概要についてはNext.js 13のRoute Handlersに移行したぞ!の記事をご参照ください。
このRoute Handlersにzodを組み込む実装をしてみたので、今回メモ書きします。
前提
- 今回使用した使用したNext.jsのバージョンは14.0.4です。
実装で留意した点
NextRequest
の仕様が変更になっていて、GETではURLSearchParamsを介してパラメータの値取得が必要となります。なのでzodのsafeParseを通すためには一度、objectの形式にしてあげる必要があります。その実装としてはa way to parse URLSearchParams with Zodの記事が参考になります。
実装サンプル
以下はzodのパース・チェック部分の実装です。
import { NextRequest, NextResponse } from "next/server";
import { z, ZodSchema } from "zod";
function searchParamsToValues(
searchParams: URLSearchParams
): Record<string, any> {
return Array.from(searchParams.keys()).reduce((record, key) => {
const values = searchParams.getAll(key);
return { ...record, [key]: values.length > 1 ? values : values[0] };
}, {} as Record<string, any>);
}
function makeSearchParamsObjSchema<T extends ZodSchema>(schema: T) {
return z
.instanceof(URLSearchParams)
.transform(searchParamsToValues)
.pipe(schema);
}
export async function withZod<T extends ZodSchema>(
schema: T,
req: NextRequest,
next: (reqValue: z.infer<T>) => Promise<NextResponse>
) {
const parsed =
req.method == "POST"
? schema.safeParse(await req.json())
: makeSearchParamsObjSchema(schema).safeParse(req.nextUrl.searchParams);
if (!parsed.success) {
// 共通のバリデーションエラーレスポンスとして処理
return NextResponse.json({ error: parsed.error.message }, { status: 400 });
}
return await next(parsed.data as z.infer<T>);
}
以下が呼び出し部分の実装です。
import { z } from "zod";
import { NextRequest, NextResponse } from "next/server";
export const addSampleUserPostSchema = z.object({
id: z.string().min(1, {
message: "ユーザIDは必須です",
}),
name: z.string().min(1, {
message: "ユーザ名は必須です",
}),
});
export type AddSampleUserPostRequest = z.infer<typeof addSampleUserPostSchema>;
export const addSampleUserPostHandler = async (req: NextRequest) => {
return withZod(
addSampleUserPostSchema,
req,
async (reqValue: AddSampleUserPostRequest) => {
// 実処理の内容は記載割愛
・
・
・
return NextResponse.json({}, { status: 200 });
}
);
};
Discussion