🐅

tRPCの便利だった機能

2022/08/20に公開約3,400字

nextと組み合わせるのをやりながら見つけた便利だった部分についての列挙

マージ機能

Routerの部分が肥大化してしまいそうだなというのが結構懸念だったが、mergeする機能があるのでこれは便利そうだった。

const appRouter = createRouter()
  .merge('user.', users) 
  .merge('post.', posts)

Infering Types

Routerなどの各種定義からTypeScriptの型を取り出す機能。
ちょっと記述が面倒なところはあるが、AppRouterの返り値や入力値が抽出できるので、一通り覚えておくと便利。
(とはいえinputについてはzodのinferを利用する方がスッキリするような気もする)

Context

それぞれのrouterの処理の事前にContextを取得できるのが便利。

例えばCookieからAuthしたりなどができる。

inferについても、inferAsyncReturnTypeなどを利用すれば良いようだた。
ざっくり組み合わせると下記のような具合に共通化できてよかった

const createUserAppContext = (opts?: trpcNext.CreateNextContextOptions) => {
  // 本来は認証など色々共通処理を行う
  return {
    foo: "baz"
  }
}

type UserAppContext = trpc.inferAsyncReturnType<typeof createUserAppContext>

const createUserAppRouter = () => {
  return trpc.router<UserAppContext>()
}

export const userRouter = createUserAppRouter()
  .query( .... )

// export type definition of API
export type UserAppRouter = typeof userRouter

// export API handler
export default trpcNext.createNextApiHandler({
  router: userRouter,
  createContext: createUserAppContext
})

transform機能

Prismaとnext.jsを組み合わせる際、Date型が引き継げなくてsuperjsonを挟む必要が出てきたりするのだが、これもtransformというオプションを使えばバッチリできた。

クライアント側、サーバー側どちらも設定しないと片方がズレて適切に取り出せないので注意

// サーバー側
export const appRouter = trpc.router()
  .transformer(superjson)
// client側
const useUserTrpc = () => {
  const client = useMemo(() => {
    return createTRPCClient<UserAppRouter>({
      url: '/api/trpc',
      transformer: superjson,
    })
  }, [])
  return client
}

サーバー側のエラーハンドリング

クライアントにはエラーが返ってくるが、サーバー側でもエラーをログに出すなどしたい場合は、明示的にonErrorを指定しておく必要があるらしいようだった

export default trpcNext.createNextApiHandler({
  router: userRouter,
  createContext: createUserAppContext
  onError({ error, type, path, input, ctx, req }) {
    console.error(error);
  }
})

もしくは`middlewareも用意されているので、こちらでエラーを出力するのでも良い可能性がある

ヘッダ送信

Clinet側からHeaderを送るにはheaders()という関数で設定可能。
上記ドキュメントではwithTRPCで記述されているが、vanillaなcreateTRPCClientからでも可能

const useAuthorizedTrpc = (authToken: string) => {
  return createTRPCClient<AppRouter>({
    url: '/api/trpc',
    headers() {
      return {
        Authorization: authToken
      }
    }
  })
}

CookieやCORSなどfetch周りをいじる必要があるならfetch関数自体を変更することで対処できる模様

その他

まだあまりちゃんと触れてないがそれ以外の便利そうな部分

  • middleware
  • メタデータ機能
  • Output Validation
  • Request Batch
    • https://trpc.io/docs/links
    • tRPCの通信はバッチ化される。都合が悪い場合は設定でdisableにもできる
    • linksの設定などを使うと更に通信周りをカスタマイズできる模様
  • Subscription

まだ未解決な事

  • クライアントサイドのコードからのコードジャンプがちょっとめんどい
  • Routerの型だけ抽出できたらプロジェクト間で共有するとかできて嬉しいなと思ったが、そういうのはなさそう
  • inferは充実しているのだが、router側のハンドラー定義を細かく切り出そうとするとちょっと面倒
    • CreateProcedureOptionsなどProcedure系が定義はされているが、外部で利用できるような感じにはできてなさそう
GitHubで編集を提案

Discussion

ログインするとコメントできます