BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Presentations Building Typesafe APIs with tRPC & TypeScript

Building Typesafe APIs with tRPC & TypeScript

Bookmarks
50:46

Summary

Brian Douglas discusses how tRPC provides type safety end to end, ensuring the contract for the API boundary can be trusted by default.

Bio

Brian Douglas is the founder and CEO of Open Sauced where he works on increasing the knowledge and insights of open-source communities. In the past he led Developer Advocacy at GitHub by fostering a community of early adopters through content creation showcasing the newest Github features.

About the conference

QCon Plus is a virtual conference for senior software engineers and architects that covers the trends, best practices, and solutions leveraged by the world's most innovative software organizations.

Transcript

The Basketball Analogy

Brian Douglas: I'm going to give a quick little history lesson all the way from 2020. In 2020, there was this documentary called, The Last Dance, and it was based on Michael Jordan. Here pictured are the very first Air Jordan 1's. Actually, these are not the exact copy, because there was way more white than that. What I'm getting at is, I put some shoes there just to represent that documentary, because that's what I could put there. I spent a lot of time watching that documentary. I'm a big basketball fan. I watched Michael Jordan during his last season. I might have been 8 or 9 years old at that point. Going through that I got to relive the experience of his last season. Ironically, it wasn't his last season because he actually retired multiple times after the fact. What I'm getting at is, during that time in the documentary, they were talking about what made Michael Jordan the greatest.

One thing they mentioned was actually, not just the fact that he was the greatest player of all time, but he was in the greatest system of all time too as well. I wanted to explain, this is a basketball court. Object of the game is to get the ball in the opposing team's hoops, like soccer, but with your hands. During this era, it was like the Shut Up and Jam era. The same year when he was drafted, Charles Barkley was drafted, and there was a game called Shut Up and Jam, Sega Genesis. I bring that up, because it was always about getting the ball to the opposing team's hoop. Eventually, they discovered years later, using statistics, that there are certain parts of the court are just really hard to defend. This one part which is on the right side of the foul line, it's called Area 31. The reason for that is because 31% of the shots go in at that point.

I bring this up, because now in basketball, it's very clear that everyone plays place to the best place to get the best shot. There's 5 people on the court, so 1, 2, 3, 4, 5. If you are the number 1, you have an option to pass it to number 2, or if they're double guarded, there's probably someone else wide open. Number 5 is going to be in the triangle as well. Number 4 is going to be in the other triangle. Basically, what I'm getting at is, the best players, they don't have to think about what's happening next, they always know there's someone to the left or to the right. Getting back to Michael Jordan, he was the best player because he trained himself and he practiced enough that he always knew what situation he was in, and had to get the ball either to the hoop or to a teammate on the court. I bring this up as more of an anecdote analogy, because I honestly think that when it comes to DevOps, or engineering, or building APIs, it's all about having the most at-bats. Then when you come up with the issues like solving bugs, it's all about having that familiarity or running plays, or frameworks.

Outline

This talk is going to walk through three different sections. One, we're just going to talk about building modern APIs, really in the context of Node. I don't have context in building APIs in C or C++, or if that's something you do in C++. It's all going to be around in the context of JavaScript, really talking to frontend engineers, but also people who support frontend engineers as well. If you work on the backend, looking for next level stuff to build your APIs with, this is a talk for you. We're going to talk about prototyping quicker with tRPC. tRPC is the framework that makes it a lot easier to approach building strongly typed APIs. Then I'll touch briefly at the end about t3-app. This is a CLI tool to build apps quicker. At the very least, we should have some context of these libraries, these frameworks on how to build APIs.

Background

My name is Brian Douglas. I'm the Michael Jordan of open source. I mention that because I'm actually building a project, open source. The theme is taken from the company that I'm working at, called OpenSauced. We're building a tool to empower the best developers who work in open source. If you feel like you're the greatest, or if you want to find insights on who is the greatest in open source, that's what we're hoping to build. Currently, it's just a platform to index Hacktoberfest, but we're looking to build more stuff in the future. I bring this up really for the sake of, I was exploring tRPC for one of our newer products. It was more like an exploration. I didn't really spend a lot of time with the rest of the team. It's more of like I heard tRPC multiple times in different contexts and conversations, and I wanted to try it out myself. That's what this talk is. It's me evaluating tRPC for my use case, which is building APIs that multiple people can consume.

