Developing an API with tRPC

Do not miss this exclusive book on Binary Tree Problems. Get it now for free.

In this article, we are going to analyze and create a basic tRPC API with an express server, each file is going to be explained, and after this article you should be able to understand how basic tRPC works.

Introduction

tRPC is a remote procedure call for Typescript and Javascript frameworks, it is a simple way to control the communication between client and server with data and type validation.

We are going to develop a server connected to an API for the communication, this application is going to have 3 files with very distinct purpose so that you can understand how and why you should use each function and method.

Requirement to use the files:

So that this files can be used in the correct way, you should install these packages:

  • @types/express
  • express
  • typescript
  • @trpc/client
  • @trpc/server
  • zod

npm install @types/express express typescript @trpc/client @trpc/server zod

yarn add @types/express express typescript @trpc/client @trpc/server zod

Server

We start by importing the necessary modules, including the createExpressMiddleware method from tRPC, which we will use to create the middleware for our express server.

We create a new express application by calling the express() function. We then configure the application to use JSON as the data format by calling app.use(express.json()).

Next, we define the API endpoint by setting apiEndpoint to '/trpc'. We then use createExpressMiddleware to create the middleware for our express server. We pass the router object, which we will define later, and an empty createContext function as arguments.

Finally, we start the server by calling app.listen(3000) and logging 'Server is running' to the console

import { createExpressMiddleware } from '@trpc/server/adapters/express'
import express from 'express';
import { appRouter } from './router'

const app = express();

app.use(express.json());

const apiEndpoint = "/trpc";

app.use(
    apiEndpoint,
    createExpressMiddleware({
        router: appRouter,
        createContext: () => ({})
    }),
)

app.listen(3000, () =>{
    console.log('Server is running')
})

So that the server can be started, on your package.json scripts add node server.js or just run this on your command line.

Router

Now that we have set up our express server, let's define the router. We will start by initializing tRPC and importing the default router. We will then create and define our procedure used on the router.

We start by importing tRPC and our AppRouter type, which we will define later.

We define publicProcedure as t.procedure and router as t.router, which are both part of tRPC's API for defining and handling procedures.

Next, we define our helloRouter endpoint. It contains a greeting procedure that takes an input parameter in the format of an object with a name property that is optional and nullable. The procedure returns a string that concatenates the input name if it exists, otherwise, using zod the API is defining in what format the API is going to receive the information, and how the API should answer this input.

Then we are exporting our endpoint for the server.ts to use and exporting the type of the router, so the client can use.

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

const t = initTRPC.create();

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

const helloRouter = router({
  greeting: publicProcedure
    .input(z.object({ name: z.string() }).nullish())
    .query(({ input }) => {
      return `Hello ${input?.name ?? 'World'}`
    }),
})

export const appRouter = router({
  hello: helloRouter,
})

export type AppRouter = typeof appRouter;

Client

The final step in our setup is to create a client that can connect to our server and make requests to our API.

First, we will import the createTRPCProxyClient function from the @trpc/client package. We will also import our AppRouter type from our router file. The AppRouter type is used to ensure type-safety when we call our procedures.

Next, we will create a new async function named main
which will create a new client using the createTRPCProxyClient
function. We will use the httpBatchLink
function to create a new link that will connect to our server at http://localhost:3000/trpc.

In this example, we first create a new client object using the createTRPCProxyClient function. We pass in our AppRouter type as a generic parameter to ensure type-safety when we call our procedures.

We then create a new httpBatchLink that points to our server at http://localhost:3000/trpc. This link is used to connect our client to our server.

Finally, we make two requests to our API using the greeting procedure. The first request is made without any input parameters, and the second request is made with the name input parameter set to 'Alex'. We then log the responses to the console.

import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import { AppRouter } from './router';

async function main() {
    const client = createTRPCProxyClient<AppRouter>({
        links: [
            httpBatchLink({
                url: '<http://localhost:3000/trpc>'
            }),
        ],
    })

    const withoutInputQuery = await client.hello.greeting.query();
    console.log(withoutInputQuery)

    const withInputQuery = await client.hello.greeting.query({name: 'Alex'})
    console.log(withInputQuery)
}

void main();

Disclaimer

Using tRPC should be a facilitator for you when creating and API, the usage of zod, different procedures and other utilities of tRPC will be making the process of validation easier for you. So if you are comfortable with tRPC and think that your project may progress faster with it, use it! If not, choose another remote procedure call.

Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.