Honoとzod-openapi
Honoとzod-openapiを組み合わせることで、zodスキーマを使用した堅牢なAPIインターフェースを実装しつつ、openapiのドキュメントを生成することができます。
上記のテキトーな日本語があっているかはわかりませんが、エンドポイントの生成、リクエストのバリデーション、openapiのドキュメントをとても楽に実装できます。初めて聞いたぜ!って方はぜひ以下のリンクから覗いてみてください。
zod-openapiを使用した場合の400エラーハンドリング
さあ、ここからがこの記事の本題です。
zod-openapiを使用してルーティングを設定した場合、リクエストボディがzodスキーマを満たさずにバリデーションエラーとなると以下のようなbodyで400レスポンスを返してきます。
{
"success": false,
"error": {
"issues": [
{
"validation": "email",
"code": "invalid_string",
"message": "Please enter a valid email address",
"path": [
"email"
]
}
],
"name": "ZodError"
}
}
zodエラーの内容がまるッとissues
の配列として渡ってくる感じですね。
これでも良いのですが、それぞれ仕様によっては上のような構造ではなく、独自のエラーレスポンスで返したい場合は往々にしてあると思います。
その場合、以下のようにOpenAPIHono
クラスからインスタンスを生成するときにdefaultHook
を設定すると、バリデーションエラー時のエラーレスポンスをカスタマイズできます。
const app = new OpenAPIHono({
defaultHook: (result) => {
if (!result.success) {
throw handleError(result.error);
}
},
});
handleError
は独自に定義しているユーティリティですが、その中で私は以下のような処理を実装しました。
if (error instanceof ZodError) {
const details = error.errors.map((err) => ({
field: err.path.join("."),
message: err.message,
}));
return new HTTPException(400, {
message: JSON.stringify({
error: {
code: "VALIDATION_ERROR",
message: "Invalid input data",
details,
},
}),
});
}
リクエストボディに対してzodスキーマでのパースに失敗した場合、result.error
からZodError
が渡ってきます。それをごにょごにょして良い感じのカスタムレスポンスを400ステータスで返しています。
この実装をしてから、以下のようなJSONで返却されるようになりました。
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": [
{
"field": "email",
"message": "Please enter a valid email address"
}
]
}
結構わかりやすく、完結にカスタムできたのではないでしょうか!
以上、hono/zod-openapi
を使用した場合のバリデーション時エラーレスポンスのカスタム方法の紹介でした!
ありがとうございました!