メインコンテンツへスキップ
バージョン: 11.x

クイックスタート

tRPCはRESTGraphQLの概念を組み合わせています。どちらにも馴染みがない場合は、キーとなる概念をご覧ください。

インストール

tRPCは複数のパッケージに分割されているため、必要なものだけをインストールできます。コードベースの適切なセクションに必要なパッケージをインストールするようにしてください。このクイックスタートガイドでは、シンプルさを保ち、バニラクライアントのみを使用します。フレームワークガイドについては、Reactでの使用Next.jsでの使用を確認してください。

要件
  • tRPCにはTypeScript >= 4.7.0が必要です
  • 公式にはstrictモード以外はサポートしていないため、tsconfig.json"strict": trueを使用することを強く推奨します。

まず、@trpc/serverパッケージと@trpc/clientパッケージをインストールすることから始めましょう

npm install @trpc/server@next @trpc/client@next

バックエンドルーターの定義

tRPCを使用してタイプセーフなAPIを構築する手順を見ていきましょう。まず、このAPIには次のTypeScriptシグネチャを持つ3つのエンドポイントが含まれます

ts
type User = { id: string; name: string; };
userList: () => User[];
userById: (id: string) => User;
userCreate: (data: { name: string }) => User;
ts
type User = { id: string; name: string; };
userList: () => User[];
userById: (id: string) => User;
userCreate: (data: { name: string }) => User;

1. ルーターインスタンスの作成

まず、tRPCバックエンドを初期化しましょう。tRPCオブジェクト全体ではなく、別のファイルでこれを行い、再利用可能なヘルパー関数をエクスポートするのが良い慣習です。

server/trpc.ts
ts
import { initTRPC } from '@trpc/server';
 
/**
* Initialization of tRPC backend
* Should be done only once per backend!
*/
const t = initTRPC.create();
 
/**
* Export reusable router and procedure helpers
* that can be used throughout the router
*/
export const router = t.router;
export const publicProcedure = t.procedure;
server/trpc.ts
ts
import { initTRPC } from '@trpc/server';
 
/**
* Initialization of tRPC backend
* Should be done only once per backend!
*/
const t = initTRPC.create();
 
/**
* Export reusable router and procedure helpers
* that can be used throughout the router
*/
export const router = t.router;
export const publicProcedure = t.procedure;

次に、後でプロシージャを追加する、一般的にappRouterと呼ばれるメインルーターインスタンスを初期化します。最後に、クライアント側で後で使用するルーターのタイプをエクスポートする必要があります。

server/index.ts
ts
import { router } from './trpc';
 
const appRouter = router({
// ...
});
 
// Export type router type signature,
// NOT the router itself.
export type AppRouter = typeof appRouter;
server/index.ts
ts
import { router } from './trpc';
 
const appRouter = router({
// ...
});
 
// Export type router type signature,
// NOT the router itself.
export type AppRouter = typeof appRouter;

2. クエリプロシージャの追加

publicProcedure.query()を使用して、クエリプロシージャをルーターに追加します。

以下は、データベースからユーザーのリストを返すuserListという名前のクエリプロシージャを作成します

server/index.ts
ts
import { db } from './db';
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
userList: publicProcedure
.query(async () => {
// Retrieve users from a datasource, this is an imaginary database
const users = await db.user.findMany();
const users: User[]
return users;
}),
});
server/index.ts
ts
import { db } from './db';
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
userList: publicProcedure
.query(async () => {
// Retrieve users from a datasource, this is an imaginary database
const users = await db.user.findMany();
const users: User[]
return users;
}),
});

3. 入力パーサーを使用したプロシージャ入力の検証

userByIdプロシージャを実装するには、クライアントからの入力を受け入れる必要があります。tRPCでは、入力を検証および解析するための入力パーサーを定義できます。独自の入力パーサーを定義することも、zodyupsuperstructなどの選択した検証ライブラリを使用することもできます。

publicProcedure.input()で入力パーサーを定義します。これは、以下に示すように、リゾルバー関数でアクセスできます。

入力パーサーには、z.string()z.object()などの任意のZodTypeを指定できます。

server.ts
ts
import { z } from 'zod';
 
const appRouter = router({
// ...
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const input: string
// Retrieve the user with the given ID
const user = await db.user.findById(input);
const user: User | undefined
return user;
}),
});
server.ts
ts
import { z } from 'zod';
 
const appRouter = router({
// ...
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const input: string
// Retrieve the user with the given ID
const user = await db.user.findById(input);
const user: User | undefined
return user;
}),
});
情報

このドキュメントの残りの部分では、検証ライブラリとしてzodを使用します。

4. ミューテーションプロシージャの追加

GraphQLと同様に、tRPCはクエリプロシージャとミューテーションプロシージャを区別します。

プロシージャがサーバー上で機能する方法は、クエリとミューテーションの間でそれほど変わりません。メソッド名が異なり、クライアントがこのプロシージャを使用する方法が変わりますが、それ以外はすべて同じです。

ルーターオブジェクトの新しいプロパティとして追加することにより、userCreateミューテーションを追加しましょう