Last year, I had a conversation with Mike Cavaliere talking about rethinking your stack. Because just like where I'm at right now is every single piece of your stack, there's always that opportunity to introduce something new, or level up another piece. Because sometimes code gets stale, sometimes libraries get undermaintained. That was a conversation I had with Mike on my podcast called Jamstack Radio. Also, my current implementation for the API, api.opensauced.pizza, it's the API that we built a platform on top of, really so that way anybody can build their own tooling on top of OpenSauced. We're actually currently using REST API, and using the OpenAPI spec, Swagger. That's the context that I can give on where I'm coming from, and how I'm approaching evaluating these APIs or these tools.

Building Modern APIs

Let's talk about how people are building modern APIs today. If you're currently working, at least in the JavaScript ecosystem, maybe you're in Rails or Django, you have a couple options. Swagger, GraphQL, are probably the most two popular options. Swagger has been around a bit. It gives you structure around building your REST APIs. It also helps you to maintain and document those APIs as well, by giving you some hooks and automation to create that documentation. GraphQL is a spec. It came out of Facebook. It is just that, it's a spec but a standard. It's actually prescribed ways on how to grab data from the server. It takes a lot of work to set up. Once you're set up, you do get a lot of benefit from having GraphQL. I spend a lot of time using both of these a lot, really a lot of GraphQL.

OpenSauced, the original project that I built years ago as a side project was built on top of the GitHub GraphQL API. It was the reason I learned GraphQL is because of that project, and because the GitHub GraphQL API is so open and ready to be used. I use the analogy of peanut butter and jelly, describing clients and servers. Because those separation of concerns is how I know and how I operate when developing my code bases. I tend to have a backend and a frontend. I tend to have them pretty separate in different folders. I also deploy them separately as well. That really just keeps us nimble to be able to have the backend deploy independent of the frontend. It also enforces being able to have a public API. If we ever want to build tooling, or if we have to worry about security, that's all built in. We take that into consideration upfront.

TypeScript Remote Procedure Call (tRPC)

I do want to jump into tRPC. tRPC is TypeScript Remote Procedure Call. The RPC, remote procedure call, is not something brand new, it's something that's been used multiple times in different languages and frameworks. You might have heard of gRPC. gRPC and tRPC, they share the remote procedure call, but they're slightly different. tRPC is something I've actually only heard of in the last year. Didn't really give it much of a look. I felt like it was a little early.

We actually built our new project, which I showed earlier, just using Swagger and a REST API. The reason for that, it's like tRPC, I didn't really feel like it was ready for me to use. I feel like a lot has changed in the last couple months. tRPC, very similar. You have a structure. You do have a client. You have a server. You have a frontend and backend. What you're doing is you're actually doing remote function call. Instead of having some spec to consume or some resolver to consume your backend into your frontend, you're actually just calling JavaScript functions. What I love about this, is the fact that you can actually have the full-on experience directly in the same project, or in the same repository. Historically, I've always had separate repositories. This has been a paradigm shift back into where I was before, prior to separating my concerns using the Jamstack. I am very much excited about the possibilities for tRPC and where it's taken us.

TypeScript

I want to really just touch on TypeScript. If you're still running JavaScript, or maybe you haven't touched TypeScript, at all, I just want to mention that TypeScript is a superset of JavaScript. There's a compiler involved. What that does, it takes your types and compiles it to something that's safe, which means that if you write unsafe code, it won't deploy. Also, a majority of your linter will probably fail, you get warnings. This gives you a huge heads-up. There's some people in the TypeScript ecosystem that say, you don't actually have to write tests. I actually recommend you still write tests. It does give you a lot of safety, like maybe tests aren't as needed. Still write tests.

What I'm getting at is that, so in TypeScript, you have these interfaces, or you have these objects that you can identify, and you can parse those objects into your functions. That way, every time you parse in an object to the function, it is confirmed that this interface itself is always going to have ID, it's always going to have a first name, last name, or a role. It just knows undefined or unexpected side effects, because now you're confirming this is the exact format that your code should be formatted in. A thing I want to point out is that TypeScript gives JavaScript devs the ability to do bold things. I missed a word there, but what I'm getting at is like, you can now take chances if you know that your code is going to be running, and it's going to work at the time that it gets deployed.

