はじめに
いまのプロジェクトでtRPCを使用していて、開発体験がかなり良かったので、忘れないようにREST APIの課題とtRPCのメリットをまとめていきます。
本記事の対象者
- React(TypeScript)を使用したフロントエンドエンジニア
- 普段REST APIを使用してバックエンドと通信しており、tRPCは使用したことがないエンジニア
- tRPCのメリットを理解したいエンジニア
tRPCとは
tRPCは、TypeScriptを使用してエンドツーエンドの型安全性を実現するライブラリです。フロントエンドとバックエンド間の通信を簡素化し、型の不一致によるバグを防ぎ、開発速度を向上させます。
REST APIを使用している場合の課題
私も含めて多くの開発者は、フロントエンドとバックエンドでデータのやり取りをするのにREST APIを使用しています。しかし、REST APIでは下記のような課題が発生することがあります。
フロントエンドとバックエンドの型の不一致によるバグの発生
REST APIでは、フロントエンドとバックエンドで別々に型定義を行う必要があリます。そのため、型定義の重複が起こり、それによる修正漏れや修正ミスなどが起こりやすくなります。そして、結果として型の不一致が起こり、バグが発生してしまいます。
以下は、REST APIを使用した場合のフロントエンド側のコード例です。
type Todo {
id: number;
title: string;
completed: boolean;
}
const fetchTodos = async () => {
const res = await fetch('/api/todos');
const data: unknown = await res.json();
if (!Array.isArray(data))
throw new Error('Invalid res data');
const todos: Todo[] = data.map((item) => {
if (
typeof item.id === 'number' &&
typeof item.title === 'string' &&
typeof item.completed === 'boolean'
) {
return item as Todo;
} else {
throw new Error('Invalid todo item');
}
});
setTodos(todos);
};
スキーマの定義が大変
REST API+OpenAPIを利用する場合、OpenAPIのファイルを手動で作成・更新する必要があります。慣れていれば楽なのかもしれませんが、都度調べながら定義をし、さらにドキュメントの定義と実装がに相違がないかを確認する作業は地味に大変です。。
tRPCを使うことで便利になるところ
tRPCを使うことで、以下のような利点が得られます。
フロントエンドとバックエンドの型安全性が向上する
tRPCを使用すると型の定義が1つになり、フロントエンドとバックエンドで型の安全性が保証されます。例えば下記はtRPCを用いた場合のフロントエンドのコードです。先ほどの例と比べて型チェックが不要になっていることがわかります。
import { createReactQueryHooks } from '@trpc/react';
import type { AppRouter } from './server';
const trpc = createReactQueryHooks<AppRouter>();
// todosに対して型チェックを手動で実装する必要がない
const { data: todos } = trpc.useQuery({});
setTodos(todos)
ちなみにバックエンドの実装は下記のようになります。
import { initTRPC } from "@trpc/server";
import * as trpcNext from "@trpc/server/adapters/next";
type Todo = {
id: number;
text: string;
};
export const t = initTRPC.create();
export const appRouter = t.router({
todos: t.procedure.query(() => {
const todos: Todo[] = [
{
id: 1,
text: "todoA",
},
{
id: 2,
text: "todoB",
},
];
return todos;
}),
});
export type AppRouter = typeof appRouter;
export default trpcNext.createNextApiHandler({
router: appRouter,
createContext: () => ({}),
});
zodと組み合わせることでスキーマ定義が簡単になる
tRPCのリクエスト定義にzodというライブラリを使用すると、さらに開発体験が良くなります。zodはTypeScriptによるスキーマ定義と、それをもとにバリデーションを行ってくれます。これによりドキュメントを見ながらスキーマ定義とずれていないか?リクエストのバリデーションを忘れていないか?などの余計な心配が不要になります。
最後に
tRPCを使うことで、フロントエンドとバックエンドの型安全性を確保しながら、効率的にアプリケーション開発を進められます!ぜひ、tRPCを導入して開発体験の向上を体感してください!