tRPCをDenoで試す

tRPCをDenoで試す

巷で噂になっているtRPCを触ってみたいと思い、せっかくなんでDenoで動かしてみました。

普段はOpenaAPIを使用している。そこからClient,Sever両方にコードを生成し、ClientからはSever側で実行される同じ名前の関数を実行してリクエストを飛ばしているため、RPCっぽい挙動になっている。

※ ClientはReactで動かそうと思いましたが、それはまた次回。

RPCとは

>Remote Procedure Call is a software communication protocol that one program can use to request a service from a program located in another computer on a network without having to understand the network's details. RPC is used to call other processes on the remote systems like a local system. A procedure call is also sometimes known as a function call or a subroutine call.
What Is Remote Procedure Call (RPC)? Definition from SearchAppArchitecture
Read the definition for Remote Procedure Call (RPC), a client-server communication protocol where clients call remote systems as if they were local.

ということでnetworkの詳細を知らなくてもプログラムを呼べるようにできる。

要はnetworkを抽象化し、通常のプログラムの関数呼び出しのように使えるようになる。

tRPC

それのTypeScrpit版がtRPCというわけである。

gRPCでは.protoを定義し、そこからコードを生成するが、tRPCはServer側を書けばそれをそのまま生成無しでClient側でも使えるところがメリットだったりする。

詳細は下記。

tRPC - Move Fast and Break Nothing.End-to-end typesafe APIs made easy. | tRPC
End-to-end typesafe APIs made easy. Automatic typesafety & autocompletion inferred from your API-paths, their input data, & outputs 🧙‍♂️

Denoとは

Deno — A modern runtime for JavaScript and TypeScript
Deno is a simple, modern runtime for JavaScript and TypeScript that uses V8 and is built in Rust.

プログラム

全体のコードはこちら

trpc-deno/server.ts at main · kooooohe/trpc-deno
Contribute to kooooohe/trpc-deno development by creating an account on GitHub.

Server

import * as trpc from "https://esm.sh/@trpc/server@10.9.0";
import { z } from "https://deno.land/x/zod@v3.20.2/mod.ts";

const t = trpc.initTRPC.create();

const router = t.router;
const publicProcedure = t.procedure;

interface Something {
  id: number;
  name: string;
}

const appRouter = router({
  helloWorld: publicProcedure.query((req) => {
    return "Hello World";
  }),
  createSomething: publicProcedure.input(z.object({ name: z.string() }))
    .mutation((req) => {
      const s = Math.random();
      const sm: Something = { id: s, name: req.input.name };
      return sm;
    }),
});

export type AppRouter = typeof appRouter;

import { serve } from "https://deno.land/std@0.140.0/http/server.ts";
import { fetchRequestHandler } from "npm:@trpc/server/adapters/fetch";

function handler(request: any) {
  return fetchRequestHandler({
    endpoint: "/trpc",
    req: request,
    // @ts-ignore
    router: appRouter,
    createContext: () => ({}),
  });
}

serve(handler, { port: 5005 });

Client

import { AppRouter } from "./server.ts";
import {
  createTRPCProxyClient,
  httpLink,
} from "https://esm.sh/@trpc/client@10.9.0";

const client = createTRPCProxyClient<AppRouter>({
  links: [
    httpLink({
      url: "http://localhost:5005/trpc",
    }),
  ],
});

try {
  {
    const query = await client.helloWorld.query();
    console.log(JSON.stringify(query));
  }
  {
    const query = await client.createSomething.mutate({ name: "kohe" });
    console.log(JSON.stringify(query));
  }
} catch (e) {
  console.error(e);
}

serverを起動

実際にClient側から呼んでみる。

それぞれのendpointを定義せずとも通信が関数のみで完結するのは楽で良いなと思いました。パスをどうするかも割と悩んだりするので。

ちゃんとClient側から関数補完が出て素敵ですね。

その他

T3 Stackというものもあり、流行りを感じますね。

GitHub - t3-oss/create-t3-app: The best way to start a full-stack, typesafe Next.js app
The best way to start a full-stack, typesafe Next.js app - GitHub - t3-oss/create-t3-app: The best way to start a full-stack, typesafe Next.js app