Share
Embed
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
Ryutaro Yako
May 20, 2026
Transcript
22 slides
-
2026.05.22 TSKaigi 2026 / Ryutaro Yako Copyright ©Unique Vision Company.
All Rights Reserved. Zod v4 Codec で スキーマに型変換を埋め込む REST API 設計 -
Profile 名前: Ryutaro Yako 所属: ユニークビジョン株式会社 役割: テックリード Experience 7
名のエンジニアが所属する開発チーム toB 向けアプリケーションをフルスタック TypeScript で開発中 Copyright ©Unique Vision Company. All Rights Reserved. 自己紹介 -
当社はソーシャルメディアを利用した、ソーシャルメディア総合管理ツールや X・LINE・Instagram・TikTokなどSNSマーケティングツールを多数提供しています。 Belugaスタジオ X/Facebook/Instagramなどのアカウ ントを一元管理できるソーシャルメディ ア総合管理ツールです。企業のSNS運用 をサポートします。 Beluga キャンペーン フォロー&リポストに代表されるXのイ
ンスタントウィンをはじめとしたキャン ペーンを複数展開しています。質の高い キャンペーンの実施を支援しています。 その他 SNSキャンペーンに連動した、生成AIを 活用したシステムやプラットフォームが 提供する独自ツールやシステム開発も順 次行っています。 Copyright ©Unique Vision Company. All Rights Reserved. 事業内容 -
01. 背景と課題 02. Zod v4 Codec とは 03. Zod スキーマの設計
04. バックエンドでの利用方法 05. フロントエンドでの利用方法 06. まとめ Copyright ©Unique Vision Company. All Rights Reserved. アジェンダ -
フルスタック 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. 背景 -
モノレポを生かしてスキーマを共有している 型定義 バリデーションロジック しかし日時の表現が各層で異なり、変換が必要 アプリケーション: Dayjs オブジェクト API 通信: ISO
文字列 日時の変換を毎回手動で行うのは大変 変換ロジックもスキーマに集約したい apps/ ├── api/ # バックエンド └── client/ # フロントエンド packages/ └── schema/ # 共有スキーマ 共有スキーマ 共有 共有 バックエンド API 通信 フロントエンド 共有スキーマ 共有 共有 バックエンド Dayjs API 通信 string(ISO 文字列) フロントエンド Dayjs Copyright ©Unique Vision Company. All Rights Reserved. 課題 -
日時の変換を毎回手動で行うのは大変 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. 課題 — 日時の変換を楽にしたい -
Zod v4.1 から導入された機能 Zod のスキーマでデータを双方向変換できる 従来の preprocess / transform は単方向の
変換しかできない transform ⼊⼒の型 単⽅向のみ 出⼒の型 Codec ⼊⼒の型 Dayjs encode decode 出⼒の型 string(ISO 文字列) Copyright ©Unique Vision Company. All Rights Reserved. Zod v4 Codec とは -
Zod スキーマ (Codec) 共有 共有 バックエンド Dayjs encode decode API
通信 string(ISO 文字列) decode encode フロントエンド Dayjs Copyright ©Unique Vision Company. All Rights Reserved. Codec で実現するデータフロー -
日時を双方向変換するスキーマ 変換ロジックをスキーマに集約できる 利用例 変換ロジックを意識せずに使える 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 スキーマの設計 -
@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. バックエンドでの利用方法 -
ハンドラー実装例 — 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. バックエンドでの利用方法 -
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. フロントエンドでの利用方法 -
フォームでバリデーションするときの注意点 — 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. フロントエンドでの利用方法 -
Zod v4 Codec を使うと、スキーマに型変換ロジックを埋め込める ハンドラーや API クライアントで変換を意識せずに使える Hono・日時変換を題材にしたが、他のフレームワークやデータ型にも応用できる Zod スキーマ
(Codec) 共有 共有 バックエンド Dayjs encode decode API 通信 string(ISO 文字列) decode encode フロントエンド Dayjs Copyright ©Unique Vision Company. All Rights Reserved. まとめ