5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

tRPC v10.35 の最小コードを読み解く (2023年7月21日)

Last updated at Posted at 2022-09-21

リポジトリ

masakinihirota/trpc_minimal_code

環境

Windows10
VScode
Next.13 pages router
TypeScript >= 4.7.0

tsconfig.json で "strict": true
※この設定を推奨します。

Next.13 App router は安定版になりましたが、周りはまだ対応中です。
tRPC も例外ではなく、GitHub の Issue を見ると現在対応中のようです。

Next.13 App router でのtRPC対応状況。

feat: Support RSC & App Layouts · Issue #3297 · trpc/trpc

https://github.com/trpc/trpc/issues/3297

Issues · trpc/next-13

https://github.com/trpc/next-13/issues

trpc/next-13: experimental playground for tRPC + next.js 13

https://github.com/trpc/next-13

trpc/examples-next-app-dir

https://github.com/trpc/examples-next-app-dir

2023 年 7 月 21 日

使用想定場所

tRPC を使う場所
DB --(ORM Prisma etc)-- サーバー --(tRPC)-- ローカル

DB サーバー間には ORM Prisma や その他を、
サーバーローカル間に tRPC を使います。

tRPC (GitHub リポジトリ)

trpc/trpc: 🧙‍♀ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.
https://github.com/trpc/trpc

GitHub での Star History

Star History Chart

Star 数

2023/07/21 27.2K
2023/05/27 25.9K
2023/01/25 20.0k
2023/01/23 19.8k
2023/01/15 19.2k
2022/09/22 13.1k

インストール

プロジェクトを作る場所に移動します。

Next.js のインストール

npx create-next-app@latest .

app router は選択しません。
それ以外はすべてデフォルトでインストールします。

tRPC 関連ライブラリのインストール

npm install @trpc/server @trpc/client @trpc/next @trpc/react-query zod @tanstack/react-query

tRPC の最小コード

src\pages_app.tsx

src\pages_app.tsx
import '@/styles/globals.css'
import type { AppProps } from 'next/app'
import { trpc } from "../utils/trpc"

