Open-Source Internship opportunity by OpenGenus for programmers. Apply now.
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.