技術・業務

【Next.js】tRPCを使ってみる

こんにちは。システムデザイン開発の高木です。
tRPCは、T3 Stackの技術の1つに採用されているRPCフレームワークです。
現在のWebアプリケーション開発では、REST APIとGraphQLが広く使用されており、
特にGraphQLの利用率が年々増加しているように感じます。
一方で、tRPCは比較的まだ新しいフレームワークではあるのですが、
Github上のリポジトリスター数は増加しており、コミュニティも活発ではあるため、
これからどんどん伸びていく技術なのではないかなと思っています。
今回はNext.jsのAPI Routes内でtRPCを使用して、基本的な使い方をご紹介していきます。

tRPCとは

TypeScript用の高速で安全なRPCフレームワークです。
tRPCの「t」はTypeScriptを表し、「RPC」はRemote Procedure Callの略称です。
クライアントとサーバー間で通信を行うための仕組みの1つと言えます。
tRPCを使用することで、バックエンドとフロントエンドで型情報を共有できるため、
Type SafeなWebアプリケーションを作成することができます。

プロジェクトの作成

まずはNext.jsプロジェクトを作成します。
今回はNext.js12を使用します。

npx create-next-app@12 trpc_tutorial_app --ts

プロジェクト作成完了後、正常にデフォルトページが表示されるかどうかを確認してみましょう。

npm run dev

バックエンドを実装する

tRPC関連のパッケージをインストールします。

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

server/trpc.ts

import { initTRPC } from '@trpc/server';

const t = initTRPC.create();

export const router = t.router;
export const procedure = t.procedure;

initTRPC.create()を使用してtRPCサーバーのインスタンスを生成しています。
注意点として、このメソッドはアプリケーション内で1度だけ実行するようにしましょう。
公式でもtRPCサーバーのインスタンスは単一であるべきという記述があります。

server/routers/_app.ts

import { z } from 'zod';
import { procedure, router } from '../trpc';

export const appRouter = router({
  hello: procedure
    .input(
      z.object({
        text: z.string().max(10),
      })
    )
    .query((opts) => {
      return {
        greeting: `hello ${opts.input.text}`,
      };
    }),
});

export type AppRouter = typeof appRouter;

router()を使用してエンドポイントを定義しています。
形式としては、keyにエンドポイント名、valueにprocedureを指定する必要があります。

procedure.input()では、リクエストの型定義 + validationを実装しています。
上記コードではzodを使用していますが、yupやSuperstruct等も使用できますし、
サードパーティー製ライブラリを使用せず、自前で実装することも可能です。
レスポンスに対して同様の処理を実装したい場合には、procedure.output()を使用します。

procedure.query()では、処理内容を記述しています。
今回はデータ取得系の処理なので、procedure.query()を使用していますが、
データ更新系の処理を実装する場合には、procedure.mutation()を使用しましょう。
この辺りはGraphQLと考え方が同じなので馴染みやすいかと思います。

pages/api/trpc/[trpc].ts

import * as trpcNext from '@trpc/server/adapters/next';
import { appRouter } from '../../../server/routers/_app';

export default trpcNext.createNextApiHandler({
  router: appRouter,
  createContext: () => ({}),
});

createNextApiHandler()を使用してApiHandlerを作成しています。
ApiHandleを作成することで、appRouterで定義しているエンドポイントを
Next.jsのAPI Routesに統合することができます。

フロントエンドを実装する

utils/trpc.ts

import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import type { AppRouter } from '../server/routers/_app';

function getBaseUrl() {
  if (typeof window !== 'undefined') 
    return '';

  if (process.env.VERCEL_URL) 
    return `https://${process.env.VERCEL_URL}`;

  if (process.env.RENDER_INTERNAL_HOSTNAME)
    return `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`;

  return `http://localhost:${process.env.PORT ?? 3000}`;
}

export const trpc = createTRPCNext<AppRouter>({
  config(opts) {
    return {
      links: [
        httpBatchLink({
          url: `${getBaseUrl()}/api/trpc`,
          async headers() {
            return {};
          },
        }),
      ],
    };
  },

  ssr: false,
});

tRPCのhooksです。
createTRPCNext()を使用してtRPC Clientを作成しています。
linksurlにはNext.jsのAPIエンドポイントを指定しましょう。
ssrをtrueに設定するとSSRが有効になります。

pages/_app.tsx

import type { AppType } from 'next/app';
import { trpc } from '../utils/trpc';

const MyApp: AppType = ({ Component, pageProps }) => {
  return <Component {...pageProps} />;
};

export default trpc.withTRPC(MyApp);

trpc.withTRPC()でルートページをラップします。

pages/index.tsx

import { trpc } from '../utils/trpc';

export default function IndexPage() {
  const hello = trpc.hello.useQuery({ text: 'world' });

  if (!hello.data) {  
    return <div>Loading...</div>;
  }

  return (
    <div>
      <p>{hello.data.greeting}</p>
    </div>
  );
}

useQuery()を使用してhello procedureを実行しています。
fetchAPIやaxiosを使用せず、メソッドを呼び出すような形でエンドポイントにアクセスできます。
また、型情報が共有されていることにより、不要なエラーを未然に防ぐことができます。

keyが異なるobjectをuseQuery()の引数にした場合

型が異なるobjectをuseQuery()の引数にした場合

以下の画面が表示されればtRPCが正常に動作しています。

まとめ

今回はtRPCを使用してみましたが、型情報を共有できることが最大のメリットに感じました。
厳密な型チェックにより、エラーを防ぐことができるのはもちろんですが、
IDEの補完機能もしっかり働くため、開発効率が上がる事を実感しました。

フロントエンドにTypeScriptを使用している方や、T3 Stackを使用してType Safeな
フルスタックアプリケーションを作成したい方は、是非一度使用してみてはいかがでしょうか。

次回は、T3 StackベースでWebアプリケーションを作成する方法についての記事を書こうと
思っています。


システムデザイン開発は、北海道の地で35年以上の歴史があります。企業向けのシステム設計~開発・構築~保守運用までワンストップサービスを提供するシステム開発会社です。豊富な開発実績と高い技術力を強みとして、北海道から全国へ幅広い分野・業種へトータルにサポートいたします。

当社の受託システムでは、お客様企業のコスト削減やシステムの使いやすさの向上はもちろん、お客様の将来まで見据えたシステム提案や開発を行っています。

システムの導入やご検討、お困りごとがありましたら、お気軽にご相談・お問合せください。

SDDの受託システムとは?

お問い合わせはこちら

タイトルとURLをコピーしました