🎃

Cloudflare functions を Zod で型安全に

2023/01/05に公開

こんばんは、2023年は Cloudflare に全振りしたい松本です。
ちなみにこの記事は Cloudflare Advent Calendar 2022 の25日の記事です。(執筆時点2023/01/05です...すみません...)

functions に zod を使った middleware で API を型安全にする

Next.js API Routes に Zod を組み込むの記事を見て functions にも組み込みたいなというモチベで作成しました。
正直実装は上記の記事を 丸パクリ もろに参考にさせて頂いているので zod 周りで特筆すべき点はありません。

一点、 zod で解析した body をどこに突っ込むかで学びがあったので共有します。
Next.js ですと body は req の中にオブジェクトとして内包されていますが、functions の場合、await requeest.json() で取得するような形になっています。
ここで取得したデータをどこに生やすか(request.body に上書き?)を悩んでいた所、こちらのレポジトリ の middleware.ts にて d1 データベースを data に突っ込んでいるのを発見しました。
cloudflare のドキュメントを見ていて data オブジェクトの存在は知っていましたが、いまいち使いみち分からなかったので頭が凝り固まっていた自分的には目からウロコでした(ちなみにドキュメントには data の存在だけで説明は皆無です)
この data を使って zod でバリデーションした body を突っ込みます。

type Context<T extends ZodSchema = never> = EventContext<
  Env // お好きな Env,
  Param // お好きな Param,
  { body: z.infer<T> }
>;

export function withValidation<T extends ZodSchema>(
  schema: T,
  next: (context: Context<T>) => Response | Promise<Response>
) {
  return async (context: Context<T>) => {
    const json = await context.request.json();
    const parsed = schema.safeParse(json);
    if (!parsed.success) {
      return jsonResponse({
        message: "Bad Request",
        issues: JSON.parse(parsed.error.message),
      });
    }
    // data に zod でパースしたオブジェクトを突っ込む
    context.data.body = parsed.data;
    return next(context);
  };
}

利用側
第一引数に zod obejct, 第二引数に関数の実態

const postInput = z.object({
  name: z.string().optional(),
  avatar: z.string().url().optional(),
});

export const onRequestPost = withValidation(postInput, async ({ data }) => {
  // バチバチに型ついてる!
  const { name, avatar } = data.body;
  .
  .
  .
  return jsonResponse(result);
});

無事にバチバチの型安全です

P.S.

data の使い方これであってるか分からん...

cloudflare 関連の OSS プロジェクト作ってますのまた報告させて下さい!
あと誰か cloudflare 関連の仕事下さい

Discussion