There are amazing tools out there providing you with excellent type safety. But when you get to the client-side fetching, things go wild. Even if you have perfectly typed backed, you lose the type information during the client-side communication. Yes, you can use GraphQL or protobuf and generate types, but... what if I told you there's an easier way? A way that lets you develop your apps smoother than with REST or GraphQL? How? RPC! Say hi to maximum productivity with fantastic developer experience.
Take a Rest From REST (And GraphQL)
Transcription
Hello everyone, welcome back here in Berlin and welcome back on the live stream if you're watching from home. We are about to introduce our next speaker, Alexander, in just a second, but before that let's just say a couple of words about our wonderful sponsors. Thank you Callstack for sponsoring. Callstack is a team of react and react Native consultants and developers with core contributors on board. I have personally worked with them and they are wonderful people. Thank you also to Brintem who helped the world stay on schedule with its javascript widget library. The UI toolkit contains scheduling, Gantt chart and data components. Visit Brintem in their booth over here for a personal demo or if you're at home go to brintem.com. Now, without further ado, we're going to invite Alexander on stage. Do remember to ask questions on Slido, so if you go to slido.do and you use the code 0212, you'll be able to ask questions that we'll then ask Alexander here on stage live. Alexander is a software engineer and she's based in Wroclaw in Poland. She's done a lot of things. She's a full stack developer, she's worked on Elixir, Golang, Python and typescript. She was previously tech lead for the Hasura console and very recently been working being the lead maintainer of Blitz. Thank you very much. Let's give a big applause to Alexander Steklora. Welcome everyone. Thank you for the introduction. My talk today is Take a Rest from Rest. You probably wonder what happened here. What's this talk about? I'm going to focus on building full stack react applications, but specifically I want to talk about the api layer because for me it's super exciting what's going on currently in the full stack web development like we have new next.js with react server side components. We have TRPC becoming super popular. We have remix that is growing and it's been recently acquired by shopify. So there is a lot going on around the api layer, around building full stack applications. So before I start, I want to explain, yeah, there's a meme. I want to explain what an api is. So in a distributed programming world, when we have computers that have to communicate with each other, for example, we have client application, server application or different servers that have to talk to each other, we need a bridge that allows that communication and an api is that bridge. So why are we talking about this? Because a common scenario for a full stack application is that you have a database and the client and the client has to access this database. But even if your database was publicly accessible, you can't call it from the client because that would expose all your database credentials. So usually we have servers that are responsible for communicating with the database. But whenever we have to make two separate entities talk to each other and understand each other, in this case, client and server, we face a lot of problems. And some of them are tons of boilerplate, losing type safety, repetitive error handling. So can we do something about it? Luckily for us, yes, we can. We now see new tools, new projects that are ready to solve this problem. We have tRPC procedures. We have BlitzRPC with query and mutation resolvers. We have remix data Loader, react server side components, and probably many more that I didn't put on this slide. But with my talk today, I'm going to focus on the approach taken by the first two, by tRPC and BlitzRPC. So that means that we are going to talk about RPC. But in order to understand how we got here, how we got to have tools like tRPC and BlitzRPC, I'm going to take you back to the 80s to see a little bit of history. Why we are starting here? We are starting here because RPC is the beginning of web services. And 1981 was the year when Bruce J. Nelson wrote a thesis and coined this term. So let's start by explaining what is RPC. It stands for Remote Procedure Call. So imagine you have a local procedure, welcome. And when you have it on the same computer, you just call it like that, like on the slide. But now imagine that welcome is on a different processor, on a different server, and you have to use a network to call it. So that's when RPC comes into play. And RPC is the practice of remotely calling functions. And an RPC api is built with functions, and the functions are called with arguments. Of course, that's not how RPC looked like 40 years ago, because there was no HTTP or javascript yet, but that was the idea. So now what were the problems? One of the major problems was that the procedure convention made client and server tightly coupled, meaning it was almost impossible for the server to be consumed by many clients. Another problem was that it was tied to a specific language. So other problems were like no request cancellation, parameters marshalling, exceptions handling, and forcing developers to use multi-threaded servers. Some of those problems are not even relevant anymore. But those 40, or almost 40 years ago, it made developers to look for something better, something more suited for the distributed programming world. And this new thing was CORBA. So what was cool in the 90s, apart from boy bands, Nintendo, and rollerblades? Object-oriented programming. And that's what CORBA have we relied on. So in CORBA, we have a server process which contains objects, and we have a client process that makes requests to get those objects. CORBA, in contrast to RPC, its goal was to be platform and language agnostic. And in order to achieve that, it introduced interface definition language. So that was a huge improvement. And one of the most popular IDLs that you might be familiar with is currently Protobuf. But introducing IDLs, it solved a bunch of problems, but it wasn't also that easy. There were a bunch of mapping problems. Because can we map everything, like types, exceptions, exceptions handling? The answer ended up being no. And apart from that, this goal of being agnostic added a lot of complexity and caused CORBA to have a steep learning curve. So there was, again, need for something better, something new. And since CORBA was complex and heavy, there was a need for something simple. So here we go. Simple object access protocol in late 90s. So in SOAP, we have this envelope. The requests are by default HTTP POST. However, SOAP is not tied to any transport protocol, but by default, it uses HTTP. So it's XML. We have an envelope which identifies the requested api. And then we have a SOAP body with the parameters that are passed to the server. Here's an example of a SOAP request and a response. So that was better. That was supposed to be easier than CORBA. And in fact, SOAP is still used to this day. But because it was XML only, it was heavy and required more bandwidth. And since it was using POST by default, there was no caching on HTTP layer. And another problem, it was tightly coupled with server. So we are back with the same problem as with RPC. So again, what's next? REST. It's not a protocol like SOAP. It's a set of architectural constraints. And in REST, the server exposes resources and the client requests the resources or updates them. So since SOAP had this envelope concept and it was heavy, you can often find comparisons that SOAP was like an envelope and REST was like a postcard, so lightweight. So how it compares to RPC? RPC was about procedures and REST is about resources. So here's the comparison of how a similar api would look like in both RPC and REST. So in REST, you ask the server, can you give me the state of this resource? Or here's the state of the resource. Can you please store it? And with RPC, you say to the server, can you call this procedure for me? So that's cool. But what's wrong with REST? Well, firstly, it's very difficult. It's difficult to follow the constraints. They are awesome. They make you gain a bunch of guarantees about your api. But they are also quite limiting and difficult to follow. In fact, most of the REST APIs that call themselves REST are not really REST. They are RPC over HTTP, which uses JSON. But it doesn't matter whether your REST api is fully RESTful or somewhat REST. There are common problems to deal with. Some of them are, for example, overfetching, so getting unneeded data, and plus one problem, so having to do multiple round trips to the server to fetch data for one view, limiting constraints that I mentioned before, and what's also important, no end-to-end type safety. So then came graphql, aiming to solve those problems. And with graphql, it was very easy to solve the n plus one problem, because in order to fetch data for a view, we can send just one query. This n plus one problem is not really solved, because we just push it to the server. But from the user perspective, someone who's using the graphql api, we don't have to do those multiple calls like with REST. It's also easy to eliminate the overfetching problem, because it's the client who controls the data it gets. But graphql shares some similar ideas as SOAP, which means it also shares its problems. So one of them is that it's also by default HTTP POST, so there's no caching on HTTP layer unless you use something like graphql CDN created by Max Teuber. Then if you want to have end-to-end type safety, you have to generate types, which isn't a big problem, like it works very well. There's an additional step that you always have to consider. Another thing, another problem is that writing graphql servers is quite a difficult task, and there's a lot of boilerplate you have to do. On the other hand, you can use tools like Hasura or Postgre file, but those tools map your database to a graphql api, which can mean that you push a lot of domain logic to the front end. So what's next? As we recently learned that graphql is just a bunch of poorly batched RPC calls, we're going back to talking about RPC. And we're going to revisit the original promise of RPC, which was enabling you to treat remote calls as if they were local. So why is that cool? And why is that important right now? It's because typescript became de facto, and typescript and static typing became de facto a standard. And having end-to-end type safety has never been more important. So this problem of end-to-end type safety regarding the api layer is a crucial thing to solve. So we saw this welcome example before. Right now I just put additional database call. So if your backend is perfectly type safe, then you make this fetch call on the front end and you're losing this type safety. I mean, you could generate types, that's one option, or you can type it again on the client. But you know, that's additional work. It would be cool if we could get rid of this fetch stuff. If we could, for example, create a client on the client side that knows everything about the api, knows what are the procedures available on the server, what are the argument types, and so on, something like this. So now I have a small demo application. So this is something I've built recently because it's Christmas soon, and I really don't like getting too many presents because that's a lot of waste. So I built this app that I'm using with my family and my friends, which lets you draw a name so it can only buy a present for one person. And we're going to see how this draw a name looks like in both tRPC and BlitzRPC. So I'm going to take you to VS Code. So here's a draw name mutation with BlitzRPC. So what's going on here is I'm using resolver pipe from Blitz. It's not even necessary. I won't go into the very details of that. But what's important here is that this mutation is an async javascript function. I have all the logic inside. I have calls to the database, logic figuring out what names are available, and so on. And then what happens is I'm using this function by directly importing it from the server, from the mutations folder in my react component. There is this useMutation hook, which is a Blitz wrapper on react query. It has the same properties. So what happens here is that Blitz is kind of magical. This import is being swapped with an HTTP call at build time so that there's no database data leaking to the front end. But we have all the type definitions we know with draw name mutation returns. We know we have access to all the react query properties. We know what arguments does this function accept. So I have a usage here. It accepts a group name and a member name. So if I remove this, I will get an error. typescript will tell me that I'm missing an argument. So that was Blitz RPC. Now let's see tRPC. So with tRPC, I'm defining the procedures in a... This is a next.js project, so I'm defining it in as a next.js api route. This is... I create a tRPC router. That's unexpected. That's very slow. Okay. Anyway, I'm writing all my procedures, both queries and mutations in one file. And then I'm not sure why it doesn't scroll. But then if I go to the same file when I was calling the draw name mutation, if it lets me... It doesn't. Oh, yeah. It works. Now it's working. Oh, yeah, it works. Now I would have to scroll. Okay. I don't have time to wait for VS Code to catch up. So that was like an overview of how it works in Blitz RPC and tRPC. My goal wasn't to go into very details. My goal was to give you a sneak peek that those things, like having full stack type safety and 10 type safety are now possible. I think my laptop froze. So let's wrap it up. Yeah, I will give you my finger. No. Yeah, I think my laptop is restarting. So there are only two slides left. So I think I can improvise. So the takeaway from my talk is that sometimes we don't need a brand new thing. We can revisit things from the past and iterate on them. We can improve them. Because the thing is that RPC had a bunch of problems a while ago. But now we have frameworks like next.js, like remix. So we can revisit ideas like RPC. And now they are a perfect fit for the javascript ecosystem of things like next.js. So I have this quote, which is, in my opinion, a good summary of my talk. So RPC is a natural fit for the increasingly ubiquitous practice of full stack development with frameworks such as next.js. And by the way, Telefunk is another thing similar to tRPC and BlitzRPC. So we can see that we have a bunch of them. We have a bunch of tools that are now relying on RPC APIs. Because what was once a disadvantage, like that RPC is tightly coupled with the server and the client, can now be used to our advantage. Because now we have typescript. We want to know exactly what are the procedure names. We want to know exactly what are parameters that are accepted by our APIs. So that's all. If you have any questions after this talk, you can follow me on Twitter. I'm AlexandraSes. If you scan this QR code, it will take you to my personal website with the slides. I also have a bunch of resources that I used to prepare this talk. And if you want to buy fewer presents, you can visit everybody.gifs. Thank you. Thank you so much, Alexandra. Why don't you take a seat? I have some questions. Okay, that's awesome. Yes. Let's see. All right, cool. Well, first of all, congratulations for making it through the day's first technical challenge. Yeah, that was stressful. Yes, but I think you handled it very well. So good job. Let's start with a simple question from the audience. If someone has never used RPC, what would you recommend them to try out first? So I guess which library or which tool, which approach? Well, RPC is not such a new idea. It's just you can imagine that when you're calling javascript functions that are in a separate file, but now they are not in a separate file. They are somewhere else. And you need this bridge that I was talking in the beginning of my talk to be able to access it. So that's the whole idea behind RPC. So I think you can start with TRPC or even Bleed RPC. It depends on the project that you want to build, because Bleed RPC currently only works for next.js. TRPC is working with Next, Spelt, and it's framework agnostic. So you can start playing with TRPC, see how it works for you. And that should be a good start to learn more about RPC. Nice. And I think here's a question that dovetails nicely to that. What would be a smooth RPC solution when backend uses Java or Kotlin, like a JVM backend? And maybe a broader question there is, is RPC an appropriate solution for a non-monolanguage environment? Yes, yes. But we need to have, like, when I was talking about Corba, I was talking about this IDL, interface definition language. So we need something like that. We need a mapping between languages. So for example, there is this RPC framework of GRPC, which uses Protobuf, and it does mapping between different languages. So you can have communication between servers written in completely different languages. And Protobuf has all the information about the schema, the types, how the mapping should work. So in order to use Java as a backend and something else as maybe another backend or as a client, you need to have something like that. So maybe GRPC is something to try, or something similar. I would guess that that's the way to go. Nice. Let me ask them the opposite question, which is that when you have multiple clients to a single server. The question here is, isn't using TRPC or Blitz a problem when we have multiple clients? For example, a native mobile app and web clients? Well, if you have the same application and you have, like, a web application and a mobile application, it may not be such a problem. But that's true that Blitz RPC and TRPC, it's not meant to be consumed. It's not meant to create APIs that are consumed by many clients. For those kinds of things, we have graphql, we have REST. And what's worse, Blitz RPC and TRPC great fit is full stack frameworks like next.js, when we have everything coupled together. Because that's what I think is also mentioned in TRPC docs, that for better or for worse, TRPC tightly couples client and server. Yeah, I think, you know, if I may kind of inject there, the way I've always thought about RPC, it's kind of in the name, it's a remote procedure call. Like, it's almost like you want to make a function call, but there's a network boundary in between. And so it's just a solution to that problem and not necessarily like an api layer for all. Yeah, yeah, yeah. Cool. I'm going to take a couple of more audience questions. But I wanted to ask my own question, I think, because one thing, the historical perspective that you gave is, you know, we went through, you know, these different approaches, you know, Corba and SOAP and so forth and so forth. And sometimes it feels a little bit like we're just making this up. Like, we keep going, you know, from, you know, a FAT client to think client, we keep doing RPC to like service oriented catalogs. And then we go right back to the previous one. Yeah. Are these just like trends and forces? Or do you think there's like something changing in the kind of environment or the problems that we're trying to solve that now truly makes RPC a more appropriate solution today than it was maybe five years ago? Yeah, yeah, I think it's still related to how other things evolve. So how languages evolve, how frameworks around languages evolve, because, you know, I don't know if a state of frameworks and state of javascript and typescript, I don't know, 10 years ago, RPC and, you know, like tools like tRPC and bRPC wouldn't make such sense. But right now, since we have this evolution in full stack frameworks, like next.js, remix, and the typescript becoming super popular, we can revisit those things. And I think this is, this is why we like keep going in circles, because, you know, other things are becoming popular or other things are being created. So some things that were once a bad fit for the current state of ecosystem can now be revisited. Interesting. And do you think that the rise of sort of, like distributed javascript edge run times is also making RPC sort of more attractive, because suddenly the network boundary becomes a lot more fast, right? Like it's easier to model things as a function call? Yeah, yeah, I think, I think it is. So yes, actually, one of the things why RPC was kind of like, hated, or not particularly liked back then, was because it was being marketed as something that allows you to create local remote procedures, exactly the same as if they were local. But that was very flawed, because there was a network and there were a bunch of issues that you have to face when there's network involved, when there's transport layer involved. So now I think when we have like this functions on edge, and when we kind of like limit those problems with network, we can kind of revisit this promise, we can kind of like, not be so skeptical about this promise of treating remote calls as if they were local, because there's actually this network, network part is very minimal. So the problems will be minimal. Yeah, it's almost as if the physics are changing, right? Like, if you can call a remote function 60 times a second, you know, suddenly, things become a lot, you know, a lot more attractive. Yeah. Cool. Let's go back to audience questions. We have a few more minutes. So every solution has its drawbacks. Whoever brought this up, it is unfortunately true. What are the drawbacks for the current state of RPC libraries? And which of them should we work on to remedy? So what's still missing in the landscape of, let's say, for example, javascript or typescript RPC that we should be working on next? Oh, that's a good question. So something that might be missing is this. So I mentioned before that the RPC and Blitz RPC tightly coupled server and client. So maybe that's something next to work on so that we have server APIs that can be accessed by multiple clients. It's the first thing that we were discussing today. That's one thing. And well, I'm still looking forward to having the best developer experience tool. Because to be honest, I may be quite opinionated here because I used to work on Blitz RPC. But I still like the developer experience of Blitz RPC. I like how it works. There's a tradeoff. It's this magical imports have this drawback that Blitz messes up with webpack. But it's quite a nice developer experience. And TRPC works slightly different. It's more explicit. So I would love to see a tool that combines it, that is framework agnostic, that has more of the Blitz RPC experience. Yeah, but you currently, we talked yesterday on the speakers dinner, you currently don't actively full-time work on Blitz right now. So as final question, what's next for you? What is it that you're excited about and what do you want to work on? I don't know. That's the very honest question. I'm sorry, I thought it was going to be an easy question. No, no, I'm still figuring out. There are a lot of things I like. I like talking about APIs. I like databases. So I think something around it. Nice. And will the demo code that you just showed us, will that be available online? Yes, yes. That's on my GitHub. My nickname is B-Rose. Yeah. Nice. I use emoji generally. Cool. All right. Well, we don't have, unfortunately, any more time for questions because lunch is waiting. So let's all say thank you to Alexandra. And if you do have more questions, you can find her at the Q&A booth.