function App({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}

// AppをtRPCでラップしています。
export default trpc.withTRPC(App)

src\pages\index.tsx

pages\index.js
// クライアント側:サイトのトップページ
import { trpc } from "../utils/trpc"

export default function Home() {
  // CTRL+Click で `greeting` をクリックすると関数の定義へ飛びます。
  const result = trpc.greeting.useQuery({ name: "VNS.BLUE" })

  // データが取得できない場合は Loadingが表示されます。
  if (!result.data) return <h1>Loading...</h1>

  return (
    <div>
      {/* 型が定義されており、オートコンプリートも可能です。
      下記のdataにマウスカーソルを乗せるとresultの型が見えます。
      下記のtextをマウスでCTRL+Clickすると、定義されている場所へ飛びます
      下記のtextをマウス右クリックしてメニューのシンボルの名前変更(F2キー)で、
      クラアントとサーバーの両方の名前を同時に変更します。
      (要:VScode) */}
      <h1>{result.data.text}</h1>
    </div>
  )
}

src\utils\trpc.ts

src\utils\trpc.ts
// クライアント側
// Next.jsのAPI エンドポイントに接続を通すための設定です
import { httpBatchLink } from "@trpc/client"
import { createTRPCNext } from "@trpc/next"
import type { AppRouter } from "../pages/api/trpc/[trpc]"

function getBaseUrl() {
  // ブラウザか、そうでないかの判断します。
  if (typeof window !== "undefined") {
    //  ブラウザでは、相対URLを返します。
    return ""
  }
  // サーバーでレンダリングする場合は、絶対的なURLを返します。
  // 例:vercelを利用している場合
  if (process.env.VERCEL_URL) {
    return `https://${process.env.VERCEL_URL}`
  }

  // 上記のURLが設定されていない場合localhostを返します。
  return `http://localhost:${process.env.PORT ?? 3000}`
}

// Next.jsで使うtRPCクライアントを作ります
export const trpc = createTRPCNext<AppRouter>({
  config() {
    return {
      links: [
        // バッチリンクは処理をまとめてしまうことです。
        // データローダパターンの一種で、
        // 通信負荷を軽くします。
        httpBatchLink({
          // [tRPC].tsの設定場所
          url: getBaseUrl() + "/api/trpc",
        }),
      ],
    }
  },
})


src\pages\api\trpc[trpc].ts

src\pages\api\trpc[trpc].ts
// サーバー側
// これはアプリのAPIハンドラですべてのAPIルートを含んでいます。
// 大きなアプリではこのファイルを複数のファイルに分割するとよいでしょう。
// initTRPC関数は、TRPCサーバーのインスタンスを作成するために使用されます。
import { initTRPC } from "@trpc/server"
// trpcNextはNext.jsアプリケーションでTRPCサーバーを使用するためのアダプターです。
import * as trpcNext from "@trpc/server/adapters/next"
// 入力スキーマを定義するために使用されるスキーマ定義言語
import { z } from "zod"

const t = initTRPC.create()

// TRPCルーターを作成します。
// appRouterは、t.router()関数を使用して作成されます。
const appRouter = t.router({
  // greetingという名前のプロシージャが定義されます。
  greeting: t.procedure
    // これはプロシージャの入力スキーマです。
    // これを変更すると、クライアントの型エラーがすぐに表示されます。
    // inputオブジェクトを受け取り、textプロパティを持つオブジェクトを返します。
    // inputオブジェクトは、zodを使用して定義された入力スキーマに従います。
    .input(
      z
        .object({
          // nameプロパティは、文字列またはnullを持つことができます。
          // nullish()メソッドは、nullまたはundefinedを許容します。
          name: z.string().nullish(),
        })
        .nullish(),
    )
    .query(({ input }) => {
      // これをクライアントに返します
      return {
        text: `hello ${input?.name ?? "world"}`,
      }
    }),
})

// エクスポートAPIハンドラ
// API の型定義のみエクスポートします。
// 実際の実装は一切クライアントに公開されません。
export type AppRouter = typeof appRouter

// エクスポートAPIハンドラ
export default trpcNext.createNextApiHandler({
  // routerオプションには、appRouterオブジェクトが渡されます。
  router: appRouter,
  // createContextオプションは、APIリクエストごとに呼び出される関数を指定します。
  // この例では、空のオブジェクトを返します。
  createContext: () => ({}),
})

ローカルサーバーで動作確認。

ソースコードを写し終えたら、
ローカルサーバーを実行します。

npm run dev

http://localhost:3000/

hello VNS.BLUE と表示されれば成功です。

雑学

メモ程度なので特に読む必要はありません。
RPC (Remote Procedure Call) の歴史など。

雑学 RPC関連

REST、RPC の歴史

要はサーバークライアント間によるネットワーク越しの通信手段の歴史
それを API と呼んだ、API を実現するための技術が下記の 5 個(もっと沢山あるけど省略)

これらはこんな順番で誕生してきました。
RPC (1970 年代~)

REST (2000 年代~)

GraphQL (2015 年) 、gRPC (2016 年)

tRPC (2020 年)

アーキテクチャ

RPC 方式と REST 方式はアーキテクチャが違うだけでそれぞれ一長一短があります。

超シンプルに説明すると。

RPC

独立した PC 間でネットワーク越しに関数を実行することでデータを操作できます。

REST

HTTP という通信手段(プロトコル)を決め、HTTP メソッド(GET、PUT、POST、DELETE)でアクセスすることでデータを操作できます。

GraphQL

複雑なデータでも 1 回の要求で必要なデータを取得できます。

gRPC

バイナリ形式を採用しており、テキストベースの JSON 形式よりも数倍早くデータを取得できます。

tRPC

TypeScript を使うことで型安全に関数を実行してデータを取得できます。

※ Web サイトで DB を使う場合は Prisma(ORM)等をサーバーと DB の間に置いたりするが、
Web サイトから DB を直接操作出来る Supabase とかがあったりする。

tRPC と GraphQL はどちらを選んだほうがいいか?

(もちろん REST 等 他のを選ぶ場合もあります・・・)

tRPC は・・・

サーバークライアント間越しで関数を実行できるようにしたことです。
これまでは
REST や GraphQL がこの役割を担ってきましたが
直接 関数が実行できるならこれらはいらないよねってなりました。
(関数は実行すると返り値が返ってくるのです。)

tRPC を選ぶべき場面

tRPC の恩恵を受けるのには少し制限があります。
その条件をクリアしたプロジェクトの場合は tRPC を選ぶべきです。

サーバー側、クライアント側が両方とも TypeScript を使えること。
そして、両方とも中身が見えること、自分でソースコードをイジれる事。
小さなプロジェクトの場合に tRPC は適しています。

例えば、
個人開発で手軽に実装したい時、
社内ツール用

GraphQL を選ぶべき場面

片側の中身がわからないなら GraphQL の方が適しています。
データがものすごい複雑なら GraphQL の方が適しています。
また、片側が TypeScript を使えないとなると、動かせなくはありませんが自分でクライアントを書く必要があります。
セキュリティを意識するプロジェクトなら GraphQL の方が適しています。
大規模なプロジェクトの場合 GraphQL の方が適しています。

例えば、
公開 API 等を利用して開発する時。
誰か他人に使ってもらうプロジェクトの時。
大型プロジェクトを開発する時。

その他

tRPC は React に依存していません。

GraphQL は実装が複雑で、スキーマ定義をしたり、リゾルバーでデータを操作したりと
学習することが多い。

tRPC のことを習う前に、
RPC とはどういうものか勉強すると良いと思います。
RPC とはクライアントからサーバーの関数を呼び出す方法です。

基本的には、バックエンドとフロントエンドを
より統合された方法で書くことができます。
基本的に、バックエンドに関数を記述し、
フロントエンドで tRPC の関数すべての型を推論できます。

後で tRPC から GraphQL に移行するのは簡単です。
リゾルバーをコピーするだけの場合。スタック内の他のものにロックインはありません。
GraphQL と tRPC の主な違いは、実際には重要なことではありませんが、GraphQL API を使用するのはより多くの作業が必要なことの 1 つです。
ただし、GraphQL API を取得したら、それを公開することもできます。

API のバージョン管理という概念はありません。
これは、関数が返すデータのようなものだからです。

リクエストのバッチ処理
これは一定時間内の複数の同じ処理を一つにすることで負荷が減る
クライアントがネットワーク経由で行う作業が少なくなります。
また、サーバーは HTTP 要求を 1 つしか取得せず、
要求ごとに 1 回、その要求に関する
コンテキストオブジェクトのように作成できるため、
サーバーが行う作業も少なくて済みます。
そして、それらすべてを 1 つの大きな JSON 本体として送り返します。

jsDoc をつかえばそれはクライアント側でも推論に使われます。

用語

zod

TypeScript First なバリデーションライブラリ

バリデーションライブラリは多数あれど、なぜ zod が選ばれたのか?
Zod は TypeScript の型を自動的に作成できて、その型推論は高性能です。
yup(という人気バリデーションライブラリ)より後発ですが TypeScript の型推論に優れていると言われています。
tRPC で使用する場合にはピッタリのバリデーションライブラリということで選ばれたのだと思います。

API (Application Programming Interface)

アプリケーションから別のアプリケーションと通信する規約です、
なので異なるプログラム言語間でも通信が可能です。

RPC (Remote Procedure Call)

REST の前身
別のサーバーから直接関数を呼び出して操作が可能です。
つまり、PC 間の境界を意識する必要はありません。

デメリットとして、
クライアント側でもサーバー側のメソッド名を把握する必要があります。
エンドポイントとしてメソッド名を直接晒すことになるのでセキュリティリスクがあります。
単一メソッドからのコールバックが基本のため、
条件に合わない要求だと適切な処理が難しい
なので、サーバーとクライアント両方とも中身をしっかり把握しておく必要がある
REST のようにパスによる親子構造では無いので整理しづらい。

REST API

(Representational State Transfer)
フロントエンドの技術で JSON ベース、HTTP1.1 を使用しています。
エンドポイントを複数作ってそこメソッド単位で通信をします。
REST の注意点は、標準化されていないことです。
簡単に言えば、REST は URL パスを
エンティティにマッピングし、
さまざまな HTTP メソッドを公開してエンティティを
取得または変更するアーキテクチャスタイルです。

デメリットとして、
状態を保持しないので認証系では RESTful の例外となるリクエストが生じる。
RPC のように直接メソッドを呼び出すのではなく、
HTTP 通信のヘッダーに付与する、テキストベースのため
送信するデータ量が増えパフォーマンスが落ちる
リクエストが増えるほどそれに比例して HTTP メソッドの量も増え複雑化していく。

REST と RPC は、
API アーキテクチャの違いなのでそれぞれ長所短所があります。

GraphQL API

エンドポイント 1 つだけつくり、REST のメソッド (POST、GET、DELETE 等)の代わりに
Query、Mutation 等を使って通信をします。
必要なデータを取得するのに 1 回のリクエストで済ませることが出来る。
データが複雑に関連しあうなら GraphQL が適している。

GraphQL は標準化されており、
REST の問題を正確に解決するために作成されました。
サーバーとクライアントの間の境界線を取り除き、
サーバーが応答しようとしているものの正確なスキーマを提供します。
ただし、注意点として、GraphQL 構文を習得する必要があります。

g RPC API

JSON では十分な速度と軽さを得られないので、かわりに
Protobuf(プロトコルバッファー)というメッセージングフォーマットと HTTP2 を用いることで、
バイナリで効率よく通信をします。

tRPC

サーバーとクライアントの双方が Typescript を使用して通信をします。
型安全です。

フロントエンドとバックエンド両方の中身がはっきりしているなら
tRPC
どちらか片方の中身がわからないなら
GraphQL

Next.js

React 用のフレームワーク
API を使用することが可能
React は画面描写に特化したライブラリだが
Next.js はサーバー側に API 通信をも可能にした React 用のフレームワーク。
Web アプリを作るのに React だけでは足りない部分を補ってくれる。

tRPC 公式

tRPC - Move Fast and Break Nothing. End-to-end typesafe APIs made easy. | tRPC
https://trpc.io/

Example

公式 Example Apps | tRPC
https://trpc.io/docs/v10/example-apps

次の記事

参考

Let's Learn tRPC!
https://www.learnwithjason.dev/let-s-learn-trpc

Episode #21: Alex Johansson - tRPC, Zart
https://devtools.fm/episode/21?view=TRANSCRIPT

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?