Over the past 11 years, the software engineering world has been disrupted with the widespread adoption of GraphQL from companies like Github, Shopify, and Yelp. We have large enterprises like Apollo, The Guild, and Wundergraph to host the necessary complexity associated with this revolution in API development.
But I’m skeptical of the hype. How can an API paradigm as established and battle-tested as REST not catch up to the power of GraphQL? And if GraphQL can do everything REST can do, why doesn’t everyone use it?
To preface, when I say “REST,” I mean the API pattern of receiving and delivering JSON via HTTP, with standard request types and response codes. By “GraphQL,” I mean the language and paradigm of sending queries and mutations and receiving JSON payloads. Hey, I’m a poet!
People have been comparing GraphQL and REST for a decade now, so I’ll try to summarize the state of the nation. First, let’s consider the REST API.
REST (Representational State Transfer) APIs are:
- Usually versioned:
/api/v1/orders?page=2or/api/v3/orders?page=2 - Widely supported: REST helpers and functions come built in with most backend languages
- Untyped: the client doesn’t have a validated contract with the server
- Singular: the client must make many requests to different REST endpoints to receive more data
Meanwhile, GraphQL (Graph Query Language) APIs are:
- Unversioned: a GraphQL schema “evolves” with new data getting new fields, and deprecated fields are decorated with
@deprecate - Strict: the client and server have an established contract, and no requests are processed if the contract is broken
- Encapsulated: the client accesses all data through one endpoint, and the data returned is changed based on the query/mutation received
- Composed: the client can request more data from different resolvers to reduce the amount of round trip calls to the API
Please note, this post will stick only to server and client transport, as if I were to examine server-to-server transport, I would also have to review SOAP, gRPC, messaging queues, etc.
So, to answer my original question of why anyone would need GraphQL, you don’t. GraphQL was pitched as necessary complexity to solve the problems of REST, like sunsetting and maintaining many API versions, data overfetching, and a lack of strict contracts. Today, REST API versioning is a developer preference; overfetching usually isn’t the cited reason to choose GraphQL, and it can be solved in REST by having smaller API endpoints; and strict typing/contracts was established with libraries like oRPC, tRPC, and other OpenAPI type/spec generators.
But Apollo GraphQL, The Guild’s Hive, and Wundergraph’s Cosmo are all products which sell the one advantage of GraphQL that nobody can beat: Federation.
GraphQL encourages clients to compose a query from many fields mapped to many sources in one request, but that doesn’t work with a microservice architecture. With distributed systems, each service maintains its own data and API, so clients have to query individual services. You may see where I’m going with this… Querying individual services is no better than just using REST.
Federation was born in the microservice boom as an answer to the question of “How do several services maintain their own GraphQL schemas but still allow clients to query all of them in one request?”
Whether microservices, submodules, monoliths, or others are the right system architecture is a separate topic entirely. Assuming you’d like to embark on the multi-repo, distributed systems route, an established API unification strategy is to create/use a gateway/router service.1 With GraphQL, we can combine an API gateway with a Supergraph—a GraphQL schema composed of many smaller subgraphs—to create a federated gateway.
Image courtesy of supergraph.io.
With Federation, your clients can query many microservices in one request through one encapsulated endpoint. If a resolver needs another microservice’s “external” data to fulfill a client’s request, it can use the @requires field to mark that data as a dependency. (There are several other features depending on the GraphQL provider you choose.). Federation is the primary use case I see for GraphQL today.
There are several drawbacks to GraphQL. Because we’re abstracting away cross-service data fetching onto the backend, it’s moving the complexity down a layer, not reducing it. How can you measure which specific fields (external or internal) are causing a specific query to be slow? How about error responses: How does a partial failure look when querying many services, and how are those partial failures handled downstream? These issues are not exhaustive, and there are many solutions to the problems GraphQL introduces in our APIs.
In summary, people use the REST architecture to build APIs because it’s simple, widely adopted, familiar, and can be typed with OpenAPI + client libraries. People use GraphQL if they prefer schema evolution to API versioning, or if they need a unified, federated API across several microservices. But that’s just my opinion.
-
API gateways also exist for REST APIs ↩