Because if you're in development mode, or if you deploy this, you're going to get a failure. You have a little bit more confidence to try bolder and bigger things. I've only been using TypeScript for the past, really full-time, six months, really off and on the last year. I love it. I love the fact that I know that if I'm going to write some bad code, I'll find out before I get to production. You're always going to have bad code no matter what, but it actually removes some of those undefineds and those common edge cases which you hit whenever you write this normal JavaScript.

The Zod Library

I also want to bring in just a quick context around this library called Zod. Zod is a library to introduce some standard objects or standard types into your code base. That's what the import z, you'll see that in some of the other code that I have. What I'm getting at is that it's inferring.

If you have an object that looks like this, it's going to infer that, so you don't actually have to handwrite this every single time. You can just wrap this on object.z.object around your object itself, and it will just confirm the type for getting it back. There's a lot of type inferences. This is what really gives us the end-to-end type safety, is through Zod as well. TypeScript is not always easy to work with. If you're not using something like Zod, and you're building your own interfaces by hand all the time, or you may be consuming a third-party API where you don't know what to expect, or you're working with the standard JavaScript library that don't have types built in.

Things like local storage is something I've run across. There's a lot of edge cases you get to leverage. My recommendation is use something like Zod so that way you can infer the types, you don't have to fight TypeScript, or fight the compiler the entire time.

What is tRPC?

tRPC allows you to easily build and consume type safe APIs. You don't have to write a schema before starting to write code, or you don't have to create code generators or resolvers. It's all built in by default. tRPC is making it easier for you to write just JavaScript code and infer the types in the TypeScript along with them. Schema-less API development is the bread and butter of tRPC. It's how I wrote JavaScript back in 2014, when I first was writing server-side JavaScript. You just write JavaScript in the backend, write JavaScript in the frontend, and you're good to go.

This is an example of a CRUD request. Basically, this is a post request, adding a mutation under the router. Here I'm wrapping the Zod object, and then shipping that to the frontend. I'll show you how to ship that to the frontend. This is a GIF taken directly from trpc.io, so it's actually an example above the fold. Here they're changing the attribute in the backend. That attribute is getting a validation right away, telling you that the name itself, undefined, it's going to get updated in the frontend.

I'll run that one more time so we can see that because it went faster than I can explain, so, msg, change to name, msg, now input that name. This is my frontend client code, changing that to name, everything compiles. It's good to go. This is a VS Code experience right here. In VS Code, they're going to infer all the types and give you autocompletion as well. The developer experience is amazing.

tRPC vs. REST

Now that I gave you the quick intro into tRPC, the benefit really is that you get a very clean end-to-end experience where you're writing JavaScript for the backend and the server, JavaScript for the frontend, and you're consuming that just as you would consume any other JavaScript library. Everything's all in-house. tRPC vs. REST, this is a question that comes up a lot. Me personally, I know how to build REST APIs. Why wouldn't I just use that until the end? Really, it just comes down to this. REST is the most common. One of the benefits right now is most common, most familiar. It's been around since I think 1995. It came out in 1995. Been around for a while, tried and true. A couple cons is, not a standard, not a style.

A lot of what you get in CRUD actions, it's been spec'd out in REST, a lot of people don't use that. What we end up using is we use a lot of libraries and addons that are needed. Things like Swagger, that enforces a standard and a style. There's also a bunch of other standards and styles that come through that, like JSON being the other one. You hope everyone is using the same thing, for the most part, at least in the JavaScript world I live in. A lot of the folks I work with, we all use the same stuff, so it's helpful. Once you move outside that realm, or outside that world, or into a new company, you're not guaranteed everyone's writing APIs the same way. That means that you have different standards between the companies you work for.

tRPC vs. GraphQL

tRPC vs. GraphQL. I'm not going to break down GraphQL line by line because this is really focused around tRPC. Same deal. GraphQL does give you a standard and a spec out of the gate. Facebook, I think did really good when they offered GraphQL back in 2016, is that they said this is a standard, this is a spec. Everyone built their libraries and their interactions and their frameworks around that standard. The other thing is that it's a strong community. The folks who have chosen to use GraphQL and start building on GraphQL pretty early, including GitHub, they are now in touch with each other and they're building a strong community around GraphQL and GraphQL's tooling. I highly recommend checking out the Guild and what they've been building.

