The standard for data exchange between frontend and backend has always been a bit contentious.
With different API technologies available for sharing data between clients and servers, and each one having its own set of unique capabilities, it can be quite daunting trying to decide which one serves you best.
The three most popular technologies currently for creating APIs are GraphQL, gRPC, and REST. In this post, we’ll look at how each one works, including their pros and cons.
GraphlQL is a data query language that uniquely allows clients to request any specific data that they need. As opposed to REST’s HTTP methods, GraphQL uses queries, mutations, and subscriptions for sourcing and manipulating data.
Queries request data from the server while mutations send data to and modify data gated by the server. Subscriptions get live updates when data changes, usually through Websockets.
In all, GraphQL supports languages like JavaScript, Java, Python, Ruby, PHP, and more.
With GraphQL, data is represented through schemas. A schema defines an object, the fields that the object contains, and the type of data the fields can hold. Here’s an example schema:
type User { name: String! age: Number! }
Here, User
is the object, and inside it, we specify the fields or properties the object has, including data types.
What this means is that in any query operating on the User
object, the only fields that can appear are name
and age
. For both of these fields, the only kinds of data that can be written to or read from are strings and numbers, respectively.
In GraphQL, when field types are trailed with exclamation marks like what we have above, it means that the fields are non-nullable. They must have data written to them when the object updates and the fields always provide values when queried.
As mentioned before, there are mainly three actions that can be performed on data objects: queries, mutations, and subscriptions. These can also be defined using GraphQL’s schema language.
With a query
schema, you can request data from your GraphQL server by specifying the object and fields under the object you’re interested in:
query { users { name age } }
Above, we request a list of the users in the database, specifically their names and ages. The user
object could have other fields like gender
or job title
, for example, but only the fields we requested will be sent to us.
This is one advantage that GraphQL has over REST. In REST, when an endpoint is queried, all the data behind that endpoint is sent to you, leaving you to sort through the array and select the particular fields you need.
GraphQL is more flexible because it allows you to customize the structure of your result. This saves you the memory and CPU consumption that goes into filtering data.
And, because GraphQL queries generally request less data, they are usually processed faster than REST requests.
With a mutation
schema, you can make changes to existing data or post new data to your database. For example, if you wanted to create a new user or update the details of an existing user, you’d use mutation
:
mutation { newUser($name: name!, $age: age!) { name age } }
With a subscription
schema, you can subscribe to an object and have your server update your client in real-time whenever changes are made to that object.
For example, if you want your client app update in real-time whenever an existing user is deleted or modified, or a new user is created, you can subscribe to the User
object and your fields of interest:
subscription User { newUser { name age } }
With GraphQL, you can define the exact scope of data required in every instance. By preventing excess data from sending from the server to the client, your app performs better.
Since you can select multiple fields from different resources in a single query, GraphQL eliminates the need to carry out multiple round trips to the server to fetch data for your client.
With so many third-party code generators, it is easy to write GraphQL code for one platform and have it created automatically for a second platform using these generators. You can explore the possibilities over at GraphQL Code Generator.
As you can define the exact data you want the server to respond with on the frontend, you can add new fields to the data model on the server side without the need to create a new versioned API. This also leads to less breaking of frontend code just because you added a new field to the model and forgot to update the frontend code.
GraphQL has some serious caching problems (specifically HTTP caching) that have contributed to the prevention of its widespread adoption.
Due to the way GraphQL queries are written, requesting too many nested fields at once can lead to circular queries and crash your server. Implementing rate-limiting in REST is more seamless.
GraphQL also doesn’t support file upload out of the box and requires a workaround to implement this functionality.
GraphQL support is still flaky for different web/application frameworks compared to REST. The JS ecosystem has the most support, but the landscape is changing and the support is improving day-by-day for other languages and frameworks.
And finally, compared to REST, GraphQL has a steeper learning curve.
The most popular and most used API format on the list is REST, which stands for representational state transfer.
REST APIs use HTTP methods like GET
, POST
, PUT
, and DELETE
to access and manipulate data within uniform resource identifiers (URIs):
GET
methods retrieve data from a serverPOST
methods transfer data from the client to the serverPUT
methods modify data in the serverDELETE
methods erase data in the server.When in use, for example, a GET
request can perform on a URI that typically looks like /api/users
; a REST
operation typically returns data from the server to the client.
The REST response data can then come in formats like JSON, CSV, XML, or RSS, and the REST response data tends to come in property/value pairs:
{ "users": { { "id": "1", "name": "Dan", "age": "20" }, { "id": "2", "name": "Kim", "age": "19" } } }
Caching is easy in REST because it takes advantage of HTTP by eliminating the need for your client and server to constantly interact with each other, improving performance and scalability.
REST’s maturity in the tech world is one of its biggest advantages. It is ubiquitous, as opposed to GraphQL and gRPC, which can be considered niche solutions to creating APIs.
Due to REST’s popularity, lots of developers are already familiar with it and find it easy to work with. And so, if you create a commercial API for many third-party developers to consume, then REST might be the best option for you.
It is easier to monitor and rate-limit REST based APIs compared to GraphQL as each resource is usually located behind a unique endpoint. As a result, individual resources can also be easily locked behind a paywall.
Large payloads; where you can select specific fields under a resource in GraphQL, REST requires you to request all the data under the resource before looping through it to get the data you need, which often results in large payloads. You can counter this by creating a new endpoint or adding server-side filtering based on query args, but it is not feasible to do that for each use case.
While with GraphQL we can batch different resource requests and send them to the same endpoint at /graphql
, and executed once in one round trip, with REST, different requests must be sent to different endpoints, resulting in multiple round trips.
GraphQL makes making changes to a database schema very easy. Since queries only involve requesting specific fields, new fields can be removed from a resource without causing breaking changes and requiring you to version your application.
gRPC also comes with its own backward compatibility features that make it easy to update your data structure without breaking clients’ code. REST does not come with this flexibility.
gRPC is a high-performance, open source remote procedural call (RPC) framework created by Google.
In gRPC, protocol buffers make API requests to the server. These API requests and responses are protocol buffer messages, and each one must have its own type defined using the protocol buffers language.
Services are defined to pair API requests with their corresponding responses, and the protocol buffers compiler generates the server and client code artifacts for your API service:
// Syntax version syntax = "proto3"; service UserService { rpc GetUser (UserRequest) returns (UserResponse); } // Message type message UserResponse { string name = 1; string age = 2; } message UserRequest { string name = 1; string age = 2; }
In the code block above, the message UserRequest is the request, the message UserResponse is the response, and GetUser is the API endpoint.
gRPC supports a host of languages and your client app can be written in any of these languages because the client code for any of these languages can be generated with the help of the protoc
tool. As of today, gRPC supports the following languages out of the box: C#, C++, Dart, Go, Java, Kotlin, Node, Objective-C, PHP, Python, and Ruby.
Protocol buffers, which gRPC comes out of the box with, support more data types than JSON, and it’s significantly faster, thanks to its optimized binary format.
gRPC supports full-duplex streaming out of the box, which makes it suitable for features like video and voice calls. With REST on the other hand, queries are handled one at a time.
gRPC performs load balancing so that client requests are evenly distributed across servers to avoid overloading a particular server. This improves the overall performance of your application.
The load balancing feature, as well as the fact that gRPC uses HTTP2 by default, makes latency in gRPC much lower than in REST APIs.
gRPC is designed to be backward compatible, so adding new fields/methods to a gRPC service doesn’t generally break existing clients. This makes sure that you don’t have to update all existing clients when you modify the service.
Finally, gRPC serializes data in binary format (protocol buffers), which is much faster than REST’s JSON, giving gRPC a greater performance advantage.
Protocol buffers, gRPC’s official data format, currently only supports a handful of languages. It doesn’t support languages like R and Swift. JSON, which REST uses, is supported by virtually all languages.
gRPC doesn’t come with browser support out of the box, although there are some workarounds for it. You will have to use gRPC-Web, which is an extension of gRPC for the web and is based on HTTP 1.1. It doesn’t provide all gRPC features, but is a nice bridge if you want to use gRPC in the browser.
Feature | GraphQL | REST | gRPC |
Data fetch | Efficient fetching of data. Only required data will be fetched from server. | Extra data might be returned by the server unless new endpoints/query filters are defined on server side. | Extra data might be returned by server unless new endpoints/filters are defined on server side. |
HTTP 1.1 vs HTTP 2 | Follows request-response model. It can work with either HTTP version but is typically built with HTTP 1.1. | Follows request-response model. It can work with either HTTP version but is still typically built with HTTP 1.1. | Follows client-response model and is based on HTTP 2. Some servers have workarounds to make it work with HTTP 1.1 but it is not the default. |
Browser support | Works everywhere. | Works everywhere. | Limited support. Need to use gRPC-Web, which is an extension of gRPC for the web and is based on HTTP 1.1. |
Payload data structure | Uses JSON-based payloads to send/receive data. | Mostly uses JSON- and XML-based payloads to send/receive data. | Uses Protocol Buffers by default to serialize payload data. |
Code generation | Need to use third-party tools like GraphQL Code Generator to generate client code. Docs and an interactive playground can be natively generated by using GraphiQL. | Need to use third-party tools like Swagger to generate client code. | gRPC has native support for code generation for various target languages. |
Request caching | Hard to cache requests by default as every request is sent to the same endpoint. | Easy to cache requests on the client and server sides. Most clients/servers natively support it. | Doesn’t support request/response caching by default. |
At the end of the day, the best API technology for your specific project will depend on what you are trying to achieve. If you need a generic API that will be used by a lot of clients, then REST might be your best option.
If you need a flexible API that different clients will use to make many unique requests, then it might be best to let the clients create their own unique queries and get only the specific data they need quickly, and you can achieve this with GraphQL.
If want to create fast, seamless communication between internal services, then gRPC is your best option.
As we’ve seen, all three have their pros and cons and you can get optimal results by combining technologies.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic GraphQL requests to quickly understand the root cause. In addition, you can track Apollo client state and inspect GraphQL queries' key-value pairs.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
7 Replies to "GraphQL vs. gRPC vs. REST: Choosing the right API"
Your description of REST cons is a good example of some common misconceptions about REST APIs. This largely stems, I think, from thinking of resources as rows/documents in your database… and this just is not the case.
I’m not aware of anything in the REST specification that requires full transmission of a resource or even all the resources when you GET a REST endpoint. Plus any REST API I’ve worked with allows you to use “fields” and “query” query parameters to filter how many resources and how much of each resource is returned. Some APIs even default to only returning the id and an e-tag for the resource which you can later use for optimistic locking requests… and you can’t really ask for a smaller payload than that.
Further, versioning a REST API because of fields being added isn’t necessary. You can always add information without re-versioning because the consuming code is already ignorant of it. It’s only when you remove fields where this is a problem. Or you just create a new endpoint with the new “version” of the resource and add another link in your HATEOAS that will allow applications to grab that instead.
And lastly, I fail to see how having many vs a single endpoint affect batch processing unless you are needing to have atomicity. But even then, this can be resolved by creating a new REST endpoint that represents the collection of batch requests to the server. You make a single POST there that contains the information required to process the request, and then a GET against that endpoint can return the status of the request while PUT, PATCH and DELETE could be used to modify the request of delete it if the server has not begun processing it yet.
My question to you is…. Did you check out Ankr’s api toolkit and public RPC protocol before you did this article??? Im not affiliated with Ankr but their products are top notch…
There are some serious “assumptions” made here. Caching, for one, seems to be thought of a good thing and, while it is in some cases, it is the very first thing disabled (e.g. when access control is needed) in others and/or being only a necessary evil to make the thing perform acceptably (usually with REST). This also assumes a trivial implementation of GraphQL without any of the advanced frameworks, many of which are available. GraphQL requests cannot be circular and deep repetitive requests actually benefit from server-side query optimization available in those frameworks and server-side transaction caches even if we ignore any others. Think what would happen if you needed the same data with REST… hint: worse, much, much worse). Finally, the learning curve: I will claim that GraphQL, due to embedded documentation, standardization and tooling is much easier to learn for API users than any other. REST is absolutely the worst of all. On the server side, implementing GraphQL properly isn’t easy and requires some investment – explaining the common misconceptions. But REST is horrible in its own way as, no, NO, not everything is or should be a “resource”. There are actions, different kind of mutations, notifications all of which are horribly represented in REST (they aren’t really).
It doesn’t come across to me like you’re judging REST fairly here. I totally believe what you’re saying reflects your experience with REST APIs, but REST is such a buzzword that anyone using an HTTP API calls it a REST or RESTful API when it either doesn’t conform to the standard or uses it too narrowly as a simple CRUD wrapper over a database.
For example, we use REST to model actions and all sorts of mutations in our API to the point where we rarely, if ever, use verbs outside of GET and POST. Every type of action has a corresponding collection of actions against its base resource. Like /protocols/:id/submissions would be a collection of all the submission actions requested against a particular protocol. We practice CQRS so a POST against that collection causes our domain to decide whether or not to accept the command, and if it does, the protocol aggregate makes the appropriate state changes and persists them to the database.
So yeah, if an API architect is trying to shoehorn every kind of activity into the HTTP verbs against a single /protocols/:id endpoint, then sure: actions, mutations and notifications aren’t represented well if at all. But they are in our REST API just fine. Well, not notifications, but that’s because we don’t have a use case to allow an external application to manually trigger them… we have an internal RxJS event stream that notification listeners subscribe to which in turn generate notifications when particular events occur.
Also revisiting the learning curve issue: a proper REST API implements HATEOAS. That means that you should be able to give a user a single entry-point endpoint, and once they GET against that, you are told exactly what you can do via hypermedia. All appropriate actions with their endpoints and HTTP verbs allowed are returned along with any payloads.
For us, this allows our API to completely drive what is displayed on our front end apps because if something isn’t authorized due to security or just the workflow state, the URL for the resource isn’t returned so the front end doesn’t even have the knowledge of how to make the request at all. The only thing the app has to know is 1) the entrypoint and 2) the link structure. We can change the entire structure of our endpoints so long as the link structure remains the same so we achieve pretty minimal coupling.
But here’s the point: a properly constructed REST API should mean that all a human needs is the entrypoint. From there, you can just sit there with Postman and navigate through and explore the API with the information you get back… even completing business workflows and what not. You’ll need additional documentation for any payload requirements, but from what I understand, you need to have some idea of the schema a GraphQL server is running to run your mutations so I don’t see these as particularly distinct learning curves. And if you’re experience is that there is a big difference, again, I’d imagine it’s related to poor implementation on the REST side… which could be an argument in favor of GraphQL. Maybe it’s just harder to create a functional, yet difficult to understand GraphQL API, whereas it’s easy enough to make something awful “look” like a REST API.
Very good explanation.
Most people would call any ‘back-end-only-application-over-http’ a “REST” server. Where this is 100% incorrect.
Base GET: List every field, a list of (ideally with a default page)
POST: Insert what is minimally (at least) required [to insert anyways].
PUT: Update providing only what fields needs updating [with any required keys].
DELETE: Delete
To be RESTful it MUST conform to using the proper verbs and explicitly doing what those verbs represent. It was explained in the article beautifully.
Many applications expose endpoints that are actually routes that create a ‘headless’ application for manipulating what should be built with a graphical user interface, “programatically”. They essentially are “actions”, in a MV(a)C application, where the (a)ction [C]ontrollers allow some parameters through the route (not a query string – which is where the mistake is made in interpreting that the application is RESTful); while the application itself is mostly comprised solely of GET and POST requests. This is not REST. This is only a “server-side application designed without a dedicated GUI.
A good RESTful programming initiative demonstrates beautifully how functionally tactful a REST server really is; and they are a beautiful implementation the only caveats exist those that are in the nature of REST being something derived in recognition that those VERBS were intended to do what they do to data in a database – now, in abstract practice – to files in a file server historically (Apache never came with support for PHP, NodeJS didn’t exist when Nginx was invented – they served and manipulated files originally – and the rest is history). In that sense, if one were to construct an engine (in PHP, leveraging the later added mod_php module in Apache, or by inventing a “GraphQL module for Apache”) that would manipulate a JSON file appropriately upon accepting a request (rather than a database) they could honestly make it work (and it would work) essentially identically to GraphQL. At that point the nuance to it would be the same nuance GraphQL has – some distinction in implementation. GraphQL is essentially the recognition of what REST does [now] and how, and a specific solution in place of it that isn’t always the best choice.
The advantage GraphQL has (in use, not design) is SOLELY in that when viewing how we want a request to work (when using GraphQL); A request can easily provide ONLY the data requested, whereas in REST (derived as it is to wrap a database) you will need to know modifiers or alternate URIs (if they exist). However, you presume correctly that this is essentially very easy to explore and view as not being an issue at all; simply a distinction in use.
With GraphQL a user must create their requests with only the information they need to have it limited, and with all the information if they need all; while in REST you can make the simplest request to get all the information and use an alternate URI or add a modifier to get more minimal information.
It is extremely easy to make RESTful applications look horrible, and while easy to use REST correctly and make it look powerful and awesome it does require knowing what you’re doing in implementing it. Those diving in blind can easily, again, make it work – and make it look – horrible in implementation; it’s a concept applied over a set of technologies – there’s a lot of room for error! Ironically, it is extremely easy to make GraphQL look powerful and awesome – even by those diving in blind. It’s harder to make it look horrible, because its easy to blame the learning curve on inability to make it work – even poorly for a use case. You don’t get to decide on the implementation for GraphQL; its provided for you.
Throwing ElasticSearch on top of GraphQL doesn’t suddenly make it better than a RESTful application; it provides a framework for doing something with GraphQL that would otherwise require experience and proper implementation. It cannot, in contrast and design, be compared to the nuance of poor implementation of a RESTful application – the latter is such a specific derivation of a technology with a much broader use-case.
Honestly, comparing GraphQL to REST, regarding much of the argument, is like comparing a “Rocket ship” to “Transportation”. The original author did make an assumption: That the RESTful application was properly implemented.
In the end RESTful APIs are the simplest, and the most popular, for a reason. The person you’re responding to is the one making very strong assumptions (namely that the RESTful application is not implemented properly, where GraphQL by design only has its implementation) – and its clear that they are distinctly favoring GraphQL, and biased against the idea that its not always the best to use; which often happens when someone makes a risky decision to devote themselves to one technology and dislikes the idea that something else could ever be a better choice – and apt to argue.
I think anyone could read into the social psychology of it a bit more, but that just starts getting into unnecessary topic.
Those coming to this article for a read should not be deterred. There are fewer functional use-cases to GraphQL over REST. They both have their place; though there exist other solutions (arguably more effective) where GraphQL would perform better in place of a RESTful service.
I think if one is interested, they should try GraphQL out, and those interested in REST should keep in mind that it is easy to use a “framework” that supports REST the wrong way ⇨ in either case, pay strong attention to detail, read up on theory and practice, and pick what will functionally do what you need – most effectively and efficiently.
This has been a great comment thread, I learnt a lot.
@kevin and @Richard, any open-source project which would be good models to study from?
For someone who is starting out, these articles are the easiest starting point, but one should keep learning and realize initial sources of guidance can be limited, and one should keep digging into nuances from diverse authoritative sources.
Hi Samson, any insights with the above comments in mind. Do you disagree/ have more nuances to share? What’s your opinion?