💤
zodとgenericでexpressのrequest.bodyにタイプをつける
はじめに
こんにちは。
full-stack developerを目指しているShenです。
最近zodがすこく流行っていて、zodでexpressのrequestにタイプをつけるをやってみました。
問題
例えば、以下のようなエンドポイントがあって、id
が提供されていないでしたら、400
でエラーを返したいとしています。
app.post('/user', (req: express.Request, res: express.Response) => {
const id = req.body.id;
if (!id) {
res.status(400).send('no id provided error');
}
res.status(200).send('done');
});
req.body.id
にhoverすると、any
タイプになっていることがわかります。
req.body.id
が文字列で、長さは1~10なんかしらの入力制限をしたいとき、class-validatorを使っていると思うが、zodでかなり実装時間を省けると思う。
zodで解決
まずはgenericでエラーをハンドルできるようなcallbackメソッドを作成する。
const requestBodyLayer =
<Tbody>(
schema: z.Schema<Tbody>,
callback: (
req: express.Request<any, any, Tbody, any>,
res: express.Response
) => void
) =>
(req: express.Request, res: express.Response) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).send(result.error.issues[0].message);
}
return callback(req, res);
};
req: express.Request<any, any, Tbody, any>
はなぜ3番目にgenericタイプを付与したか?expressのタイプファイルを確認すると、ReqBodyは元々anyだったので、bodyのタイプをzodで定義したタイプに変更する
そして、/user
専用のユーザーリクエストハンドラを生やす。
const userHandler = requestBodyLayer(
z.object({ id: z.string().min(1).max(10) }),
(req, res) => {
const id = req.body.id;
res.status(200).send(`id : ${id}`);
}
);
app.post('/user', userHandler);
上記のハンドラが追加されたことにより、req.body
がタイプでガードできるようになりました。しかも、runtimeタイプチェックになります。
body
に項目を追加したい場合、z.object
内でプロパティを追加するだけで良い。
z.object({
id: z.string().min(1).max(10),
name: z.string().min(1).max(255),
age: z.number(),
}),
以下のように、自動補完機能も効くようになって、開発速度が早くなるわけだ。
終わりに
以上はzodとgenericを使って、expressのrequest.bodyにタイプをつけるようにするサンプルになります。様々な方法があると存じますが、ぜひこれも一度試していただけたらと・・・
Discussion