Oracle is another good consumer of GraphQL. Apollo, another great one as well. The cons end up being the syntax. It is a learning curve for understanding the syntax of GraphQL. A lot of it is expectation of you just start typing and autocomplete your way into using the query-based language. That could be detrimental for folks who aren't familiar with GraphQL, or maybe someone's naming other things differently, maybe they structure their data.

GitHub was an early adopter of GraphQL, so everything has nodes inside of edges inside of nodes. It becomes very challenging to know how to get the data you want, at least the first couple times. Eventually, you get it, you get used to it. There's a lot of blog posts and a lot of Stack Overflow questions around the implementation of GitHub's GraphQL API, because it's a learning curve at the end of the day. The other thing, it's all post requests, because GraphQL has one endpoint, and they use parsed in queries into that endpoint.

The challenge is post requests, which means error handling is quite different. If you ever have to handle errors in GraphQL, there's a lot to be desired. There's a lot of tools out there that handle this for you, so you have to include the ecosystem of tooling along for the ride. For the most part, GraphQL is great. It's a great solution. I love it. When it comes to tRPC, tRPC stands above GraphQL quite a bit.

tRPC vs. gRPC

The biggest difference is that gRPC ships a binary. With tRPC, it ships JSON. JSON, for me, it's pretty easy to work with. JSON is pretty familiar for me. I don't know how to manage that binary. Usually, with this binary, you have to use extra tooling as well. There's another learning curve for managing gRPC. For the most part, it's a fast way to consume APIs.

The Benefits of tRPC

tRPC, end-to-end type safety. You saw on the backend, I can write a function. I could match that function and the endpoint and the attributes on the client. Expectation that tight connection between the server and client exists in a way that hasn't existed at least a while for me, when I've been working with REST and GraphQL. Some downsides is, note only, for the most part, you're going to expect to write JavaScript.

There's not really other languages that you're going to be involving. It's TypeScript base as well. It's in the name. It's also very much early adoption. This is part of the reason why I didn't actually end up leveraging this for the thing I just built. Because very early, so most of the stuff you'll have to figure out yourself.

At this point, I'm currently trying to build a project base with users. I don't have the time to basically build tooling on top of the project that I'm building. I'm looking to benefit out of a community ecosystem, which doesn't mean I won't end up using tRPC in the future, it just meant the last project I built, we did not use tRPC for. Also, early adoption. The developers that are working on it currently are mostly React devs. Most examples are React.

There are some Vue examples or some other examples from other frameworks, in like React Native. I mention that because if you're going to get involved in tRPC, understand it's early adopter ecosystem, and expect that you'll have to solve some problems, and be expected to probably write some blog posts, or libraries to help support the ecosystem itself. Not for everybody. Not for the faint of heart. Definitely, if you want to basically make a name for yourself in the API movement, definitely check it out.

tRPC Setup

tRPC, the setup. The difference is that instead of consuming through GraphQL, or through a REST API, you're going to be consuming the tRPC server consuming through function call. Importing the function into the client directly, and you're just clearly calling function calls in the client. Still going to be a server-client setup, the difference is now the setup all lives in the same ecosystem, the same folder structure. There we go. tRPC server, we got the client, that's the biggest difference.

Blog Example

I want to walk through building a blog. I won't build a blog from scratch. I'm just going to walk through some code that exists. This example code came directly from the docs from trpc.io. Definitely, you can get a closer look, check it out. This is the blog example from the docs. I'll show you how this is set up. This is my blog example. We've got the server here, and I'm going to walk through some of this server code. It is quite a bit of code but I'll break this down for us. tRPC comes out of the box. The reason I'm not really showing the setup is most setup actually gets done through generation.

You're not expected to actually go and install all these different pieces like the Prisma client, or Zod, or the router itself. All this stuff will actually be built for you through things like create-t3-app. My recommendation is, use the tooling. Don't try to build this stuff from scratch, unless you're trying to build a workshop or something like that.

