How, why and how to use tRPC by the creator of it
tRPC - Move Fast and Break Nothing
AI Generated Video Summary
TRPC is a tool that simplifies API development by allowing you to call functions in the backend and have the type data inferred into the frontend without code generation. It provides type safety and auto completion when querying databases using Prisma. TRPC can be used with various frontend frameworks and has features like automatic batching and middlewares. It can be shared between repositories using a monorepo or by publishing the types as an npm package. TRPC is easy to set up compared to gRPC and provides built-in input and output validation.
1. Introduction to TRPC and API Development
So, welcome. I'm really happy to be here to talk about my baby, TRPC. Yeah, my baby is really growing up. Like, right now we have over 24,000 stars on github, closing in on 200,000 weekly npm downloads and there's no signs of slowing down. It's really growing a lot. But I want to ask like, who here have used TRPC? OK. All right. Hopefully a bit more after today.
So, a bit about me. Here is me at my first mob programming session at the age of eight or nine maybe. Around this age as well I started doing websites using Microsoft Frontpage. And then I started with this sort of like lamp stack, PHP and MySQL. And I moved to like Node.js mainly in 2011 or something like that. And PHP for me has always been a bit like a northern star in DX. I really loved the simplicity of just being able to like call a database query next to your HTML and just render it. And did it go blank? Okay, technical issues. Okay, it sleeps if I don't touch it in a while. So I'll speed up.
2. Calling Functions and Querying Databases with TRPC
So in TRPC, you can call a function in your backend and have the type data inferred into your frontend without code generation. You can set up a router, define a procedure, and use type-safe validators like Zod. TRPC allows you to build type-safe APIs easily without schemas or code generation. You can also query a database using Prisma and get auto completion and type safety in your frontend.
So why not just be able to like call a function rather than going through all these steps to have a type type thing to just call and get some data or write some data. So how do you do the same thing in TRPC? The first thing you do is to write a back end function, I'll go a bit more into this, it's a bit small. And then you use that function. So all you need to do is to define a function in your back end, all the type data gets inferred straight into your front end without having any code generation or any extra steps.
Let's look at that a bit more involved example. Here we have a full a full Node.js server using TRPC. In the top we import some dependencies, we set up TRPC, router and an end point or procedure or function. I'll be, I'll be using like the words endpoint procedure and function a bit interchangeably in this talk. And then at the end, we start up HTTP server. What I want us to focus on is this part. This is the thing that changes from like router to router to endpoint to endpoint. So, so what we do here is that we set up a router in TRPC, we defined a procedure and point function called greet. We say that this takes an input argument that is a string using a type, a type safe validator called Zod. And then we say that we, this one, this is a query and we return a greeting with hello user. And then here's the magic of TRPC and TypeScript. We just export our backend as a type. And in the front end, we use that, we use that, let's see, that type to set up a TRPC client and straight away you get auto-completion on all of the API routes that you have. You get type safe, type safe outputs inferred straight from your function backend. Note here that you don't declare any types whatsoever, you just get it straight away. What TRPC does is that it allows you to build type safe APIs easily without any schemas or code generation.
And again, a bit more involved example, in this one, we are actually querying a database. Here we have a post router where you can query posts by ID, you can query a list, you can add a post. So here we have a procedure that takes an input that is an object and an ID. And we say that this is a query, we use Prisma, which is a type safe or M for TypeScript to query our database, get some fields out and return that post. What you get straightaway in the front end then is you get auto completion of all your API routes. You get type safety and input and auto completion on the query. Here I'm using the React library of TRPC. And then you get that type safe result straight from the database. If you change anything here you will have it updated straight away in your front end. To show you some live coding, I recorded a video just before this talk today because live coding on stage is a bit risky.
3. TRPC Starter Application and Type Safety
Here we have a TRPC starter application where you can look at a few posts, add a post, and view the post. The focus is on the page and how it works. In the Next application, the pages folder contains the React component for the post ID. The query for the post by ID provides a type-safe response from the database. Clicking on the React hook takes us directly to the backend function. Editing the database return immediately shows type errors on the frontend.
I'll just show you here, here we have a TRPC starter application where you can look at a few posts, you can add a post and if we submit this we get some validation error from the back end because our text is too short. And then you can go and view that post. So what I want to focus on is this page, how it works. So here we can see, we have a Next application here and in our pages folder we have this post slash ID which corresponds to this React component here. And here we query post by ID and you see that we have type safe response straight away from our database query. And if I go and command click my React hook, I actually get straight into my backend in this function that I'm defining. So there's no extra steps in that. And if we take this side to side and we edit what we're going to return in that database, we will get type errors straight away on our frontend. So without even saving the file, you comment out that, and we get a type error here that we don't return title from our database anymore.
4. Benefits of tRPC for Collaboration and Development
And with tRPC, you can have better collaboration between backend and frontend teams. tRPC is not tied to React or Node.js and has no dependencies. You can use it in demo, Svelte, React Native, and there's no build steps. It provides magical type safety for full stack web and mobile development. It can be used as a backend for frontend frameworks and for service to service communication. tRPC has features like automatic batching, middlewares, and request context.
And with tRPC, I think you can have like a lot better collaboration between your backend and the frontend teams, because all you need to do to like understand what's going on is just like it's just a command click away. There's no complicated architecture on figuring out where stuff is coming from. tRPC is not tied to React or Node.js at all. And there's no dependencies on it. So you can use it in demo. You can use it in Svelte, you can use it in React native, and it works the same. And there's no build steps either. So you don't have to worry about any like webpack magic or whatever. It's just TypeScript. And you can have your backend and your frontend in two independent packages in a monorepo. They don't have to be deployed together, but you can still get this sort of like magical type safety where you can just like dive into your backend from wherever you are. And you can use it for full stack web development, mobile development.
A lot of people use it for like a backend for frontend frameworks. As a backend for frontend framework, if you have a lot of services and you don't want to like stitch together API responses on the frontend, you can do like a slim tRPC layer where you, where you do that sort of like gluing together APIs. And also a node, you can use it for like service to service communication. And obviously this is sort of a surface level talk of tRPC. But we have a lot more features on the client. We have borrowed a lot of concepts from GraphQL. So we have like automatic batching on the backend, we have things that you probably already used to in like express. So we have like middlewares, we have request context. So like when an HTTP request comes in, we create the context objects of that, that you can use for like user contextual user information.
5. TRPC Middleware and Ecosystem
Here we have a middleware in TRPC that is pretty cool. It allows you to create a protective procedure where you can throw an error if there's no user in the context. By using this protective procedure, you get a type-safe middleware that provides a reliable behavior. TRPC has a large ecosystem with tools like create T3 app, TRPC OpenAPI package, and TRPC Chrome. There are many contributors to TRPC, and companies of all sizes are using it. If you're interested in TRPC and TypeScript, please reach out as we're always looking for more people to contribute.
And I'll show you a bit more a bit, I'll show you a middleware in TRPC that is pretty cool. Here we have, before we talked about procedures, all I'd showed you was public procedures. And here we have a public procedure where we use a middleware. And if the context objects will return a session or a user based on the incoming request, and here we're creating a protective procedure where we throw an error if there's no user in the context. And then we call our next function with the user that is now known to be non null because we throw in every other context.
What we then get when we use this protective procedure is this, is this behavior here where when you define a procedure, you'll know that the context, you the context user is set. If we list here, we have this context user that is user or undefined because we're using the public procedure. So just by doing this, you get like type safe, type safe middleware that goes for you end I don't know why it goes into sleep mode. And as it's getting, we're getting a really large ecosystem around here per se as well. Sorry, we're getting a really large ecosystem around here per se. There's obviously create T3 app that is very popular sort of starting point for setting up an application with Next.js, Tailwind, and NextAuth, et cetera. This, for instance, a TRPC OpenAPI package where you can use TRPC as a foundation to make like OpenAPI compliant APIs. And there's like TRPC Chrome for making Chrome plugins. And there's a lot more. You can check it out on the trpc.io slash awesome.
And also I want to take a moment to just like say thanks to these people. If you have a phone, take a photo of the slide and follow these people. They're great. Yeah, there's a lot of contributors to TRPC it's not only me doing this. Like I've been doing this for a bit more than two years now, and I wouldn't be able to be here unless there was like a big open source community. And if you like the looks of TRPC, you like hacking on TypeScript, please chat to me because we're always looking for more people to help out. And so companies using TRPC, you might recognize some of those logos. And it's not only like a toy project for the startups. There's a lot of really serious companies using it as well. And yeah, I hope you will give TRPC a go. And if you have questions, please ask. Awesome, Alex, thanks a lot. It was a very nice talk. We have a lot of questions. Alright, cool.
6. Sharing TS Contract Between Repositories
To share the TS contract between different code repositories, the recommended approach is to use a mono repo like NX, Turbo repo, or Lerna. This allows both the front end and back end to be in the same repository, making it easier to share type information. However, tRPC can also be used with separate repositories by publishing the types of the back end as an npm package. Although this approach may not provide all the conveniences of a monorepo, it is still possible to use tRPC without one.
Yeah. Yeah, let's begin with the first one. By the way, the order is very random to me. How do you go about sharing the TS contract between different code repositories? So the recommended approach with that is that you use like a mono repo, right? So you use NX that are here or like Turbo repo or something like Lerna or something like that to have both the front end and back end in the same in the same repo, because then it's easier to share this sort of type of information. But you can use tRPC with separate repositories as well, but then you need a way to publish the types of your back end as an npm package that you then can import on the front end. And if you do that, you miss some of the niceties where you can, you know, command click into the right thing in a package and just update the back end straight away. But it's definitely possible to use it as well without a monorepo.
7. Exposing App Router and Compatibility
In a monorepo setup, the best practice to expose the app router to the client is by creating separate packages for the back end logic and the client, where only the type is exported. This ensures that the back end application is not accessible in the browser. tRPC can be used with quick dev $server function, allowing the same API to be reused across multiple applications. Support for bling and similar projects is currently being developed. There are adapters available to use Nest.js with tRPC, and there are discussions on GitHub on how to integrate the two.
Yeah, next one is actually related to that. In a monorepo setup, where the back end is not really exporting anything, what's the best practice to expose the app router to the client? So, what you could do is, you can do a package that is your back end logic, as its own package, and then you have applications using both your front end and your back end. And, to make it extra, extra safe, you can do a package for the client, where you only export the type. So, you can be guaranteed that your back end application never ends up in the browser, where someone can just read that source code.
Awesome. Yeah, next one. Maybe you would know how to answer that. How does that compare to quick dev $server function? I mean, quick... I think tRPC works with quick dev $server function. I've seen someone doing a proof of concept with that. With quick, it's very bare bones, right? You basically have a callback function with a handler. And yeah, it's very, very coupled to your application. And in tRPC, you can reuse the same API across multiple applications as well. But you can also use tRPC within the context of quick callbacks functions. But I have not personally used quick very much. But I guess it's similar to what I've seen with bling and things like that.
Yeah, so we're right now working on doing some sort of support for bling and similar projects. Yeah. And how about Nest.js? Is that supported there? I know people have used it. I'm not. I've not used Nest.js much myself. But I know there are some people that have done adapters work to, to use it. So I think if you Google like Nest.js, gRPC, GitHub, there's a bunch of, there's a, I know there's a GitHub discussion where there's a lot of like findings on how to use it with Nest.js.
Why do you think gRPC hasn't gathered much interest? TRPC is easy to set up compared to gRPC, as it doesn't require writing interfaces or schemas. TypeScript syntax is sufficient for contract definition in TRPC. TRPC provides built-in input and output validation, supports various input validators, and allows custom serialization of JSON. TRPC can be used in native mobile applications with React Native or Expo. For clients in other languages, the TRPC Open API package can be used to generate an Open API compliant spec. Tooling for generating TRPC clients in other languages is being developed. The motivation for building TRPC was to scratch the creator's own itch after using GraphQL for several years.
Got it. Do we have inbuilt validation in TRPC? I think you showed that. We have an input and output validation of your procedures, and we support a bunch of different input validators. SOD is the one I always refer to because it's my personal favorite, but it also works with Jup, and you can do your own input and output validators if you like that. I don't know why anyone would do that.
So, how good is your Portuguese? Where do you live in Brazil? I mean, it's pretty embarrassing at this point, like it's four years ago since I spoke it a lot, but it's all right. Yeah, I've got one. How about native mobile applications? I'm not sure which slide is that referencing to. So, native mobile is a fun one, so if you're using React Native or Expo, you can just use our, you can use TRPC the same way. If you want to use TRPC and have clients in other languages that aren't TypeScript, you probably want to use the TRPC Open API package to generate sort of an Open API compliant spec, so you can then generate a type safe client on the front end. We are working on tooling for like static analysis of your TRPC backend in order to generate real like TRPC clients automatically and other languages, but it's a bit, it's not coming in the next few months or very short term. Okay, if you're looking into the generating approach as well. Yeah. Okay. What was your motivation for building TRPC? Scratching my own edge, like I I've used GraphQL for like five, six years.
Motivation for Building TRPC
Ever since GraphQL came out, I've been a big fan. But when building my own company's web application, I didn't need the full flexibility of a GraphQL backend. I was contributing to BlitzJS, which has a similar RPC concept, but I wanted a simpler RPC layer. When I discovered that I could achieve this without code generation, I was thrilled.
Ever since it had come out, it was sort of like a eureka moment for me when it came out. I'm still a big, big, big GraphQL fan. However, I was building my own company at the time a couple of years ago, and I was doing this sort of like full stack web application in react and next.js or whatever. And I wanted a backend for that. And it felt a bit silly to make like a full GraphQL backend for an application that only has one consumer. Like the GraphQL is amazing and very flexible, but like I don't didn't really need that flexibility. And, yeah, I was also contributing to BlitzJS early on that also has a similar RPC idea, but I only really wanted a RPC layer. And once I found out that you can actually do that without any code generation, I just went crazy on like hacking, on trying to make something work. Yeah, and it works.