tRPC: a smart way to manage APIs
Tags: typescript, api, nextjs, prisma
Published: 2022-09-05 10:08:57

In past, I suffered a lot of the pain of maintaining a schema or some sort of type definition in my typescript applications.
If your frontend and backend are written in typescript and you suffered the same headaches, this article is for you.

---

In some projects with [Swagger](https://swagger.io/) I generated schemas based on .yaml files 😢, but when you create new features is quite common to change API(s) so you end up with a lot of definitions/schemas generations. 😅 
It's the same with [graphql](https://graphql.org/) because you need to learn graphql language to describe your API(s) and then every time you change something you have to generate the updated API definitions/schemas.
Up to here only bad news 😂 but fortunately you can solve all these pains with [tRPC](https://trpc.io/).

## Getting started

The best way to start with tRPC is using the [Next.js example](https://github.com/trpc/examples-next-prisma-starter) from the tRPC GitHub repository because is created and maintained by the core team and follows the best practices.

Here we can test the solution with a fully configured project with: 
- [Next.js](https://nextjs.org/)
- [Prisma](https://www.prisma.io/)
- [Postgresql](https://www.postgresql.org/)

### Requirements

- Node >= 14
- [Docker](https://www.docker.com/) up and running in your local machine (for Postgres)

## Next.js example

With these commands you can start from scratch with the application:

**yarn:**
```bash
yarn create next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter
cd trpc-prisma-starter
yarn
yarn dx
```

**npm:**
```bash
npx create-next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter
cd trpc-prisma-starter
yarn
yarn dx
```

Then after the initial setup at http://localhost:3000 you can add new posts and read the post text with the `View more` link.

![Example screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3k5gnwzaeb8g4xfu41e6.jpg)

So far so good! 💪

## Show me the code

> Soon the tRPC team will release the new version 10 anyways these concepts are always good.

### Server

Let's focus on these files in our server codebase

Here we have a function to create our router and as you can see we are using `trpc.router` straight from `@trpc/server` library.

- _/src/server/createRouter.ts_

![CreateRouter](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9dh0zv5qnu05r70n5gr1.jpg)

Similar to GraphQL, tRPC makes a distinction between query and mutation endpoints. Let's add an `add` mutation:

- _/src/server/routers/post.ts_

![PostRouter](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ejwxh7gcbznstsmclry7.jpg)

mutation needs a few parameters:
- `name`: string: The name of this endpoint
- `input`: Optional. This should be a function that validates/casts the input of this endpoint and either returns a strongly typed value (if valid) or throws an error (if invalid). Alternatively, you can pass a [Zod](https://github.com/colinhacks/zod), [Superstruct](https://github.com/ianstormtaylor/superstruct), or [Yup](https://github.com/jquense/yup) schema.
- `resolve`: It's a function with a single `req` argument, the validated input is passed into `req.input`. This is the actual implementation of the endpoint, you can use prisma or you can write your logic. Here we are using `prisma.post.create` to save our data in the database.

Here we define a router somewhere in our server code base.
`.merge` function (line 7) helps us to split endpoints into multiple files and this is a best practice to obtain a clean code.
Finally, we export the AppRouter type, typescript infers to this type the right shape ready to be used in our client.  

- _/src/server/routers/_app.ts_

![AppRouter](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xpuy039yfumych06d8rl.jpg)

### Client

The Next.js application needs to be configured to work with tRPC. First thing first we need to wrap the whole application with `withTRPC` higher-order component

- _/src/pages/_app.tsx_

![_app.ts](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pmenzihorbdnnmck0h9b.jpg)

Then we need to create a set of strongly-typed hooks using your API's type signature `import type { AppRouter } from '~/server/routers/_app';`

- _src/utils/trpc.ts_

![hooks](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c92xusz1lhuvigswkqtt.jpg)

Then we can make an API request

- _src/pages/index.tsx_

![index page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ayr9870n1vaqyk9zjywx.jpg)

As you can see we are using the hook we created before to call the mutation.

## Aha moment 🤯

Now I simulate a change in my API definition
- changed `add` to `add_new` 
- added `newField` inside input object

![serveApi](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zplu3fwsog1z7jjluooi.jpg)

Wow 🤩 Typescript interface is smart enough to complain immediately about that

![ClientError1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9ciedqjobdreadsf30p.jpg)

![ClientError2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fse8r5wn28lxesykbyxd.jpg)

---

## Final thought

This library is very convenient when: 
- you have private APIs and you don't need documentation to share with the rest of the world
- you have a full stack typescript project

I like it because it's fast and intuitive
I hope this article convinces you to give it a try.

You can [follow me on Twitter](https://twitter.com/giorgio_boa), where I'm posting or retweeting interesting articles.

See ya! 👋

{% embed https://dev.to/gioboa %}