Right off the bat, we have Prisma. Prisma itself, it's become the default ORM for JavaScript devs. It's pretty popular. Prisma is going to be your TypeScript ORM. There are other solutions like SQLite, or TypeORM. Prisma is the one that's been chosen by the tRPC community. Here you're going to be able to get end-to-end validation for your types.

Prisma also has type safety built in as well. Prisma has a validator, similar to what Zod was doing for the validation of your types. Prisma is doing this right here with the Prisma.PostSelect right there. Then, this defaultPostSelect, this is what gets reused in multiple places. In your client, you have a defaultPostSelect. What's specifically named in the server, that's what it's going to be called on the frontend. tRPC wraps Prisma for database interactions.

Adding records to the database, removing records, updating records, all that gets wrapped in Prisma. You don't have to hand wire that yourself. You end up passing that a database URL. Here's some basic CRUD operations. Here we've got mutations. We've got the router that we have established on the line one. We have a mutation where we add the flag or the string add. That's just distinguishing that this is what it's going to be named. We're going to add the input itself, which will be wrapping the Zod object. We're heavily inferring the TypeScript type to get safety on the server so that when we call it on the frontend, we'll get safety as well on the frontend.

Here's our resolver here, and this resolver is the interaction for the asynchronous request for creating the post. This is a post operation for creating a record on the blog. The other thing I want to point out is that I just collapsed a mutation to make it fit on the screen. Then now we have our read, fetch all data from there. Because we're using Prisma, Prisma is our ORM for interacting with the database, we're just going to find all. We're going to find all the posts inside the database. I would go through the rest of them. The update is going to look very much like the add. The delete is going to look very much like the add except it's removing the record. It's just another interaction for Prisma.

This is what our frontend looks like. We've moved ourselves from the backend to the server to move to the frontend. The frontend and the client itself, is going to call an add. This is the actual add post function that you would add into your React client. It's pretty straightforward. There's not really much up here, other than the fact that you maybe put this in the onclick handler inside a button, or onsubmit, and that's going to add a post inside the form. This is our read operation. This is a little more showing some of the React code.

Here we have our post query. We're just calling a function directly from the server. We're importing this from the utilities. This utility function is called useQuery. useQuery is going to fetch using the post all function that we've described in our server, it's going to fetch all the data. Then, that data is going to be rendered on the page using React. I want to point out that this is actually just React Query. tRPC is wrapping React Query internally, and that's where the useQueries come from. The majority of the ecosystem is currently mostly React, so that's what's being built under the hood. React Query is not React specific, it just happens to be heavily leveraged in the React community and built for a React app.

If you wanted to use React Query outside of React, I believe it's possible. There's a lot of inferred React stuff in there as well, performing a powerful data synchronization. We can now take our post query in only one line. Then we can render that in a map, so postsQuery.data, all those items will be rendered. Every post will be rendered on the page using React. Pretty straightforward. There's not really much more past this. We will go into more details on how tRPC is built under the hood. That's it. That's how we get our posts added to the page. That's how we get the posts rendered on the page. This is all within the same monorepo between the server and the client.

tRPC Router

Going back to what I was mentioning before, we have the tRPC setup, it's all built around this tRPC router. The router itself is what's doing the work, the glue between the peanut butter and the jelly and making your full stack type safe API in application. This is the setup for setting up the tRPC server and then creating the router itself. It is specifically looking for that context, context, in the context of React apps.

This is our global datastore. That's what's being attached to the router itself. By doing it this way, and by using the tRPC router and the tRPC server and the tRPC client, you get things like caching, JSON distribution, and all that magic between the frontend and the backend to work out of the box. If you haven't got it already, tRPC is not meant to have a lot of boilerplate setup. You're going to get most of your tRPC setup when you start from either an example, or use a generator. For the most part, you should be able to start with tRPC, write your backend code, write your frontend code and then deploy. That's the hope.

This is your router here. This is in our setup for our server. We're creating our server, createRouter. There's some other stuff under the hood that we've just shown with the context being attached to the router, and then adding the router into your server itself, and then adding the mutation as functions, and then also adding the reads and the updates and the deletes. They will all get attached as a function. So far, I had not had to do any promise handling, had not had to do any JSON handling.

