Open10

tRPC メモ

yoshinoyoshino

backend routerを定義する

  • router instanceを作成する
  • query procedureを追加する
  • mutation procedureを追加する

Procedures

バックエンド関数を作成するための非常に柔軟なプリミティブです。ビルダパターンを使用しているので、バックエンドアプリケーションの様々な部分に対して再利用可能なベースプロシージャを作成することができます。プロシージャはRESTエンドポイントや関数と同等と見なすことができます。

以下2つのエンドポイントを持つことを想定

userById(id: string) => { id: string; name: string; }.
userCreate(data: { name: string }) => { id: string; name: string; }.
yoshinoyoshino

router instanceを作成する

server.ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC.create();
 
const router = t.router;
const publicProcedure = t.procedure;
 
const appRouter = router({});
 
// Export type router type signature,
// NOT the router itself.
export type AppRouter = typeof appRouter;
yoshinoyoshino

query procedureを追加する

server.ts
// @filename: server.ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC.create();
 
interface User {
  id: string;
  name: string;
}
 
const userList: User[] = [
  {
    id: '1',
    name: 'KATT',
  },
];
 
const appRouter = t.router({
  userById: t.procedure
    .input((val: unknown) => {
      if (typeof val === 'string') return val;
      throw new Error(`Invalid input: ${typeof val}`);
    })
    .query(({ input }) => {
      const user = userList.find((u) => u.id === input);
      return user;
    }),
});
 
export type AppRouter = typeof appRouter;
yoshinoyoshino

mutation procedureを追加する

server.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
 
const t = initTRPC.create();
 
const router = t.router;
const publicProcedure = t.procedure;
 
interface User {
  id: string;
  name: string;
}
 
const userList: User[] = [
  {
    id: '1',
    name: 'KATT',
  },
];
 
const appRouter = router({
  userById: publicProcedure
    .input((val: unknown) => {
      if (typeof val === 'string') return val;
      throw new Error(`Invalid input: ${typeof val}`);
    })
    .query((req) => {
      const input = req.input;
      const user = userList.find((it) => it.id === input);
 
      return user;
    }),
  userCreate: publicProcedure
    .input(z.object({ name: z.string() }))
    .mutation((req) => {
      const id = `${Math.random()}`;
 
      const user: User = {
        id,
        name: req.input.name,
      };
 
      userList.push(user);
 
      return user;
    }),
});
 
export type AppRouter = typeof appRouter;
yoshinoyoshino

tRPCクライアントを設定する

client.ts
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from './server';
 
// Notice the <AppRouter> generic here.
const trpc = createTRPCProxyClient<AppRouter>({
  links: [
    httpBatchLink({
      url: 'http://localhost:3000/trpc',
    }),
  ],
});
yoshinoyoshino

Querying & mutating

これで、trpc オブジェクトの API プロシージャにアクセスできるようになりました。試してみてください。

client.ts
// Inferred types
const user = await trpc.userById.query('1');

const createdUser = await trpc.userCreate.mutate({ name: 'sachinraja' });

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

yoshinoyoshino

@trpc/server

Proceduresについて

インプットバリデーションなし

import { router, publicProcedure } from './trpc';
import { z } from 'zod';
 
const appRouter = router({
  // Create publicProcedure at path 'hello'
  hello: publicProcedure.query(() => {
    return {
      greeting: 'hello world',
    };
  }),
});

インプットバリデーションあり with Zod

Zodでバックエンドが API に適合したリクエストのみを処理することを保証する

import { publicProcedure, router } from './trpc';
import { z } from 'zod';
 
export const appRouter = router({
  hello: publicProcedure
    .input(
      z
        .object({
          text: z.string(),
        })
        .optional(),
    )
    .query(({ input }) => {
      return {
        greeting: `hello ${input?.text ?? 'world'}`,
      };
    }),
});
 
export type AppRouter = typeof appRouter;

publicProcedureとprotectedProcedure

Procedure実行前にユーザーにログインを強いるミドルウェアを使用しているのがprotectedProcedure

複数のインプットパーサー

複数のProcedures

再利用可能なBase Procedures

コンテキスト

コンテキストは、すべてのtRPCプロシージャがアクセスできるデータを保持し、データベース接続や認証情報のようなものを置くのに最適な場所です。コンテキストの設定は、初期化時に型を定義し、リクエストごとに実行時コンテキストを作成する2ステップで行われます。

コンテキストの型を定義する

コンテキストを作成する

イナーコンテキストとアウターコンテキスト

ミドルウェア

t.procedure.use()メソッドで、プロシージャにミドルウェアを追加することができる。

Authorization

実行前にユーザーが「admin」であることを確認します。

Logging

クエリーのタイミングが自動的に記録されます。

Context Swapping

ミドルウェアがコンテキストのプロパティを変更し、プロシージャが新しいコンテキスト値を受け取る

Extending middlewares