A Brief History of API: RPC, REST, GraphQL, tRPC

Read Time:9 Minute, 7 Second

Last week I joined an event about GraphQL or TRPC discussion hosted by Guild in London. The creator of tPRC Alex and urql GraphQL core team member Phil had a good discussion about each camp’s general usage and problem. You can watch the whole discussion below:

A little surprise to me is that there are quite a few young guys who only used tRPC since it’s really very efficient to start from scratch, not knowing much about GraphQL. So as an old developer coming from the ice age, I probably could give a brief history about that. Why?

As Yuval Noah Harari says in HOMO DEUS:

This is the best reason to learn history: not in order to predict the future but to free yourself of the past and imagine alternative destinies. Of course, this is not total freedom – we cannot avoid being shaped by the past. But some freedom is better than none.

Hope it will free you to see more alternatives.

Let’s start with the definition. As defined in Wikipedia:

An application programming interface(API) is a way for two or more computer programs to communicate with each other.

In our scope, it’s all about how the client sends and receives messages from the server through the internet.

If you come from the same age as me, I bet the first network-related program you have ever seen or written is the chat application. 😄

During that time, to talk with the server, you need to use the Sockets library(which has nothing to do with the Socket.io or Websocket) provided by the operating system to send the message. The code on the server side would be something like the below:

int main() { // Bind if (bind(server_socket, (struct sockaddr *) &server, sizeof(server)) < 0) { printf("Error bindingn"); exit(1); } // Listen listen(server_socket, max_clients); // Accept and incoming connection printf("Listening for incoming connections...n"); int c = sizeof(struct sockaddr_in); while ((client_socket = accept(server_socket, (struct sockaddr *) &client, (socklen_t*) &c))) { printf("Connection from %sn", inet_ntoa(client.sin_addr)); // Send welcome message to client char *message = "Welcome to the chat room!n"; send(client_socket, message, strlen(message), 0); // Create new thread for incoming connection pthread_t sniffer_thread; if (pthread_create(&sniffer_thread, NULL, connection_handler, (void*) &client_socket) < 0) { printf("Error creating threadn"); exit(1); } } return 0;
} void *connection_handler(void *socket_desc) { // Get the socket descriptor int sock = *(int*) socket_desc; int read_size; char buffer[BUFFER_SIZE], message[BUFFER_SIZE]; // Receive a message from client while ((read_size = recv(sock, buffer, BUFFER_SIZE, 0)) > 0) { printf("Client %d: %s", sock, buffer); snprintf(message, sizeof message, "Client %d: %s", sock, buffer); }
}
Enter fullscreen mode Exit fullscreen mode

So here what the API provides you is simply the send and read function to send/receive the char bits. The rest is entirely on your own. You can imagine how troublesome and error-prone to deal with the char bits to do business.

As the trouble is mainly caused by handling bits transmitted over the network, people start to wonder if we could be agnostic about the network, simply directly call the function in the remote server like we call the local function. That gives birth to the RPC.

Actually, during the casual chat after the event, one guy said “I still don’t quite get what tRPC does”. I responded to him using the definition of the RPC “tRPC is to allow you to call the remote function simply like calling your local function with Typescript”. Then he seems to get it. See that could be a justification for the benefit of learning history. 😄

In order to do that, you first need to define your API in the IDL(Interface Definition Language) file like the below:

//file hello.idl
[ uuid(7a98c250-6808-11cf-b73b-00aa00b677a7), version(1.0)
]
interface hello
{ void HelloProc([in, string] unsigned char * pszString); void Shutdown(void);
}
Enter fullscreen mode Exit fullscreen mode

Then you can use the IDL compiler tool to generate the code stub to take care of the serialization and deserialization of the message for both the client and server sides:

RPC

Does it should familiar? Yes, that requires the code generation from the schema file which is the same case for GraphQL now.

Although it simplifies the work that needs to be done to talk to the server, there are several cons:

  • Tight coupling to the underlying system. It makes the client and server tightly coupled, so it’s more suitable for the internal API rather than the external API.
  • Low discoverability. there’s no way to introspect the API or send a request and start understanding what function to call based on its requests.
  • Hard to debug. The data serialization and deserialization rule are defined by NDR(Network Data Representation) including how to deal with the pointer data in 32bit and 64bit systems which are extremely hard to debug if there is anything wrong.

gRPC vs tRPC