The benefit of this is that it just works out of the box. Even though you're creating endpoints, you're just creating like this mutation add. It technically is an endpoint, but it's really just a function. The beauty of this is that you're building an API without actually not even knowing you're building an API. Again, the beauty of this is like, everything's put together in a nice sandwich, which is our full stack application.

Postgres Database?

The one other thing I'd mention is the Postgres database, not only can you get the validation, the types, the safety, the validation from your database all the way to your client, you can also set this up in SQLite, Postgres, just patch in your database URL directly in the schema.prisma, and there you go. You're not hand building any schema for your project, it's all getting generated for you, thanks to Prisma. tRPC works hand in hand with things like Prisma. It also works hand in hand with things like React. All that is being inferred. We're very early stage when it comes to tRPC, so the assumption is that you're going to be doing React applications.

Benefits

Some things that I didn't have time to basically go through are things like batch queries. Imagine you want to fetch ID 1, 2, and 3, you can actually batch those queries, so that way, they're all in one network request. This is actually how you would batch those queries, just by wrapping it into one promise. tRPC knows how to handle that, and it will batch that query in one request. Not only do you get caching for free, you also can batch queries for free as well. In addition to that, I'd mentioned in passing, you're going to have your best experience. tRPC is going to work best with the Next adapter. Next.js is basically a framework to build APIs that connect to your frontend. That's not how they sell it. It's Next level web applications. One of the best features of Next.js is that you can actually build an API pretty quickly in Next.js. I've actually used Next.js.

The original open source API was built on top of Next.js, and it had no frontend. It was actually rendering JSON only. The beauty of that is that because they are a file-based system, but also the resolvers that are built are serverless functions built in Next.js, it made it easy to generate an API pretty quickly. I don't think it's actually the best use case for Next.js, but I love it. tRPC in Next.js, they work very well together. If you want to try tRPC today, probably reach for building a quick prototype in Next.js, and see if you like it. React is going to be a pretty good experience as well.

Outside of that, you're going to probably have to build some tooling or join a community somewhere to figure out some edge cases based on the project you're working with. Just want to be upfront with that. Again, still early adoption. Make sure you know what you're getting into if you're going to be shipping this stuff to production.

t3-app

tRPC recommendations. They recommend out of the box, use an example app for setup. Start with the boilerplate that's already set up, so you don't have to do all the wiring of Prisma and everything else. My recommendation is, use t3-app. t3-app is actually a CLI to generate your tRPC application. It's not tRPC specific, but it's a Next application that adds in some extra features that are not included with Next. It's an addon on top of Next.js. The way to get started, the barrier is very low, npx create-t3-app. It will actually prompt you to make some decisions about your application. It's really focused on simplicity and modular approach to building full stack applications. It includes Next.js, tRPC, Tailwind, TypeScript, Prisma, and nextAuth.

This is what it looks like when you first run create-t3-app. It will ask you what options you want to include. I mostly include tRPC. Actually, I include all these except nextAuth. I end up using other things for authentication. For the most part, out of the box, it's a pretty good experience. Which gets me to the point, this is all you need to know about t3-app, because everything I've shown you before you get out of the box from a t3-app. You don't get the blog post stuff, but you do get all the connections, all the setup, so you don't have to worry about things like the router. It's good to know that the router exists in case you needed to debug or migrate to future versions.

This really loops you back into landing this plane, is that t3-app, it is the framework. It's that system. It's literally what the greatest developers of all time, when you think about building apps over again, are being really good at seeing situations and getting yourself out of bad situations, in the context of bugs and trying to ship features. You want to put yourself in a framework, in a system that no developer has to think too hard about how to connect their backend to their frontend.

No developer should think too hard about end-to-end testing, or about type safety. I honestly think that t3-app is going to be the future of how we build applications. With like Rails and Django giving a lot out of the box, I think that was the way the developer experience should be all the time. I think as we've developed our React ecosystem, the JavaScript ecosystem, TypeScript, we've leveled up to the point where now we can see more of these frameworks get shipped. Whether t3-app is your solution, or something similar, I do recommend starting with a framework, could be able to start net new. Because otherwise, you're going to be building your own framework from scratch. My recommendation is, use t3-app for setup, if you want to mess around tRPC.

Limitations

