What is GraphQL?
Simply put, GraphQL is a query language for APIs and a server-side runtime. It’s been developed by Facebook in 2012. The cool thing about it is that it allows to fulfill queries by using a type system you define for your data.
Why use GraphQL instead of REST API?
Indeed, why? REST is versatile and doing well, so why bother with rewriting your applications or changing the architecture of future projects? Well, like most technologies, REST is not perfect and has some drawbacks. Some of them, solved by GraphQL, are:
- To fetch data, you typically need to access multiple endpoints with fixed data structures. This can result in over- and under-fetching.
- To fulfill a complex query that fetches data according to some relationships, you have to create multiple requests and build the related data into the initial response (or modify URLs).
- Commonly REST endpoints are structured according to the needs of your app. This stifles the possibility of rapidly cycling through different front-end iterations.
- Because of the data structures, it is difficult to know exactly what kind of information each client typically needs.
- REST is a go-to solution for some uses, but in other cases it’s too rigid. Although you can do almost anything using REST API, the effort of implementing some functionalities is not worth it. Why do it if GraphQL is already there?
Using a GraphQL – a real life story
At Objectivity, we had a GitHub API based on REST, but despite all the information we provided through it, we heard from integrators that our API wasn’t very flexible. It sometimes required two or three separate calls to assemble a complete view of a resource. It seemed like our responses simultaneously sent too much data and didn’t include data that consumers needed.
What’s more, we wanted to collect some meta-information about our endpoints. For example, we wanted to identify the OAuth scopes required for each endpoint. We wanted also:
- to be smarter about how our resources were paginated; to assure type-safety for user-supplied parameters;
- to generate documentation from our code;
- to generate clients instead of manually supplying patches to our Octokit suite.
Therefore, we decided to refactor the API to leverage GraphQL. Some of the arguments that spoke for this change, were:
- You let the client decide which data it needs to download. (This type of design makes it possible to use clients where smaller payload sizes are essential. For example, you have an entity that describes an offer visualised on several screens. Depending on a business need, you sometimes need to send just a general section, sometimes a detailed pitch. A mobile app based on GraphQL could simplify its requests by only asking for the data it needs.)
- An insight into the required scopes could ensure that only the appropriate types are being requested.
- You can batch requests, where you define dependencies between two separate queries and fetch data efficiently.
- You can create subscriptions, where your client can receive new data when it becomes available.
- You can defer data, where you choose to mark a part of your response as time-insensitive.
This move to GraphQL marked a larger shift in our platform strategy to be more transparent and flexible. Our application engineers were using the same GraphQL platform that we’re making available to our integrators, which provided us with the opportunity to ship UI features in conjunction with API access.
After this transition, I claim that GraphQL represents a massive leap forward for API development. Type safety, introspection, generated documentation, and predictable responses benefit both the maintainers and consumers of our platform.
GraphQL core concepts
GraphQL has three concepts at its core:
- Queries – a GraphQL query is used to fetch data.
- Mutations – these are used to create/update/delete records.
- Subscriptions – clients can subscribe to an event and this creates a connection between client and server. When that event occurs, the server pushes the data to clients.
Let’s take a closer look at each concept.
GraphQL allows to naturally query nested information.
GraphQL queries with arguments
In general, when a client subscribes to an event, it will initiate and hold a steady connection to the server. Whenever that particular event actually happens, the server pushes the corresponding data to the client.
In this case, whenever a new mutation is performed that creates a new Person, the server sends the information about this person to the client.
GraphQL pros and cons
“Hold your horses” some of you might say “nothing is perfect. At least in the developer’s world.” And you are correct. GraphQL has its sweet-spots, but also some drawbacks.
- The server implements resolvers that fulfill specific graph queries. The resolvers can function like „API gateways” (caching, authentication, autorisation, etc.)
- One of the coolest side effects of using GraphQL is the ability to communicate gateways and services through any protocol (http, http2, grpc, queues).
- The server returns only what the client asked for (no more, no less; no wasted bits over the wire).
- The APIs sitting behind GraphQL do not need to worry about serving every possible shape clients will want.
- The GraphQL layer can specifically optimise queries over the RESTful APIs, minimizing API calls necessary to resolve a query. Yes, you need to put in the work to implement each resolver, but the explicitness is so liberating.
- GraphQL can be used in between the UI and API layers as insulation, reducing concerns on both sides. GraphQL helps both the UI and API layers as a result (facade of microservices).
- It takes a notable calendar time to design the schema thoughtfully such that it will have backwards compatibility as it grows over time.
- There’s no caching because even if the client downloads data, the operation is POST.
- If you change your service, you change your gateway.
- There is no versioning and the schema must be additive only (or with backwards compatible implementation detail changes).
- Breaking changes are possible, but you must phase it: add new schema, roll out compensating changes to ALL clients, remove old schema.
- Features like paging, filtering, and sorting are not built in and require you to invest in defining your patterns and reusable types.
- It works best when there is a single GraphQL server fulfilling the whole schema. That requires a single service that multiple teams can contribute resolvers to.
- GraphQL, along with other strongly-typed modeling systems, doesn’t handle dynamic data very well. Concur supports “Custom Fields”.
Differences between GraphQL and OData
Jesse Ezell mentioned that some of the misconceptions about GraphQL come simply from GraphQL’s name. Developers who have worked with SQL and OData hear “query language” and think “SQL.” As Nick Schrock, co-creator of GraphQL, replied:
Let’s delve into the details.
Capabilities use case
Many use cases don’t require a wide variety of types, so limiting the types supported keeps things simple… Until you’re forced to jump through hoops to support a non-native type.
Allows generic applications to be written by allowing them to:
- Discover Data Available (Catalog Metadata)
- Discover Capabilities and Expected Behavior
Versioning / Schema Evolution
Schemas evolve rapidly in our world of agile development. Our tools must allow for this evolution to occur while avoiding breaking changes from deprecating fields.
Most APIs require the ability to page data to avoid returning millions of rows of data and most applications only have a need of displaying a subset of data at a given time.
Of course, GraphQL is not a miracle cure to all of our problems. It is, however, perfect for some of the above mentioned uses. It excels where e.g. REST requires us to sweat over the keyboard.
Give it a try and tell me what you think.
You can find a presentation with the research summary here.