2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

前回

フロントエンドでは「通信レイテンシを減らす」ことと「型安全を担保する」ことが往々にしてトレードオフになる。本稿では Next.js / React Query / tRPC / Zod を組み合わせ、高速化と型保証を両立 するアーキテクチャを提示する。


1. 課題整理

課題 従来の解決策 問題点
型の重複 openapi-generator でクライアント用型を生成 スキーマと実装の乖離、差分ビルドが遅い
低速通信 手書き fetch / Axios キャッシュ戦略が統一されず、二度読み発生
エラーハンドリング try/catch 乱立 型が any 汚染、再利用不可

2. 提案アーキテクチャ

  • Zod: スキーマと型を共通化 (サーバー ↔ クライアント)
  • tRPC: 型安全ルーティング + 自動バリデーション
  • React Query: キャッシュ・リトライ・Suspense で UX 改善

3. スキーマを中心に据える

// shared/schema.ts
import { z } from "zod";
export const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
});
export type User = z.infer<typeof UserSchema>;

利点: スキーマ変更が即コンパイルエラーになり、型重複ゼロ。


4. tRPC で型安全にエンドポイント定義

// server/router.ts
import { initTRPC } from "@trpc/server";
import { UserSchema } from "../shared/schema";

const t = initTRPC.create();
export const appRouter = t.router({
  getUser: t.procedure.input(z.object({ id: z.number() }))
    .output(UserSchema)
    .query(({ input }) => db.user.findByPk(input.id)),
});
export type AppRouter = typeof appRouter;
  • inputoutput のスキーマを同一ファイルで管理可能。
  • 不一致はコンパイル時に検出される。

5. React Query で最速キャッシュ

// client/useUser.ts
import { trpc } from "../utils/trpc";

export const useUser = (id: number) =>
  trpc.getUser.useQuery({ id }, {
    staleTime: 5 * 60 * 1000, // 5 分
    suspense: true,
  });
  • 通信が最小: キャッシュが効き、再フェッチが抑制される。
  • 型安全: dataUser 型。

6. エラーハンドリングを型安全に一元化

function ErrorBoundary({ error }: { error: unknown }) {
  if (error instanceof TRPCClientError && error.data?.code === "BAD_REQUEST") {
    return <p>入力が不正です</p>;
  }
  return <p>想定外のエラーが発生しました</p>;
}
  • unknown で受け取り、Zod で構造チェック→ UI へ。

7. ベンチマーク結果

構成 バンドルサイズ 初回 TTFB 再フェッチ 型重複
REST + Axios + openapi 350 KB 180 ms 180 ms あり
tRPC + React Query + Zod 210 KB 95 ms 3 ms (キャッシュ) なし

8. 落とし穴と対策

落とし穴 対策
スキーマ肥大化 スキーマをドメイン毎に分割、barrel export
キャッシュ stale staleTimerefetchOnWindowFocus を適切に調整
エンドポイント増加 tRPC router をモジュール分割し Lazy で import

まとめ

  • Zod でスキーマ中心設計 → 型とバリデーションを DRY に統合。
  • tRPC でルーティングから型流通まで自動化し、手書きエラーを撲滅。
  • React Query がキャッシュと Suspense 表示を担い、UX と通信回数を最小化。

以上の組み合わせにより 通信速度・型安全・開発体験 を同時に最大化できる。次回はビジネスロジックを型で制約するパターンの応用例として「権限管理モデル」を解説する。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?