Chapter 02

tRPC お試し、express + React

knaka Tech-Blog
knaka Tech-Blog
2023.03.04に更新

概要:

tRPC試すメモとなります。 TypeScriptが使えて便利そうでした

※ 情報が少なめで難航しました。。

  • server: express + trpc/server

  • client : React


構成

  • trpc/server : 10.13.2
  • express : 4.18.2
  • React 18.2.0

参考


参考のコード

https://github.com/kuc-arc-f/react_trpc3


Server

  • install
npm install --save express cors @trpc/server zod

npm install --save-dev typescript nodemon ts-node @types/express @types/node @types/cors


  • server/index.ts

  • const appRouter : ルーティング定義ぽいです。export してます

index.ts
import express from 'express';
import { initTRPC } from '@trpc/server';
import * as trpcExpress from '@trpc/server/adapters/express';
import cors from 'cors';
import { z } from 'zod';

const app = express();
const PORT = 3000;
app.use(cors());

const t = initTRPC.create();
const router = t.router;
const publicProcedure = t.procedure;
//type
interface User {
  id: string;
  name: string;
}
const userList: User[] = [
  {
    id: '1',
    name: 'KATT',
  },
];
//
const appRouter = router({
  hello: t.procedure.query(() => {
    return 'Hello World-1234';
  }),
  helloName: t.procedure
    .input(z.object({ name: z.string(), age: z.number() }))
    .query(({ input }) => {
      return {
        name: `Hello World ${input.name}`,
        age: input.age,
      };
  }),  
  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;
  }),    
});

app.get('/', (_req, res) => res.send('hello'));

app.use(
  '/trpc',
  trpcExpress.createExpressMiddleware({
    router: appRouter,
  })
);

app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`));

export type AppRouter = typeof appRouter;


Client

  • install, vite使う例です

  • ⇒React, TypeScript 選びました。

npm create vite@latest client

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

  • tree
$ cd client
$ tree
.
├── index.html
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── assets
│   │   └── react.svg
│   ├── components
│   │   └── Test.tsx
│   ├── index.css
│   ├── main.tsx
│   ├── utils
│   │   └── trpc.ts
│   └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts


  • utils/trpc.ts

  • server側の、AppRouter読みます

import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../../../server';
export const trpc = createTRPCReact<AppRouter>();


  • App.tsx
App.tsx

import { useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import { trpc } from './utils/trpc';
import Test from './components/Test';

function App() {
  const [queryClient] = useState(() => new QueryClient());
  const [trpcClient] = useState(() =>
    trpc.createClient({
      links: [
        httpBatchLink({
          url: 'http://localhost:3000/trpc',
        }),
      ],
    })
  );

  return (
    <trpc.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>
        <Test />
      </QueryClientProvider>
    </trpc.Provider>
  );
}

export default App;


  • components/Test.tsx : 画面
  • useQueryで、取得の処理です
Test.tsx
import { trpc } from '../utils/trpc';

const Test = () => {
  const hello = trpc.hello.useQuery();
console.log(hello.data);
  const test = trpc.helloName.useQuery({ name: 'John', age: 21 });
console.log(test.data);
  const user = trpc.userById.useQuery('1');
console.log(user.data);
  if(test.data === undefined ) { return; }
  if(user.data === undefined ) { return; }
  return (
    <div>
      <h3>react_trpc2/Test.tsx</h3>
      <hr />
      hello : {hello.data}
      <hr />
      test:<br />
      name: {test.data.name} , age: {test.data.age}
      <hr />
      User:<br />
      id: {user.data.id}, name: {user.data.name}
    </div>
  );
};
export default Test;

....