APIの利用拡大に伴い、APIの悪用や攻撃も増加しています。APIは、悪用されると対象のアプリや関連コンポーネントを大きく制御できるため、ハッカーにとって魅力的な標的となります。
このため、API開発においてバックエンドとフロントエンドの間で強固な信頼関係を築くことが推奨されます。tRPCは最も一般的に利用されるAPIプロトコルの一つです。Wallarmは、API利用者に安全な運用方法を伝えることを目指しており、本ガイドもその一環です。
まず本ガイドは、TypeScript Remote Procedure、つまりtRPCプロトコルの基本的な意味から解説します。初心者向けに言えば、tRPCはコード生成やスキーマ作成なしで型安全なAPIを生成・利用する方法の一つです。これまでのところ、どのリモート環境からでもバックエンドの機能をクライアント側へ呼び出すための、最もシンプルで軽量なライブラリとなっています。
tRPCプロトコルを採用することで、バックエンドとフロントエンドの両チームがTypeScriptで作業できるようになります。
バックエンドでプロトコルが動作すると、バックエンドチームはクエリの定義、ミューテーションの生成、統一型の作成を容易に行えます。一方、フロントエンドでは、tRPCが工場のように動作し、バックエンドから一種類の型だけを受け取り、それを使ってクエリやミューテーションを実行します。
tRPCの意義や採用理由について知りたい方も多いでしょう。このセクションでは、その点を解説します。tRPCの主な目的は、APIのバックエンドとフロントエンドをしっかり連携させ、重要なデータを円滑に伝達することにあります。
tRPCはTypeScriptベースのライブラリであるため、シームレスな開発体験を実現するためのIDEとの優れた統合を備えています。コードやスキーマの作成なしでAPI呼び出しをシンプルに行えるよう設計されています。
自動で型が生成され、プロセスの複雑さを解消する手段として有効です。さらに、出力や入力は自動で検証されます。
前述の通り、tRPCは非常にシンプルで、スキーマやコードに依存せずにAPIを生成します。そのため、動作の理解も容易です。フロントエンドで通信を開始するには、バックエンドのデータを呼び出すためのプロシージャが必要となります。
tRPCでは、主に2種類のプロシージャが利用されます。1つ目は、特定のデータ取得をリクエストするクエリです。2つ目は、バックエンドのデータを更新、削除、または作成するためのミューテーションです。
tRPCを利用する際はmonorepoの採用が強く推奨されます。現代のアプリ開発では、バックエンドとフロントエンドを分けるために複数のリポジトリが用いられるケースが多いからです。
monorepoを採用せずにtRPCの定義を行う場合、多くのエラーが発生する可能性があります。
開発者コミュニティでは、tRPCとGraphQLの比較が話題になっています。tRPCはGraphQLの軽量版と考えられることが多いため、比較は避けがたいテーマです。現代のアプリのバックエンドでは、両者がそれぞれ異なる利点を提供しながら多用されています。
たとえば、tRPCは高速でコード不要の点が評価される一方、GraphQLは複数のデータを1つのクエリに統合できる機能で知られています。しかし、直接的なtRPCとGraphQLの比較は適切と言えるでしょうか?
率直に言えば、両者は全く異なるものです。GraphQLはクエリ用の言語であるのに対し、tRPCはライブラリです。
GraphQLは、HTTPトランスポート層として他のプロトコルと組み合わせて利用できる柔軟なリソースと捉えるのが適切です。一方、tRPCは主にTypeScriptおよびJavaScriptで動作するため、柔軟性には限界があります。多くのバッテリーやアダプタが提供されていても、基本的な自由度は限定的です。
tRPC APIは主にフロントエンドで使われ、バックエンドとの円滑な通信を担います。バックエンドに対するAPI呼び出しを行う際、tRPCに指示すればその役割を果たします。したがって、tRPCはAPIのORMと呼ぶのが適切です。
次に、使いやすさの観点で両者を比較します。シンプルさにおいてtRPCは非常に扱いやすいツールです。GraphQLや他のクエリ言語と比べ、コード生成を必要としないため、扱いやすさは抜群です。GraphQL API生成では、コードやスキーマが必要となるため、プロセスは長く手間がかかります。
そのプロセスは、スキーマ定義、正確なデータ取得のためのリゾルバ記述、スキーマを元にした型生成など、多くのステップを踏む必要があります。全体として時間と労力がかかります。
労力を軽減する最善の方法は、コードファーストのGraphQLライブラリを用い、リゾルバ記述からスキーマを抽出する方法です。
tRPCによるAPI生成は、コードやスキーマ生成に一切依存しません。プロシージャを定義し、サーバーを構築するだけで、バックエンドとの通信が可能となります。
シンプルさを重視するなら、tRPCはGraphQLより優れていると言えますが、これだけを基準に判断すべきではありません。両者は異なる特徴を持つリソースです。tRPCとGraphQLのどちらが適しているかを判断するには、以下の点を深く検討する必要があります。
様々なデータソースを問い合わせ、サービスを分離する必要がある場合はGraphQLが適しています。一方、バックエンドとフロントエンドの分離が求められない場合はtRPCが推奨されます。
言語の自由度を重視するなら、GraphQLが望ましいです。tRPCはこの点において厳しく、TypeScript以外との併用は難しいです。
現代のアプリ要求に応じてバックエンドを拡張する必要がある場合、GraphQLが適しています。拡張性は、バックエンドとフロントエンドが連携したスキーマにより実現されます。
開発要件を整理し、単純なものか複雑なものか判断してください。単純な要件であれば、余計なコードがなくシンプルなtRPCが適しています。tRPCはAPIの余計な作業を排除し、シンプルに保ちます。
複雑な要件については、コード、多言語サポート、スキーマなどを備えるGraphQLが適しています。
まずこれらの点を整理し、求める機能に応じたリソースを選んでください。両者はシーンに応じてそれぞれ強みを発揮します。さらに、tRPCとRESTの違いに興味がある場合は、ts-restとは異なり、tRPCが契約形式でAPIの実装を定義する点にご留意ください。
gRPCもtRPCも、サーバーやクライアント向けに型安全なAPIを提供するために使用されるため、同じものと見なされがちです。しかし、詳細に分析すると明確な違いが見えてきます。両者は型安全なAPI呼び出しに対して異なるアプローチを採っています。
たとえば、gRPCはprotobufプロトコルを利用して型安全なAPI呼び出しをサポートします。このコンパクトなワイヤプロトコルは、後にクライアントやサーバーの開発に利用されるプロトコルファイルを特徴としています。
gRPCでは、Python、Java、Go、C++などの言語が利用可能です。
しかし、gRPCは読みづらくデバッグが難しいコードを生成します。また、スキーマに変更があるたびにコードの再生成が必要となり、ブラウザ環境では煩雑で重いため適していません。
一方、tRPCは型安全なAPI呼び出しのためのライブラリです。gRPCとは異なり、tRPCはワイヤリングされずHTTPを使用します。gRPCのコードが複雑な一方、tRPCは扱いやすいです。ただし、TypeScriptのみのサポートとなるため、他言語との併用はできません。
tRPCは初期設定でコード生成を強制しません。スキーマを生成し、クライアントやサーバーに取り込むだけで型安全なAPI呼び出しが可能となります。Webネイティブなツールであり、JSON-RPC仕様を採用しています。
tRPCは以下のような優れた特徴により、開発者コミュニティで注目を集めています。
非常に軽量なライブラリであるため、tRPCは扱いやすいツールです。多くの準備を必要とせず、コード生成やスキーマの管理に煩わされることがありません。初心者でもすぐに使い始めることができます。
tRPCは、呼び出しから型を取得し、コードがAPI通信に関与しないため、API呼び出しが非常にシンプルです。基盤となるTypeScriptも静的型付けの利点からシンプルで、理解しやすく実践しやすいです。
コンパイル時に型定義をチェックするため、アプリの応答速度が向上します。
monorepoを好む場合、tRPCを試す価値があります。効果的なバージョン管理やgit履歴の維持に役立ち、旧バージョンのアプリを利用する場面でも、フロントエンドが適切なバックエンドバージョンを問い合わせられるようになります。
monorepoがない場合、フロントエンドが対応するバックエンドに問い合わせできず、旧バージョンとの連携が難しくなります。
短期間でAPIを開発する必要がある場合、tRPCは最適な選択です。型推論により開発時間を大幅に短縮し、自動で必要なデータを検出できます。
さらに主要なIDEとシームレスに統合できるため、開発が迅速かつスムーズに進みます。
tRPCには、AWS Lambda、Fastify、Express、tRP React、Next.jsなどに対応したアダプタが充実している、厳選されたライブラリが付属しています。
Next.jsプロジェクトでの開発において、tRPCは最適なツールです。バックエンドとフロントエンドをシームレスに統合し、フルスタック開発を容易にします。
これまでの優れた点にもかかわらず、tRPCにはいくつかの欠点があります。使用前にこれらの制約を十分に考慮する必要があります。
tRPCの主な欠点は、TypeScriptに限定される点です。他の言語で作業したり連携することができませんが、主要なJavaScriptフレームワークとは互換性があり、既存のJavaScriptプロジェクトにも容易に組み込めます。
また、tRPCは小規模なプロジェクトで最適な結果を発揮します。大規模プロジェクトで同じ容易さと堅牢性を期待するのは難しいため、もし大規模プロジェクトでの利用を計画する場合は、相応の労力を覚悟する必要があります。
最後に、クライアント側のクエリについてカスタマイズができない点にも注意が必要です。サーバーが提供するデフォルトの機能に依存するため、クエリだけでなくサブスクリプションのカスタマイズもサポートされていません。
前述の通り、tRPCの導入は非常に簡単で、初心者でも迷うことなく利用を開始できます。以下は、tRPCをプロジェクトで利用する際の手順です。
まず、tRPC API開発のためのプロシージャを定義します。プロシージャとは、APIのバックエンド開発に必要な機能のことです。通常、プロシージャはいくつかの要素から構成され、サブスクリプション、ミューテーション、クエリなどが該当します。ルーター上ではこれらがまとめて利用されます。
主にZodバリデータがプロシージャ生成に用いられ、クライアントからの入力とプロシージャの期待値が一致しているか確認します。プロシージャ生成後、基本的なテキスト文字列がクエリ結果として返されます。
const t = initTRPC.create();
const router = t.router;
const publicProcedure = t.procedure;
const appRouter = router({
greeting: publicProcedure
.input(z.object({ name: z.string() }))
.query((req) => {
const { input } = req;
const input: {
name: string;
}
return {
text: `Hello ${input.name}` as const,
};
}),
});
export type AppRouter = typeof appRouter;
次に、HTTPサーバーを作成します。このためにappRouterが必要です。下記のコマンドを利用してHTTPサーバーを生成してください。
const { listen } = createHTTPServer({
router: appRouter,
});
// APIは現在、ポート3000を監視します!
listen(3000);
指示に従えば、すぐに機能するtRPCサーバーが稼働します。tRPC APIは複数のルーターをサポートしているため、tRPC React、Fetch API、Node HTTPなど多様なサーバーとの連携が可能です。
機能するサーバーが整えば、次はクライアントを開発し、データの問い合わせを始めることができます。
const trpc = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000/trpc',
}),
],
});
const res = await trpc.greeting.query({ name: 'John' });
const res: {
text: `Hello ${string}`;
}
このサンプルコードでは、クライアント作成時にAppRouter型を指定しているため、TypeScriptのオートコンプリートやIntellisenseがバックエンドAPIと連携します。全体のプロセスはコード不要です。
以上です。これで機能するtRPC APIが整いました。
本ガイドを通して、tRPCとその動作について理解が深まりました。コードやスキーマに関与することなく、APIを型安全に実現できる簡単な方法であることが分かりました。コードが不要なため、短時間で機能的なAPIを構築でき、初心者にも最適です。
ただし、tRPCを採用する際はTypeScript依存である点、また小規模プロジェクト向けである点に留意する必要があります。tRPCの利用経験はいかがでしたか?その機能に納得できたか、改善が必要か、ぜひフィードバックを共有してください。
最新情報を購読