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

「trpc」タグがついた投稿 1 件

すべてのタグを表示

·5 分で読めます
Alex / KATT 🐱

私は 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 (
<>
<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 = '';
}
}}
/>
</>
)

今のところ終わりです。

とにかく、言ったように、私はただ勢いをつけたかっただけです。他にもたくさんのことがあります

  • リゾルバーに依存性注入されるユーザー固有のデータの着信リクエストのコンテキストを作成する - リンク
  • ルーターのミドルウェアサポート - リンク
  • ルーターのマージ(すべてのバックエンドデータを 1 つのファイルにまとめたくないでしょう) - リンク
  • @trpc/next アダプターを使用した、React ランドでこれまでにないほど簡単なサーバーサイドレンダリング - リンク
  • 型安全なエラーフォーマット - リンク
  • データトランスフォーマー(ネットワーク上で Date/Map/Set オブジェクトを使用する) - リンク
  • React Query のヘルパー

始めるには、Next.js の入門にいくつかの例があります。

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