Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Zod v4 Codec でスキーマに型変換を埋め込む REST API 設計 #TSKaig...

Zod v4 Codec でスキーマに型変換を埋め込む REST API 設計 #TSKaigi2026

2026年5月22日(金)- 23日(土)開催の「TSKaigi 2026」での発表資料です。

セッションタイトル: Zod v4 Codec でスキーマに型変換を埋め込む REST API 設計(Ryutaro Yako)
https://2026.tskaigi.org/talks/31

Avatar for Ryutaro Yako

Ryutaro Yako

May 20, 2026

More Decks by Ryutaro Yako

Other Decks in Programming

Transcript

  1. 2026.05.22 TSKaigi 2026 / Ryutaro Yako Copyright ©Unique Vision Company.

    All Rights Reserved. Zod v4 Codec で スキーマに型変換を埋め込む REST API 設計
  2. Profile 名前: Ryutaro Yako 所属: ユニークビジョン株式会社 役割: テックリード Experience 7

    名のエンジニアが所属する開発チーム toB 向けアプリケーションをフルスタック TypeScript で開発中 Copyright ©Unique Vision Company. All Rights Reserved. 自己紹介
  3. 当社はソーシャルメディアを利用した、ソーシャルメディア総合管理ツールや X・LINE・Instagram・TikTokなどSNSマーケティングツールを多数提供しています。 Belugaスタジオ X/Facebook/Instagramなどのアカウ ントを一元管理できるソーシャルメディ ア総合管理ツールです。企業のSNS運用 をサポートします。 Beluga キャンペーン フォロー&リポストに代表されるXのイ

    ンスタントウィンをはじめとしたキャン ペーンを複数展開しています。質の高い キャンペーンの実施を支援しています。 その他 SNSキャンペーンに連動した、生成AIを 活用したシステムやプラットフォームが 提供する独自ツールやシステム開発も順 次行っています。 Copyright ©Unique Vision Company. All Rights Reserved. 事業内容
  4. 01. 背景と課題 02. Zod v4 Codec とは 03. Zod スキーマの設計

    04. バックエンドでの利用方法 05. フロントエンドでの利用方法 06. まとめ Copyright ©Unique Vision Company. All Rights Reserved. アジェンダ
  5. フルスタック TypeScript の開発プロジェクト API サーバは REST API として実装 社内の他のプロジェクトでは Rust

    で API を開 発しており、設計を REST API に統一 tRPC などの利用は見送ることになった 技術スタック アプリ 技術スタック API サーバ Hono / @hono/zod-openapi フロントエンド Vue.js / hono/client Copyright ©Unique Vision Company. All Rights Reserved. 背景
  6. モノレポを生かしてスキーマを共有している 型定義 バリデーションロジック しかし日時の表現が各層で異なり、変換が必要 アプリケーション: Dayjs オブジェクト API 通信: ISO

    文字列 日時の変換を毎回手動で行うのは大変 変換ロジックもスキーマに集約したい apps/ ├── api/ # バックエンド └── client/ # フロントエンド packages/ └── schema/ # 共有スキーマ 共有スキーマ 共有 共有 バックエンド API 通信 フロントエンド 共有スキーマ 共有 共有 バックエンド Dayjs API 通信 string(ISO 文字列) フロントエンド Dayjs Copyright ©Unique Vision Company. All Rights Reserved. 課題
  7. 日時の変換を毎回手動で行うのは大変 const startAt = dayjs(body.startAt); // ← 手動で string →

    Dayjs に変換 const endAt = dayjs(body.endAt); startAt: inserted.startAt.toISOString(), // ← 手動で Dayjs → string に変換 endAt: inserted.endAt.toISOString(), app.openapi(postCampaignRoute, async (c) => { const body = c.req.valid("json"); const [inserted] = await db .insert(campaigns) .values({ name: body.name, startAt, endAt }) .returning(); return c.json( response.detail({ uuid: inserted.uuid, name: inserted.name, }), ); }); Copyright ©Unique Vision Company. All Rights Reserved. 課題 — 日時の変換を楽にしたい
  8. Zod v4.1 から導入された機能 Zod のスキーマでデータを双方向変換できる 従来の preprocess / transform は単方向の

    変換しかできない transform ⼊⼒の型 単⽅向のみ 出⼒の型 Codec ⼊⼒の型 Dayjs encode decode 出⼒の型 string(ISO 文字列) Copyright ©Unique Vision Company. All Rights Reserved. Zod v4 Codec とは
  9. Zod スキーマ (Codec) 共有 共有 バックエンド Dayjs encode decode API

    通信 string(ISO 文字列) decode encode フロントエンド Dayjs Copyright ©Unique Vision Company. All Rights Reserved. Codec で実現するデータフロー
  10. 日時を双方向変換するスキーマ 変換ロジックをスキーマに集約できる 利用例 変換ロジックを意識せずに使える export const datetimeSchema = () =>

    z.codec( z.iso.datetime(), z.custom<Dayjs>((val) => dayjs.isDayjs(val)), { decode: (val) => dayjs(val), encode: (val) => val?.toISOString(), }, ); export const campaignSchema = z.object({ uuid: z.uuid(), name: z.string(), startAt: datetimeSchema(), endAt: datetimeSchema(), }); export const campaignCreateSchema = campaignSchema.omit({ uuid: true }); Copyright ©Unique Vision Company. All Rights Reserved. Zod スキーマの設計
  11. @hono/zod-openapi でルートを定義する リクエスト・レスポンス両方に Zod スキーマを指定する body: { content: { "application/json":

    { schema: campaignCreateSchema } } }, "application/json": { schema: detailResponse(campaignSchema) }, export const postCampaignRoute = createRoute({ method: "post", path: "/api/v1/campaigns", request: { }, responses: { 200: { description: "キャンペーン", content: { }, }, }, }); Copyright ©Unique Vision Company. All Rights Reserved. バックエンドでの利用方法
  12. ハンドラー実装例 — Codec 導入によりボイラープレート削減 @hono/zod-openapi がリクエストのバリデーションと日時の変換をする @hono/zod-openapi はレスポンスに対しては何もしない ユーティリティ response.detail

    を作成して encode 呼び出しを共通化 encode はスキーマに未定義のプロパティを除去する( inserted をそのまま渡せる) app.openapi(postCampaignRoute, async (c) => { const body = c.req.valid("json"); const [inserted] = await c.var.db .insert(campaigns) .values({ name: body.name, startAt: body.startAt, endAt: body.endAt }) .returning(); return c.json(response.detail(campaignSchema, inserted)); }); Copyright ©Unique Vision Company. All Rights Reserved. バックエンドでの利用方法
  13. API クライアントを作成 hc ( hono/client )を利用する encode / decode をおこなう薄いラッパーを作成

    export class Api { client = hc<AppType>("").api.v1; async postCampaign(data: CampaignCreate): Promise<Campaign> { return requestData( this.client.campaigns.$post, { json: data }, { request: campaignCreateSchema, // encode: Dayjs → string response: campaignSchema, // decode: string → Dayjs }, ); } } Copyright ©Unique Vision Company. All Rights Reserved. フロントエンドでの利用方法
  14. フォームでバリデーションするときの注意点 — parse ではなく encode を使う フォームの値は Dayjs オブジェクト parse

    方向は string を期待するためエラーにな る encode 方向でバリデーションする Vue.js のコンポーザブルで共通化 Dayjs encode parse(decode 方向) string (ISO ⽂字列) const validate = <T extends z.ZodType>( schema: T, data: Partial<z.output<T>>, ): z.output<T> => { schema.encode(data as z.output<T>); return data as z.output<T>; }; Copyright ©Unique Vision Company. All Rights Reserved. フロントエンドでの利用方法
  15. Zod v4 Codec を使うと、スキーマに型変換ロジックを埋め込める ハンドラーや API クライアントで変換を意識せずに使える Hono・日時変換を題材にしたが、他のフレームワークやデータ型にも応用できる Zod スキーマ

    (Codec) 共有 共有 バックエンド Dayjs encode decode API 通信 string(ISO 文字列) decode encode フロントエンド Dayjs Copyright ©Unique Vision Company. All Rights Reserved. まとめ