I see lots of people asking about the difference between gPRC and tRPC as they look really like twins. Although they were both born in the modern age, gRPC is more like the lineal descendant of RPC with some advanced equipment like:

  • Protocol Buffer as IDL
  • use of HTTP/2
  • Bidirectional streaming and flow control
  • Authentication

Whereas tRPC is more like the distant relative probably only gets the last name of it. So we would take about it later.

SOAP

SOAP is released by Microsoft. Apparently, it looks like nothing related to RPC, but if you know its father’s name: XML-RPC, you might see the point. Actually, SOAP inherited a lot of RPC, it uses the WSDL(Web Service Description Language) as its schema file, which is represented by XML so that it is language and platform-agnostic which allows different programming languages and IDEs to quickly set up communication. Moreover, It provides privacy and integrity inside the transactions while allowing for encryption on the message level, which meets an enterprise-grade transaction quality.

I happened to work in the protocol suite team at Microsoft, there was a big adoption of SOAP where almost all the newly created protocols are based on SOAP, a small part of which are REST.

However, REST soon overtook it and was dominate the world.

Why?

while water can carry a boat, it can also overturn it.

  • It is XML that makes it language and platform-agnostic; it is also XML that causes it consumes more bandwidth and is slow to process.

  • It is the security that makes it enterprise-grade quality; it is also security that causes its lease easy to get up at first.

So what really changed to make those cons unbearable? It’s the mobile internet that brings us to the modern age.

REST

unlike others, REST doesn’t have strict rules standard toolkit comes with it. It is an architectural style and it defines a set of architectural constraints and agreements. An API that complied with the REST constraints is RESTful.

So the most advantage it has is simple and intuitive. It uses a standard HTTP method and resource centralized way to define API like below:

REST

It’s like bringing the OOP concept to the API world, making everyone could easily define the RESTful API which is why it’s become popular.

BTW, I have seen lots of projects only use GET and POST, which won’t affect the power REST granted at all.

However, as aforementioned, this loose standard also causes some problems:

  • As the project grows, the code becomes hard to maintain and error-prone.
  • It usually makes the frontend and backend team tightly coupled, as any change in API requires both to cooperate together.
  • It takes a number of calls to API to return the needed staff.
  • It usually have the over-fetching and under-fetching problem.
  • Sometimes it has to create duplicate API endpoints to adapt to different client consumers like the browser and mobile app

Because of the above, GraphQL came to the stage and become a game changer.

GraphQL

The main measure it takes to overcome the problem of the loose standard is to bring back the schema file.

type Book { title: String author: Author
} type Author { name: String books: [Book]
} type Query { books: [Book] authors: [Author]
} type Mutation { addBook(title: String, author: String): Book
}
Enter fullscreen mode Exit fullscreen mode

And once the schema is defined, it becomes the single source of the truth. The backend team and whatever different front-end team can work independently based on the schema. With the help of code generation, the request could be verified by the compiler to make sure the server will be able to respond to it.

All the problems mentioned about REST are resolved which you can simply see from the front page of the official website of GrqphlQL.

The best one I like is that the client gets to decide what the response will return which means they never need to push the backend to add fields for the response.

gql-gif

Until now I think GraphQL is the most efficient solution for a big project with a separate front-end and back-end team. As long as you get an experienced engineer to make the right schema design, it will pay off when you scale.

Welcome to the future, if you have a business idea, just build it.

Mincraft

Don’t worry, it’s much easy now. See what I got for you.

tRPC

TRPC

Although it is called RPC, it only inherits the concept to call the remote function locally but in a more pure way that doesn’t have the IDL file and the code generation process. You really have exactly the same experience of creating the local program:

tRPC-gif

So what about the tightly coupled issue and low discoverability issues? it still exists, but the world has changed.

With the development of Typescript and the framework like Next.js, it is the first time in history that you can build a full complete web app using one language in one single project. So it’s definitely tightly coupled, and there is no need for discoverability as you can directly go to the definition in the code.

Although without schema there are some disadvantages like the client can’t decide what the response is or it might be difficult to organize the functions when the project scale, remember tPRC is just two years old, it’s the future that matters.

trpc star

How about let’s hold together and build the brighter feature together?

alex


P.S. We’re building ZenStack— a toolkit for building secure CRUD apps with Next.js + Typescript. Our goal is to let you save time writing boilerplate code and focus on building what matters — the user experience.

Source: https://dev.to/zenstack/a-brief-history-of-api-rpc-rest-graphql-trpc-fme

WP Ad Inserter plugin for WordPress