私はAlex、GitHubでは"KATT"です。tRPCというライブラリについてお話ししたいと思います。まだ記事は公開していませんが、勢いを付けるためにこの紹介記事を書いています(しかし、GitHubではすでに530個以上のスターを獲得しています)。今後、記事や動画による紹介も予定しています! 最新情報を入手したり、質問をしたい場合は、Twitterで@alexdotjsをフォローしてください。
簡単に言うと、tRPCは(Node.js)サーバーからクライアントまで、型を宣言することなくエンドツーエンドの型安全性を提供します。バックエンドでは関数でデータを返すだけで、フロントエンドではエンドポイント名に基づいてそのデータを使用します。
tRPCエンドポイントとクライアント呼び出しを行う場合、以下のように表示されます。
優れたreact-queryの上に構築されたReact用ライブラリ(@trpc/react
)を作成しましたが、クライアントライブラリ(@trpc/client
)はReactなしでも動作します(特定のSvelte/Vue/Angular/[..]ライブラリを構築したい場合は、ご連絡ください!)
コード生成は不要で、既存のNext.js/CRA/Expressプロジェクトに簡単に追加できます。
例
string
型の引数を取る、hello
というtRPCプロシージャ(別名エンドポイント)の例を次に示します。
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/next/examples/standalone-server(プレビューではなく、ターミナルの出力を確認してください!)
え?バックエンドからクライアントにコードをインポートしているのですか? - いいえ、実際にはインポートしていません。
そう見えるかもしれませんが、サーバーからクライアントにコードは共有されません。TypeScriptのimport type
は "[..]型注釈と宣言に使用される宣言のみをインポートします。常に完全に削除されるため、実行時には痕跡が残っていません。" - TypeScript 3.8で追加された機能 - TypeScriptのドキュメントを参照してください。
コード生成は不要です。サーバーからクライアントに型を共有する方法があれば(すでにモノレポを使用していることが望ましい)、今日からアプリにこれを追加できます。
しかし、これはまだ始まりに過ぎません!
前述したように、Reactライブラリがあります。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のライブラリを作成したい場合は、ご連絡ください。
データの変更はどうですか?
ミューテーションはクエリと同じくらい簡単です。実際には内部的には同じですが、構文上の都合で異なって公開されており、GETリクエストではなくHTTP POSTリクエストを生成します。
データベースを使用した、もう少し複雑な例を次に示します。これは、todomvc.trpc.io / https://github.com/trpc/trpc/tree/next/examples/next-prisma-todomvcにある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 (<><inputplaceholder="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 (<><inputplaceholder="What needs to be done?"onKeyDown={(e) => {const text = e.currentTarget.value.trim();if (e.key === 'Enter' && text) {addTask.mutate({ text });e.currentTarget.value = '';}}}/></>)
とりあえず、終わりです。
とにかく、言ったように、私はただ勢いをつけたかっただけです。他にもたくさんのことがあります。
- リゾルバーに依存性注入されるユーザー固有データの着信リクエストのコンテキストを作成する - リンク
- ルーターのミドルウェアサポート - リンク
- ルーターのマージ(すべてのバックエンドデータを1つのファイルにまとめたくないでしょう) - リンク
@trpc/next
アダプターを使用した、Reactでこれまでで最も簡単なサーバーサイドレンダリング - リンク- 型安全なエラーフォーマット - リンク
- データトランスフォーマー(ネットワークを介してDate/Map/Setオブジェクトを使用する) - リンク
- React Queryのヘルパー
始めるには、Next.jsの入門ガイドにいくつかの例があります。