I do also want to mention some limitations specifically for tRPC. Mostly React, Next.js examples, that's what you're going to find a majority. You're going to find a few Vue, React Native examples using tRPC. These are cutting edge folks who decided they want to use tRPC for whatever situation. This is how ecosystems, how frameworks build on top of each other is that folks, they take a chance, they build something.

If you don't have a solution for tRPC, I highly recommend, go ahead and build it, if you have the bandwidth. At least we didn't have the bandwidth when we built the last app, we might have in the future. I do see this ecosystem building and growing. They do have a Discord. They do have an active community. I do see quite a few blog posts on tRPC coming out as of late, so definitely worth checking out and trying for sure.

The other thing is, I tweeted out in preparation for this talk, just looking for, what was the de facto onboarding experience tutorial for building the tRPC ready app? I actually didn't get a lot of feedback. I didn't get a lot of replies to my tweet. I did find a dev2 post about building a Discord chat application using tRPC. That was worth checking out. Shoubhit Dash, definitely check out their post. I found that pretty enlightening. The other thing, I would also mention a limitation for tRPC, which is no longer a limitation anymore, is that up until a couple months ago, it wasn't really good for use for public APIs.

This is the reason why open source, why I personally decided not to pursue tRPC is because it was not available to build public APIs on. We really wanted to build a public API for people to consume. tRPC OpenAPI, which is also Swagger, was shipped. It's still early adoption. It's something I'll be playing around with in the future and building, at least the future of open source tooling with. Might just be side projects for now. You're going to find a lot of content and context around not building public APIs with tRPC, now you can. The beauty is that it's open source. It could definitely use contribution.

The Best Framework

I feel like I'm in my last dance right now. Just recently started a company running as a founder. I'm actually doing a lot of tooling and coding myself. I'm doing a lot of coding myself, but we're not doing it in production. I'm building a lot of side projects, testing out things like tRPC for fun. I get to see my team grow around me and build up whatever the future of the product that I'm working on.

Similar to all engineers, you go to senior, to principal, to distinguished, or whatever the latter is, and you want to find ways for people to plug into the system. In the event that you go move on to another company, or you go to FAANG or whatever it is, you now have an opportunity for people to slide into the mix, and not have to relearn all the best practices. Because at the end of the day, like though Michael Jordan was the greatest player of all time, the greatest player from the Bulls was Steve Kerr. He played with Jordan during The Last Dance, during his last season on the Bulls.

Steve Kerr has won nine rings. Steve Kerr won nine rings with the Warriors and the Bulls, but he was able to take that system and apply that same framework into the Golden State Warriors, where no longer you're working towards Area 31. Actually, they're shooting three-point shots, and all sort of stuff. What I'm getting at is, it's all about the system. It's about the framework. That's where legends live on.

Why OpenSauced Utilized tRPC over GraphQL

Benjamin Dunphy: You evaluated tRPC for use in your own startup, OpenSauced. You mentioned that you ended up going with GraphQL after evaluating tRPC versus GraphQL. You've since told me that your engineering team convinced you to use tRPC over GraphQL. Can you tell me about why and how they convinced you that tRPC was right for you?

Douglas: My exploration started in June, around tRPC. tRPC just actually released version 10. A lot has happened, since me working on this talk and exploring this for the company project. I just didn't have all the information. I'd say, even there was a question around streaming support. That was more of a conversation earlier this year. That also has launched as well. tRPC is advancing pretty quickly. There's a lot of Band-Aids on the bleeding edge. I think where I've been on the bleeding edge for a lot of stuff, I was just a little more reserved on jumping in production on something that's dependent on a couple thousand users.

GraphQL is something I've been using on open source since 2016 as a side project, now it's a full-time project. It's a little more comfortable, familiar for myself. The summary that I probably should have ended off is, tRPC is really good for small up and coming teams that need to move really quickly. GraphQL is for teams that you're going to have maybe more engineers, and maybe like a separation between the backend and frontend, who owns it. Also, our biggest push was a public API, and tRPC then wasn't really set up yet for a public API usage. If you ever get internal, closed, private API, it's a better use case today. Hundred percent will change in the next six months to a year. For today, if you're shipping something in production, consider GraphQL for a public API. Small, fast, up and coming startups, tRPC.

Will tRPC Supersede GraphQL?

