There is no right kind of architecture for data exchange. What works for one organization's needs may not serve another's needs in the same way. But there are a few factors to consider when choosing the best method for moving information across systems. API design is, in fact, far more complex than it used to be.
Once upon a time, Simple Object Access Protocol (SOAP), the process of sending and receiving messages from one application to another using HTTP, was the standard for data transmission. Then came representational state transfer (REST), an architectural style coined by Roy Fielding in his PhD dissertation in 2000. RESTful API design set the stage for stateless servers, better access to resources, and more efficient data retrieval. But as times have changed, so has the way data is managed—and along came GraphQL.
While GraphQL is not perfect, its popularity is rising, and it's gaining more traction for a reason. However popular, the open source technology created by Facebook in 2012 should not be thought of as a replacement for REST but as a welcome alternative.
This article will explain some of the benefits of using GraphQL, including some tradeoffs and a few use cases from Netflix and Facebook. In addition, I've included individual contributions from Zach Lendon, director of application development at AIM Consulting and co-author of this article. I've also incorporated developer insights and feedback from Rajoshi Ghosh, co-founder of Hasura.io, Praveen Durairaju, a senior developer advocate at Hasura.io, and Joel Myers, a senior software engineer at Red Hat.
The purpose of this article is to expound upon the basics of GraphQL compared to REST to supplement your understanding of the major differences between the two and explain why GraphQL's popularity is on the rise.
The REST architectural style
In a RESTful architecture, the client and server must follow a definitive set of constraints in their interactions with each other. Cacheability is a constraint to keep in mind. Computed resources should be cached (or temporarily stored) for better performance and stability. It's the server's job to declare whether the response is cacheable or not and for how long it should be stored.
Another major constraint in a RESTful API design is the separation of client and server concerns. This concept, known as stateless, means the session data between them isn't tracked. Additionally, the client and server must be able to evolve independently of one another. Every request sent by the client is just that—an isolated request.
An additional constraint of RESTful APIs is its layered system architecture (e.g., authentication, persistence layer, load balancing, proxy, etc.). The functionality of one layer, or system, works off another to handle aspects of the API response, keeping the client blind to which server it's communicating with.
Lastly, REST's uniform interface means that no matter what kind of device or application, the resource in need of data from the server should have a Uniform Resource Identifier (URI) to fetch the desired information.
GraphQL works for front-end and back-end development
In 2019, Netflix, the world's largest streaming service, decided to program using GraphQL over REST and Falcor, an open source Java library that Netflix developed. Their decision was based upon the flexibility of GraphQL's technology to make data more accessible regardless of where it's stored. The pliability of GraphQL is one of its biggest benefits. For instance, a single-entity graph can be applied to multiple downstream integrations—connections between servers—including REST. "We define a GraphQL schema ... and then we build UIs around that," says Garret Heinlen, a Netflix software engineer. The move to this graph model also enables improved communication between different teams within the organization, namely for front-end and back-end engineers. Heinlen goes on to say, GraphQL acts as a "living source of [API] documentation." While he still supports REST, he appreciates the way GraphQL enables communication between his teams using different product schemas.
In REST APIs, endpoints are structured to allow clients access to all the corresponding data given by that endpoint. This is useful when a client wants a specific set of information. Where this kind of pattern gets tricky is when rapid iterations are involved on the frontend. When changes are constantly being made to the UI, the backend is basically forced to handle this demand, and productivity is notably hindered. Also, latency will be higher. However, with the flexibility of GraphQL, clients can specify their exact data requirements without these changes affecting the amount of work on the server side.
In addition, using GraphQL can provide you with helpful analytics on the backend without the need for additional fields outside of what the client has requested. This allows you to gain more insight into what kind of information your clients are interested in and develop a deeper understanding of how they're using the data.
The ability to monitor and measure resolver functions, which are basically query handlers, is another great facet of GraphQL. In doing this, you're able to carry out low-level application performance monitoring, which can produce incredibly useful insights into issues or bottlenecks in your application architecture.
How the GraphQL schema & type system helps developers build more intuitive APIs
One might say REST APIs rely on HTTP verbs, which makes them way easier to build compared to GraphQL APIs. That is very true. One might also say that when using REST APIs, you don't have to use any special packages to write proper queries. That would also be true. Learning the GraphQL query language is not exactly a walk in the park. But the flipside to this is, although you may know to use the typical HTTP verbs (GET/, PUT/, POST/), it's not always that straightforward when put to practice. REST APIs don't always follow HTTP protocol from top to bottom. As for having to go through the hassle of using packages or namespaces to house all your specified naming conventions and easily manageable client code, GraphQL's universal schema and type system makes it significantly easier to create a uniform API across your entire application without being restricted to one specific storage engine and language.
The GraphQL Schema Definition Language (SDL) is strongly typed, producing a more flexible and reliable communication structure to your front- and back-end teams. The defined schema serves as its own sort of designated language between the server and client. When everyone is speaking the same language, potential delays and miscommunication can be easily avoided. A strongly typed schema makes documentation easier to use and understand. It also helps with the early detection of errors.
To put this schema to work and get an API up and running, you want to add fields to the root types of the GraphQL schema: query, mutation, and subscription. To provide structure to your API, you want these queries and mutations to be accepted by a GraphQL server, which acts as a vehicle of sorts to drive your API. Every vehicle needs an engine to get it moving; a schema alone is not enough to add functionality to the server to get your API where it needs to go (or implemented). It needs resolvers, which help fetch data for each field in the defined schema. An effective schema is a vital part of building intuitive APIs, so enterprises should be thoughtful about their naming conventions for nodes and relationships in the GraphQL schema to ensure they make logical sense for their domain and users.
It goes without saying that one of GraphQL's greatest superpowers is the GraphQL query. The crux of GraphQL is asking for exactly what you want and getting back just that: no more, no less.
GraphQL simplifies how data is accessed and handled. With REST APIs, data is typically gathered from several different endpoints. This process of making several roundtrips to several different endpoints can create bottlenecks in data retrieval and wasted bandwidth, especially as you scale the application. There is also the possibility that while using REST, the client will receive more than what it asked for from the server (overfetching) or everything except what it asked for (underfetching). The unnecessary number of requests transferred could easily be executed using a single query sent to the GraphQL server. There are specific data requirements, but once they are fulfilled, the server responds with a JSON object, and voila—mission accomplished.
In REST, an endpoint is the identity of an object. GraphQL keeps an object's identity separated from how a developer fetches it. With GraphQL, there is no request too large or too small—the client can have it their way. Think of it like this: The way many people conceptualize or model a certain idea is through a table or graph, right? The "shape" of a GraphQL API can be thought of as a table or graph; one ingredient, type, or field derives from another and so on. The "shape" or structure of a REST API can be thought of as linear in that it describes its endpoints as a list of things that can only be accessed independently of one another. GraphQL's ability to naturally query nested data is where GraphQL's schema and type system comes in handy.
At times, modifications to the applications will need to be made to data currently stored in the backend. GraphQL calls these data modifications mutations. GraphQL achieves mutations by the process of creating new data (e.g., a new user signing up to your application), updating existing data (e.g., removing a user due to mischievous activity), or deleting existing data (e.g., fulfilling a user request to delete a profile picture). When new objects are created, GraphQL types associate a unique ID with them. To update existing data, you can specify a payload through different properties of the root field. Although the process for getting the initial resource from a GraphQL API and REST is similar, where REST falls short is having to define relationships through multiple requests or add special parameters to the URL to change the response.
"Designing GraphQL Mutations" by Caleb Meredith, a front-end engineer at Facebook, demonstrates a few key design principles to keep in mind when designing a GraphQL mutation system for your API. The first principle is the importance of naming. You want a name to fully describe what your mutation does. Specificity is critical in representing the potential actions taken by the user. You also want to always include a single input argument for the client to execute the mutation more easily and design the mutation payload in a way that makes it easier for the client to manage inputs and outputs, as well as metadata fields, over time.
Subscriptions help establish a steady connection between the server and the client by sending data back to the client when a specific event happens. It differs from a query in that it eliminates the request-to-response cycle by emitting messages asynchronously that enable the server side to notify clients about changes to back-end data. Hence, also unlike queries, subscriptions are long-lasting operations that can change their result over time. There are a couple of ways to subscribe to the latest result of a particular query. The first is via live queries, where the server pushes the latest result to the client as the underlying data changes. The other approach is via polling or refetching to get the latest data.
GraphQL is a great enabler of several key architectural patterns. One of the most powerful patterns GraphQL enables is the Strangler pattern, where GraphQL will often effectively be used to front a legacy application, providing a layer of abstraction that allows back-end teams to modernize their systems while upholding their GraphQL contract to their clients, thereby minimizing the impact of their modernization initiatives.
Another up-and-coming pattern with GraphQL has been Apollo Federation, where separate GraphQL microservices can be built in isolation yet defined in a way that they can be combined within a GraphQL gateway to deliver a unified "supergraph" of a business's domain. Data federation, another emerging pattern, extends this concept by creating a unified supergraph across multiple data sources that are fronted by GraphQL, giving developers the ability to join data across multiple data sources as if they were in one database.
Netflix is taking advantage of GraphQL in more ways than one. Tejas Shikhare, a senior software engineer for the company, was kind enough to share how his team is leveraging a federated GraphQL platform to "power the API layer using Apollo's Federation Spec. Currently, it composes over 100 different subgraphs into a single unified schema for the entire Netflix studio ecosystem. Netflix is heavily invested in the microservice architecture, and federation was a great fit for unifying the disparate microservice APIs."
You can learn more about Tejas Shikhare's work and Netflix's custom federation implementation by watching a GraphQL FM episode, where he was a featured guest.
[ For more on microservices, you might also enjoy "An Incremental Path to Microservices." ]
Apollo Federation was the successor of a separate architectural pattern Apollo popularized called schema stitching. Like Apollo Federation, schema stitching takes two or more GraphQL schemas and combines them to create one unified gateway schema to query against. In both schema stitching and federation, requests are delegated to underlying GraphQL subgraphs (or services) that process the request for the portion of the graph they have responsibility for.
Arguably the biggest challenge with schema stitching that drove the creation of federation was the need to author the stitching code that creates the unified graph. With Apollo Federation, we now have a language-agnostic, standards-based method to declaratively define how separate subgraphs relate to each other. It is these declarative relationships between subgraphs that Apollo Federation uses to build a unified, federated graph without the need for custom stitching code.
As more and more organizations build out GraphQL microservices to support business domains and empower their user experiences, leveraging federation concepts to combine separate GraphQL endpoints together into one endpoint has simplified architectures and helped accelerate GraphQL adoption, particularly in enterprise organizations.
REST or GraphQL for API design—which is best for my architecture?
The simple way to answer this question is to figure out how you'd like to shape the developer experience for your developer teams to successfully fulfill what they're trying to achieve. If the flexibility of building a mobile or web app that pulls data from multiple pages while keeping bandwidth down meets the needs of your architecture, GraphQL might be the better option. But if you want a lot of data from one page or source and aren't too concerned with overfetching, then maybe REST APIs are best for you. Either direction will have its own set of pros and cons to consider.
While GraphQL is a very powerful and exciting technology for organizations to consider, adoption is not without its challenges. How to handle errors, caching, pagination, authentication, and authorization are just a few cross-cutting concerns where organizations will face challenges on their GraphQL journey. While these challenge areas are not unique to GraphQL, they can sometimes feel more challenging with GraphQL than with REST.
REST, on the other hand, has been around longer. GraphQL combined with REST's comparative structural rigidity can help organizations more clearly understand how to build an architectural and development strategy to leverage its strengths and minimize its weaknesses. It is also possible to use both GraphQL and REST to complement each other's strengths. For instance, Trello, a web-based Kanban-style list-making application, uses GraphQL to translate its queries into REST API requests by integrating the benefits of a "real" GraphQL server to "generate a single REST URL with all the required query parameters." It achieves this from field narrowing and nested resource expansion, which is similar to a GraphQL query. Additionally, open source service Hasura provides an instant GraphQL API on data sources. It recently introduced support for REST with Hasura 2.0, which allows users to create idiomatic REST endpoints based on GraphQL templates.
So, is GraphQL the new REST in API design? Soon, it could very well be. But again, there's no one-size-fits-all when it comes to finding the perfect API design for your architecture. What is the API design of your choice and how do you guide the team toward adopting it? Let us know.