メインコンテンツへスキップ

tRPCの紹介

· 1分で読める
Alex / KATT 🐱
Creator of tRPC
非公式ベータ版翻訳

このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →

tRPCは(Node.js)サーバーからクライアントまでエンドツーエンドの型安全性を提供します。しかも_型を宣言する必要すらありません_。バックエンドで行うのは関数内でデータを返すことだけ。フロントエンドではエンドポイント名に基づいてそのデータを使用するだけです。

👋 GitHubでは「KATT」として活動しているAlexです。皆さんにtRPCというライブラリを紹介したいと思います。まだこれに関する記事は公開していませんが、まずはこのイントロダクションで始動します(GitHubでは既に530🌟を超えています!)。今後記事や動画での紹介を予定しています!最新情報を追いたい方や質問がある方は、Twitterで@alexdotjsをフォローしてください。

tRPCエンドポイントとクライアント呼び出しの例: デモ画像

React向けライブラリ(@trpc/react)は優れたreact-queryの上に構築されていますが、クライアントライブラリ(@trpc/client)はReactなしでも動作します(Svelte/Vue/Angular向けの専用ライブラリを開発したい方はぜひご連絡ください!)。

コード生成は不要で、既存のNext.js/CRA/Expressプロジェクトに簡単に追加できます。

以下はhelloという名前のtRPCプロシージャ(エンドポイント)の例です。string型の引数を取ります。

tsx
const appRouter = trpc.router().query('hello', {
input: z.string().optional(),
resolve: ({ input }) => {
return {
text: `hello ${input ?? 'world'}`,
};
},
});
export type AppRouter = typeof appRouter;
tsx
const appRouter = trpc.router().query('hello', {
input: z.string().optional(),
resolve: ({ input }) => {
return {
text: `hello ${input ?? 'world'}`,
};
},
});
export type AppRouter = typeof appRouter;

そして型安全なクライアントでの使用例:

tsx
import type { AppRouter } from './server';
async function main() {
const client = createTRPCClient<AppRouter>({
url: `http://localhost:2022`,
});
const result = await client.query('hello', '@alexdotjs');
console.log(result); // --> { text: "hello @alexdotjs" }
}
main();
tsx
import type { AppRouter } from './server';
async function main() {
const client = createTRPCClient<AppRouter>({
url: `http://localhost:2022`,
});
const result = await client.query('hello', '@alexdotjs');
console.log(result); // --> { text: "hello @alexdotjs" }
}
main();

これだけで型安全性が実現されます! resultの型はバックエンド関数が返す内容から推論されます。入力データの型もバリデータの戻り値から推論されるため、データをそのまま安全に使用できます。実際、入力データは必ずバリデータを通す必要があります(tRPCはzod/yup/カスタムバリデータをすぐに利用可能です)。

上記の例を試せるCodeSandboxリンク: https://githubbox.com/trpc/trpc/tree/main/examples/standalone-server (プレビューではなくターミナル出力を確認してください!)

待って?バックエンドのコードをクライアントにインポートしてるの? - いえ、実際には違います

一見そう見えますが、サーバーからクライアントへコードは共有されていません。TypeScriptのimport typeは「型注釈と宣言に使用される宣言のみをインポートします。常に完全に消去されるため、実行時には痕跡も残りません。」- TypeScript 3.8で追加された機能です - TypeScriptドキュメント参照

コード生成は不要で、サーバーからクライアントに型を共有する方法があれば(できればモノレポを使用している場合)、今日からアプリに導入できます。

しかし、これは始まりに過ぎません!

前述したReactライブラリを使用する場合、以下のようにデータを利用します:

tsx
const { data } = trpc.useQuery(['hello', '@alexdotjs']);
tsx
const { data } = trpc.useQuery(['hello', '@alexdotjs']);

..これでクライアント側で型安全なデータが得られます。

既存のブラウンフィールドプロジェクト(Express/Next.js用アダプタあり)にtRPCを追加可能で、CRAとも互換性があり、React Nativeでも動作するはずです。Reactに縛られていないため、SvelteやVue向けライブラリを開発したい方はぜひご連絡ください。

データの変更は?

ミューテーションはクエリと同様にシンプルで、内部的には同じものです。構文糖として別途公開されており、HTTP POSTリクエストを生成する点が異なります。

データベースを使用したもう少し複雑な例をご紹介します。これはTodoMVCのサンプル(todomvc.trpc.io)から引用したものです。詳細はGitHubリポジトリをご覧ください: https://github.com/trpc/trpc/tree/main/examples/next-prisma-todomvc

tsx
const todoRouter = createRouter().mutation('add', {
input: z.object({
id: z.string().uuid(),
data: z.object({
completed: z.boolean().optional(),
text: z.string().min(1).optional(),
}),
}),
async resolve({ ctx, input }) {
const { id, data } = input;
const todo = await ctx.task.update({
where: { id },
data,
});
return todo;
},
});
tsx
const todoRouter = createRouter().mutation('add', {
input: z.object({
id: z.string().uuid(),
data: z.object({
completed: z.boolean().optional(),
text: z.string().min(1).optional(),
}),
}),
async resolve({ ctx, input }) {
const { id, data } = input;
const todo = await ctx.task.update({
where: { id },
data,
});
return todo;
},
});

この場合のReactでの実装は以下のようになります:

tsx
const addTask = trpc.useMutation('todos.add');
return (
<>
<input
placeholder="What needs to be done?"
onKeyDown={(e) => {
const text = e.currentTarget.value.trim();
if (e.key === 'Enter' && text) {
addTask.mutate({ text });
e.currentTarget.value = '';
}
}}
/>
</>
)
tsx
const addTask = trpc.useMutation('todos.add');
return (
<>
<input
placeholder="What needs to be done?"
onKeyDown={(e) => {
const text = e.currentTarget.value.trim();
if (e.key === 'Enter' && text) {
addTask.mutate({ text });
e.currentTarget.value = '';
}
}}
/>
</>
)

ひとまずここまで

冒頭でも述べたように、今回は導入編として基本的な内容をご紹介しました。さらに多くの機能が用意されています:

  • リゾルバに依存性注入するユーザー固有のリクエストコンテキスト作成 - リンク

  • ルーター向けミドルウェアサポート - リンク

  • ルーターの統合(全バックエンドロジックを単一ファイルに置きたくない場合に有用) - リンク

  • React環境で実現する最もシンプルなサーバーサイドレンダリング(@trpc/nextアダプター使用) - リンク

  • 型安全なエラーフォーマット - リンク

  • データトランスフォーマー(Date/Map/Setオブジェクトをクライアント-サーバー間で転送) - リンク

  • React Query向けヘルパー機能

実際に始めるには、Next.js向けスタートガイドにいくつかのサンプルが用意されています。

最新情報はTwitterでフォローしてください!