DEV Community

Cover image for A Brief History of API: RPC, REST, GraphQL, tRPC
JS for ZenStack

Posted on • Edited on

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

Background

Last week I joined an event about GraphQL VS TRPC discussion hosted by Guild in London. The creator of tPRC Alex and urql GraphQL core team member Phil(urql is a highly customisable and versatile GraphQL client for Javascript and its frameworks) had a fantastic 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 have 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.

What is API

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.

API

The Ice Age

If you are 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, if you want 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 binding\n");
        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 %s\n", 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 thread\n");
            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.

The Iron Age

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 what we call the local function. That gives birth to the RPC(Remote Procedure Call).

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 rules 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(Interface Description Language)
  • 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 will talk about it later.

SOAP

Before REST bombarding us, there was SOAP. SOAP was 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 it is both 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 dominating 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 causing it to consume more bandwidth and is slow to process.

  • It is the security that makes it enterprise-grade quality; it is also security that make it less easy to set up if you want to scaffold a project quickly

So what really changed to make those cons unbearable?

It’s the mobile internet blooms that brings us to the modern age

The Modern age

REST

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

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

REST

It’s like bringing the OOP concept to the API world. REST is popularised because it helps everyone to easily define the RESTful API.

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

However, as I mentioned earlier, 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 changes in API requires both side to cooperate with one another.
  • It takes a number of calls to API to return the needed staff.
  • It usually has the over-fetching and under-fetching problem.
  • Sometimes developers have to create duplicate API endpoints to adapt to different client consumers like the browser and mobile app

Because of the reason listed above, GraphQL came to the stage and became 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 will be verified by the compiler to ensure 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 GraphQL.

My favourite feature about GraphQL was the fact 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, especially if the API needs to be consumed externally.

The Future

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

Mincraft

Don’t worry, it’s much easier now. Let's see what the world offers.

tRPC

TRPC

Although the suffix is called RPC, it only inherits the concept to call the remote function locally but in a more simplistic minimal 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 scales, remember tRPC is just two years old, it’s the future that matters. Having been implemented within Netflix, Pleo and a number of other companies, what tRPC has achieved is impressive itself.

trpc star

How about we hold together and build a brighter future together?

If you don't want to spend too much time on API design and implementation, you could checkout ZenStack: It enhances Prisma ORM with with access control policy(Authorization)layer and automatically generate APIs(REST/tRPC) and frontend hooks for you.

alex

Top comments (15)

Collapse
 
ushieru profile image
Ushieru Kokoran

I believe that gRPC has an advantage over tRPC because it is multilingual. But I don't rule out that tRPC could grow someday to new horizons. We can only wait.

Collapse
 
fruntend profile image
fruntend

Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍

Collapse
 
jiasheng profile image
JS

Thanks! I will do my best.

Collapse
 
sirajulm profile image
Sirajul Muneer

How will tRPC work with other languages like python, Go, Rust etc. ?

Collapse
 
jiasheng profile image
JS

For now, it's Typescript only because it's the only lanaguage that could be used for both frontend and backend.

Collapse
 
willm profile image
Will Munn

This is simply not true. Sure if you're building a single web app in start up mode this is an attractive solution to get you going. It's also totally possible to build a backend and front end in kotlin, swift, c++ or many other languages. One great thing about rest APIs and graphql is that they allow you to develop your server logic once and use it in all your website's, mobile apps, desktop apps and iot devices. I'd like to see how this scales to a business that has been alive more than 5 minutes.

Thread Thread
 
jiasheng profile image
JS • Edited

Maybe I didn’t state it clearly. What I was trying to say is that the way tRPC works requires both frontend and backend using the same language, so it’s Typescript only.

I think you are right, scale is indeed an issue usually brought up, as I also mentioned in the post. But there does have some online businesses has used tRPC, like:
cal.com

You can see more here:

👋 Who's using tRPC? #1290

KATT avatar
KATT posted on

Drop your company name, svg logo, link to your website, and links to any repos using tRPC! 🙌

Some companies / sites I know myself

Anyway, as I said in the post tRPC is just two years old, it’s the future that matters.😄

Collapse
 
khapxungquanh profile image
Tin Nguyen

There is a trpc-openapi help you make it REST, have a look at: github.com/jlalmes/trpc-openapi