Dunphy: That's a very interesting conclusion, because GraphQL has been trending lately and not in a good way. Many people are seeing tRPC as its just overall successor. Not exactly in the way that you put it, where you said that smaller teams, tRPC, move fast. Larger teams, if you got a dedicated team, integrate GraphQL. Given your extensive experience with GraphQL, what are your thoughts on this? Do you think that GraphQL is dead? Do you think that tRPC will have all of the features required to supersede it?

Douglas: I don't think GraphQL is anywhere close to being dead. When I was doing GraphQL like, even four years ago, everyone was waiting for more tooling, like Apollo has Federation. That used to be schema stitching back when I was trying to stitch different schemas together. The advancement of the tools have now picked up, to now you're seeing more of like a maturity in the GraphQL ecosystem. When you see how quickly tRPC is now, like WebSocket streaming support is only just an idea and an issue, now it's shipped to production pretty quickly. It's just the age-old adage, once you get big enough, it's a lot harder to move a ship to a certain direction. When you have a small team working on something, which is the tRPC team, you can move a bit quicker, especially now there's a lot of attention and excitement in it.

GraphQL for Public APIs vs. tRPC for Private APIs

Dunphy: What about your recommendation for using GraphQL for public APIs, and tRPC for private APIs? Do you see this changing any time soon? Anything on the horizon, on the roadmap of tRPC that would change your mind on this conclusion?

Douglas: I even tried the OpenAPI stuff. It's still pretty early. If I had to use tRPC, I'd probably just pick up REST, because just the combinations is not working with the GraphQL side. This is also just a limitation of me. My prediction is, there will be a public API use case for tRPC in the future, because there's a lot of conversation around that discussion. All the bit of GraphQL is still there.

The learning curve for setting up the server side and creating GraphQL's API, there's a learning curve there as far as tooling, but consuming as a frontend developer or a mobile developer using GraphQL, the learning curve is actually not as steep. There's a lot of context around there. When I was talking about Vue, and how there's opportunity for people to create tRPC in example apps, in different languages and frameworks besides Next.js, that's a really good opportunity.

The magic of tRPC is like, you start with create-t3-app, you have an app, you're ready to go, and you can ship to production on Vercel, or Netlify pretty quickly. That magic, it also abstracts a lot of that setup. If you started from scratch, there's a lot of setup and connecting different tools, which is why that my recommendation and tRPC's recommendation is use the CLI tool instead. It just takes you away from hands in the code of building the library itself. It's a balance. It's a push and pull. Do you want full control on what you're building? Yes, GraphQL gives you that. tRPC will get you to ship pretty quickly and make a lot of decisions for you.

Brownfield Development with tRPC

Dunphy: What are your thoughts on brownfield development with tRPC? Let's assume for this question, the brownfield in question is REST APIs.

Douglas: I wouldn't recommend it. tRPC is probably going to do the best if you start with tRPC. It is the same thing if you were having this conversation back in 2016 when GraphQL was brand new. It's going to be a better experience when you start from scratch, because there's a lot of tooling that doesn't exist for existing applications adopting tRPC. If you're using something like the Jamstack methodology, like you can't start server first, build that separately, and then plug and play. That is a lot of tooling and a lot of education.

It's like a rabbit hole you have to go down. If you have some antipatterns in how you're consuming API code, then you have to also rewrite code. It's going to be a heavy lift. My recommendation is, probably start out with a new, smaller project for your team, maybe like an internal tool. Start with that with tRPC so that way you can learn the pattern, and then having something to have a reference, then you'll be able to adopt that in a brownfield app. Again, you'll probably be writing a lot of blog posts and doing a lot of community interaction in conference talks, adding tRPC to a brownfield application, because you'll be one of the first.

tRPC Support in Microservices

Dunphy: Can you talk about tRPC support in the world of microservices, where the frontend will be using multiple instances of backend services?

Douglas: tRPC provides a strong, tightly coupled situation from your backend and frontend. If you're doing microservices, you have to have a strong connection between the frontend and the backend. That backend having multiple clients, that's something that tRPC is not set up well to do. If you're going to approach tRPC, you have the frontend and the backend, they talk to each other, there's no more talking to another application. Which is why I would reach for GraphQL instead, or just build a generic REST API instead.

 

See more presentations with transcripts

 

Recorded at:

Aug 08, 2023

BT