server.ts
ts
const appRouter = router({
// ...
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const input: { name: string; }
// Create a new user in the database
const user = await db.user.create(input);
const user: { name: string; id: string; }
return user;
}),
});
server.ts
ts
const appRouter = router({
// ...
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const input: { name: string; }
// Create a new user in the database
const user = await db.user.create(input);
const user: { name: string; id: string; }
return user;
}),
});

APIの提供

ルーターを定義したので、それを提供できます。tRPCには多くのアダプターがあるため、選択した任意のバックエンドフレームワークを使用できます。シンプルにするために、standaloneアダプターを使用します。

server/index.ts
ts
import { createHTTPServer } from '@trpc/server/adapters/standalone';
 
const appRouter = router({
// ...
});
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
server/index.ts
ts
import { createHTTPServer } from '@trpc/server/adapters/standalone';
 
const appRouter = router({
// ...
});
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
完全なバックエンドコードを見る
server/db.ts
ts
type User = { id: string; name: string };
 
// Imaginary database
const users: User[] = [];
export const db = {
user: {
findMany: async () => users,
findById: async (id: string) => users.find((user) => user.id === id),
create: async (data: { name: string }) => {
const user = { id: String(users.length + 1), ...data };
users.push(user);
return user;
},
},
};
server/db.ts
ts
type User = { id: string; name: string };
 
// Imaginary database
const users: User[] = [];
export const db = {
user: {
findMany: async () => users,
findById: async (id: string) => users.find((user) => user.id === id),
create: async (data: { name: string }) => {
const user = { id: String(users.length + 1), ...data };
users.push(user);
return user;
},
},
};

server/trpc.ts
ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC.create();
 
export const router = t.router;
export const publicProcedure = t.procedure;
server/trpc.ts
ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC.create();
 
export const router = t.router;
export const publicProcedure = t.procedure;

server/index.ts
ts
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import { z } from "zod";
import { db } from "./db";
import { publicProcedure, router } from "./trpc";
 
const appRouter = router({
userList: publicProcedure
.query(async () => {
const users = await db.user.findMany();
return users;
}),
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const user = await db.user.findById(input);
return user;
}),
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const user = await db.user.create(input);
return user;
}),
});
 
export type AppRouter = typeof appRouter;
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
server/index.ts
ts
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import { z } from "zod";
import { db } from "./db";
import { publicProcedure, router } from "./trpc";
 
const appRouter = router({
userList: publicProcedure
.query(async () => {
const users = await db.user.findMany();
return users;
}),
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const user = await db.user.findById(input);
return user;
}),
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const user = await db.user.create(input);
return user;
}),
});
 
export type AppRouter = typeof appRouter;
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);

クライアントで新しいバックエンドを使用する

次に、クライアント側のコードに移動して、エンドツーエンドのタイプセーフの力を活用しましょう。クライアントが使用するためにAppRouterタイプをインポートすると、実装の詳細をクライアントに漏らすことなく、システムの完全なタイプセーフを実現しました。

1. tRPCクライアントのセットアップ

client/index.ts
ts
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from './server';
// 👆 **type-only** import
 
// Pass AppRouter as generic here. 👇 This lets the `trpc` object know
// what procedures are available on the server and their input/output types.
const trpc = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000',
}),
],
});
client/index.ts
ts
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from './server';
// 👆 **type-only** import
 
// Pass AppRouter as generic here. 👇 This lets the `trpc` object know
// what procedures are available on the server and their input/output types.
const trpc = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000',
}),
],
});

tRPCのリンクはGraphQLのリンクに似ており、サーバーに送信されるにデータフローを制御できます。上記の例では、httpBatchLinkを使用しています。これにより、複数の呼び出しが自動的に1つのHTTPリクエストにバッチ処理されます。リンクの詳細な使用法については、リンクのドキュメントを参照してください。

2. クエリとミューテーション

これで、trpcオブジェクトでAPIプロシージャにアクセスできます。試してみてください!

client/index.ts
ts
// Inferred types
const user = await trpc.userById.query('1');
const user: { name: string; id: string; } | undefined
 
const createdUser = await trpc.userCreate.mutate({ name: 'sachinraja' });
const createdUser: { name: string; id: string; }
client/index.ts
ts
// Inferred types
const user = await trpc.userById.query('1');
const user: { name: string; id: string; } | undefined
 
const createdUser = await trpc.userCreate.mutate({ name: 'sachinraja' });
const createdUser: { name: string; id: string; }

完全なオートコンプリート

Intellisenseを開いて、フロントエンドでAPIを調べることができます。すべてのプロシージャルートと、それらを呼び出すためのメソッドが用意されています。

client/index.ts
ts
// Full autocompletion on your routes
trpc.u;
      
client/index.ts
ts
// Full autocompletion on your routes
trpc.u;
      

自分で試してみてください!

次のステップ

ヒント

お気に入りのフレームワークでtRPCがどのようにインストールされているかを知るために、サンプルアプリを確認することを強くお勧めします。

ヒント

デフォルトでは、tRPCはDateのような複雑な型をJSON同等物(Dateの場合はstring)にマップします。これらの型の整合性を維持したい場合は、これらのサポートを追加する最も簡単な方法は、superjsonをデータトランスフォーマーとして使用することです。

tRPCには、ReactプロジェクトおよびNext.js用に設計された、より高度なクライアント側ツールが含まれています。