Collapse
 
bkoprivica profile image
Bran Koprivica

To say that an API is a "way for two or more computer programs to communicate with each other" is equivalent to saying that API is a computer network. For computer network is also a way for "two or more computer programs to communicate with each other". Or saying that everything is a part of the universe. Of course it is.

So what is API?

I would focus on the letter "I" in "API, i.e. it is an interface.

What kind of interface?

Letters A and P tells us that it is Application Programming Interface. Now, for the last few decades, application programming was about two things: components and services. CBD (Component Based Development) pretty much explained everything about components, so let's focus on services.

What is a service?

Service is a remotely accessible component.

There are 3 things that characterize every service:

  1. Service interface
  2. Service implementation
  3. Component

Remember that we defined service as a remotely accessible component. In other words, service interface and service implementation help with this remote access part (as not every component is remotely accessible, of course).

But what does service interface actually mean?

Service interface represents a contractual agreement between parties, just like contracts in legal documents where obligations of the service provider (an entity that offers a particular service) are stipulated in the contract precisely.

In the last decade or so, we moved from SOA paradigm to microservices. Hence, when we say "service interface" we 99% of time mean "microservice interface". That is what API is, a design contract for accessing a microservice component remotely.
Of course, we can also say that APIs are an integration style, because it markedly differs from other integration styles such as file transfer, MOM, distributed objects, and shared databases.

Collapse
 
orpheusexultant profile image
Tom Kelleher

Re: "there is no need for discoverability as you can directly go to the definition in the code."

I don't follow this logic. If you own the backend of any system, built with any technology, the notion of "discovery" is eliminated, right? That's a tautology: You don't need to discover what you control.

And for all technologies, if you don't own the back end, you need to "discover" it in some sense (automatically thru GraphQL schemas, or SOAP WSDLs -- or manually, by reading REST API documentation).

So does tPRC offer any discoverability advantages?

Collapse
 
jiasheng profile image
JS

you are right, the "discovery" is actually eliminated. Sorry for the confusion caused.

Collapse
 
vladika profile image
Vlad • Edited

"If you own the backend of any system, built with any technology, the notion of "discovery" is eliminated, right?"

This statement could only hold true for some "personal projects", where past, present and future developers is all one and the same person. In practice, with old devs leaving and new joining the discovery is a constant process, so the culture of good practices, including extensive documentation are necessary. Furthermore, for the statement “Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.”, what usually holds true is that maintainer is yourself, just 6+ months down the track, when you forgot all the details, and need remainders.

Collapse
 
devwise profile image
Rahul Kumar

Through it, I relived my own journey from SOAP to REST to GraphQL. I am still not on the bandwagon of gRPC/tRPC yet. But, I believe that it’s the next step in the evolution of these protocols. Thank you for writing this article.

Collapse
 
maeseoki profile image
maeseoki

tRPC looks good to me but (there is always a but) it actually doesn't looks like a good answer to me. First of all, it's only TypeScript for now witch is a really bad idea right now. Don't take me bad. There are a lot apps whos backend are in other languages. Actually, there are many apps that use several backend languages.
Looks really good to make hhtp calls as a function call but that's essentially RPC and, by my experience, this can lead to several problems.
The best approach for me, after all, is REST and, of course, GraphQL.
With the first one, the backend team just have to tell you what the backend will respond. The problem is that you may get more data than you want. And this is a security issue.
In the other hand, GraphQL just gives you the data you want, and it's ok, but backend may be overkill as backend is getting more data than you want. But al least you just get eat you want.
For me, the best approach is to be in a good communication with the backend team to get the right data from the backend as it passes a DTO or a really good shaped GraphQL query. But this one can be tricked down too.

TL;DR;
There is no magical anwer right now. But REST with an awesome documentation seems to be the best answer.

Collapse
 
jiasheng profile image
JS

There is no free lunch, so it’s all about trade-offs. In general, the less the framework does, the more flexibility you have, but the more work you need to do by yourself. I think GraphQL and REST would still be the most popular ones for a while, but I always love to see the new challenger come to play which could push the development world to a better place.

I agree that REST has the most flexibility but it also means that there are lots of choices you need to make on your own like choosing the right tool to generate the documentation. 😄