So we’ve got a REST API. It’s fast, it’s simple to use and everyone understands it. Why even think about migrating to GraphQL?

GraphQL seems to be working out pretty well for them.

“Massive leap forward”, “complete game changer”, “moving 10x faster”… alright, now this is starting to sound interesting. What’s so great about it then?

For me, it comes down to six main reasons.

1) Performance

Let’s say we’re building a clone of Amazon using a React front-end, and need an API to retrieve the details of a product. On our product detail page, the situation is simple — we need all the product data that the API can throw at us.

This isn’t the only place we need to show product information though, and other places don’t need the full amount of data. We also show product information on the search results page, wish list, related products, ‘recommended for you’, on the order confirmation page and so on. Each of these shows similar but different amounts of detail, and so needs different data accordingly.

What to do? A single API always sending the full amount of data is going to be bloated and wasteful if the UI only shows a tiny proportion of it, and the problem gets linearly worse when sending lists of products.

On the other hand, if we optimise for performance and make 8 similar but different endpoints it becomes a potential maintenance nightmare. Or we could try to go somewhere in between, finding a delicate balance that tries to limit the damage either way.

It’s a hard problem to solve when designing a REST API, particularly for a public API where you can’t anticipate all the things clients are going to do and you don’t want to limit them. Most often, this means we end up with an “over-fetching” API — endpoints that send way more data than API consumers actually need at any one time.

But at the same time, we’re probably “under-fetching” too. Going back to our product detail page, we also need to show the logged-in username and avatar in the header. Plus we’re going to need to show a list of the products currently in the shopping cart in the sidebar.

These aren’t directly related to any individual product, so it doesn’t make sense to put them under the same REST endpoint. They’re going to have to be their own ones, and now the number of API calls we need to make in order to show the page quickly starts to rise…

GET /api/product-detail/123

GET /api/member-info

GET /api/shopping-cart

And wait — the logged-in member info data we need to display in the header is much less than what we’d need to show on the member detail page. How to deal with that, should it be two separate endpoints? And so the same problem rears its ugly head again, forcing us in another round of difficult decision considerations.

So how would we deal with this in GraphQL?

Can’t get much simpler than that.

GraphQL makes all this easier by delegating control to the front-end and allowing it to request exactly what it needs to display any individual page.

This approach turns out to simultaneously eliminate both over-fetching and under-fetching, making it an easy win for front-end performance. It’s also simpler to both understand and work with, as now we only have to deal with one API call instead of several.

On the server-side, our life just got easier too, as we’ve now got far fewer agonising API design decisions to make up-front. We can just make available anything the client may need, and leave it to them to choose.

2) Maintainability

Now our site is up and running, but a developer on the team has found a problem…

The API has a field called “relatedproducts”, but it should be “related_products“ so it’s consistent with our naming convention. Can I rename it?

With a REST API, it’s very very hard to ever safely remove or change a field once it’s added. If it’s a public API, you have no way of knowing which fields are being used and so any change is potentially a breaking one. Even if it’s an internal API, you’d have to carefully check all front-end platforms to make sure it’s not being used (currently or in the past, if users may still be on old versions).

Worse still, if an API changes it’s not easy to detect at build time for your front-end developers unless they have a comprehensive end-to-end test suite in place. They may only realise once their app starts to blow up in production, pretty much the worst-case scenario for all concerned.

If only they’d used GraphQL…

The difficulty of this means that most of the time removing fields from a REST API is a non-starter. Instead, we release new versions of our APIs periodically with all the hassle that involves (more code and documentation to maintain, more work for the API consumers to stay up to date).

GraphQL makes life much easier by resolving fields individually on the server. This means it’s possible for us to know exactly which fields are being used, how often and when. This valuable insight into real-world use of our API helps us know what’s safe to change and is impossible to achieve with REST.

And when we do change our GraphQL API, consumers can detect it automatically at build time and hopefully prevent errors from ever reaching production at all (more on how that works a bit later!)

If we need to remove a field, we can use the protocol level support for deprecation to minimize risk and surprises. Trying to access a field marked as deprecated will cause a build warning in front-end clients.

3) Simple state management

We all know users love fast and instantly responsive apps, and it’s a priority for our new site. We’re never going to beat Amazon with a slow site, after all. To this end, our UX team has come up with a new requirement:

When a user adds a product to their cart, it should update on screen immediately.

If we want something to be instantly responsive, it can’t be waiting for a network response. So we’re going to have to handle this “optimistically” — update the client-side UI immediately, fire off a network request at the same time and assume it’ll succeed. If it doesn’t succeed, well, we’ll need to handle that somehow too.

How to do this using Redux together with React? First, we’d need to have a local representation of the server-side data, so we’ll have to fetch data from APIs, normalize their structure, dispatch relevant actions and write reducers to update store state accordingly. When the user adds something to their cart, we’re going to need to call an API and update the UI — more action creators, more dispatches and more reducer code to write — at the same time.

At this point, we fight off the urge to go back to jQuery and turn instead to Redux Saga or Redux Thunk to help, but the amount and complexity of the code we’re having to write is spiralling.

Still true!

If we’re using GraphQL together with Apollo (the most common client library for GraphQL), this becomes much easier. So much easier, in fact, that switching from Redux to Apollo often means being able to delete hundreds or thousands(!) of lines of code.

Major League Soccer’s experience with Apollo and GraphQL (https://blog.apollographql.com/reducing-our-redux-code-with-react-apollo-5091b9de9c2a)

There’s a couple of reasons for this:

The simpler approach of Apollo, where components simply declare in a React hook (or HOC) what query data they want, and Apollo takes care of fetching it. The built-in cache of Apollo client, which removes the need to write our own code that provides a local version of the server data model. Keeping both local and remote state in sync becomes nearly trivial, as they’re unified under a single interface. Even building an optimistic UI like we want becomes much easier, as there’s built-in support for this pattern inside Apollo.

We can now bid a not-so-fond farewell to our most of our code dedicated to actions and action creators, reducers, stores, sagas, thunks, Axios/Superagent, data normalization, and normalizr schemas, plus unit tests related to all of the above.

Now if only we could get back all that time spent we debugging them, and trying to make them work well with Typescript…

4) Type safety

And talking of Typescript, its booming popularity is a reflection of how useful type safety is in building complex or large-scale systems, and so of course we want to use it.

No matter how type-safe the rest of our code is though, any calls to a REST API blow a gaping hole in our otherwise type-safe system. There’s no real way to know for sure what data we’re going to get back from a REST API call. All we can do is read the documentation, try making calls to it and see what we get back, and then base our code on what we find out.

It’s not ideal, but it mostly works. At least, it does right now, though if the response changes tomorrow we’d have no idea. We also don’t know which fields sent back in the response are optional, and would be missing if we sent different parameters. Conversely, there may be other fields that are sent in some situations, but not in our testing.

There’s not much more we can do apart from cross our fingers that the docs are detailed and up-to-date (developers love to spend time writing documentation, right?), and hope our “best guess” at what the response is going to be works out OK.

A developer prepares to deploy a REST API integration into production.

Not surprisingly, this fragile situation is a common source of real-world bugs for actively developed APIs. It’s only mitigated by the dubious benefit of REST APIs being so hard to safely change that they’re often just left alone until a new version comes out.

Not being fully confident in the response we’re going to get hinders our front-end team’s ability to move fast. We may end up adding complexity with redundant checks, fall back conditions and unit tests if we’re not sure in the type or “nullability” of a field, or spend time writing, running and maintaining lots of Postman API collections and end-to-end tests to try and validate the API response is still what we expect.

A GraphQL server solves this problem by defining a schema — a list of all fields available, their data type and whether they can possibly be null or not. Unlike documentation, the data we get from looking at the schema is guaranteed to always be up-to-date and complete.

It’s a real game-changer, as now we no longer have to “guess” at the response. From the front-end, we know what fields we’re going to get, we know what types they are and so much of the effort we’d previously have to put in to validate the API response is now no longer needed.

We finally get to be confident in our Typescript interfaces accurately reflecting what’s on the server, and can close the loop on type safety in our site.

5) Code Generation

We’re still having to do more work than we’d like though. Both our GraphQL schema and our Typescript interfaces are essentially lists of the same field names and their types, albeit defined using a different syntax. It feels like duplicated effort to have to maintain both of them.

Fortunately, we’re not the first ones to think of this, as Apollo comes to the rescue once again. Provide the Apollo tooling CLI with a GraphQL schema and it can generate the needed Typescript interfaces for us, along with options for Flow, Swift (iOS) and Scala too. We can then integrate this as part of our build, ensuring the schema and code must remain in sync or the build will fail.

Apollo’s CLI feature is still a bit lacking in features and customization for now, but the amazing GraphQL Code Generator open-source project picks up where Apollo left off. It has extensive customizable support for Typescript, plus makes Android developers happy by adding support for both Kotlin and Java.

It further has specific support for both React and Angular, including generating custom strongly-typed React hooks and Angular services for every GraphQL query and mutation in our project. Importing these into our code gives us effortless full type-safety at the API level, something we could only dream about achieving with our REST API.

6) Tooling & Testability

So far, so compelling, but how do we actually work with and test GraphQL in development? Our developers are used to how Postman and Insomnia work, is there anything similar?

It turns out that we needn’t have worried about this. Both Postman and Insomnia also support GraphQL, and there’s also popular open-source projects GraphQL Playground and GraphiQL that provide similar capabilities. Most of them work as both web and desktop apps.

All the GraphQL tools can go much further than their REST equivalents too, thanks to the information the schema provides. This lets them provide context-aware autocomplete, linting, and can highlight specific errors where your query is invalid.

And once we’ve perfected our query, we can copy it query directly from the tool into code, and vice-versa for debugging purposes. If we want to change it further once we’re in our code editor, the VSCode Apollo plugin has similar autocomplete and error highlighting capabilities, automatically setup after it’s fed the link to the schema.

The benefits the schema brings doesn’t stop there either. As we always know the type of data it will return, it’s simple to mock GraphQL queries in unit tests with a minimum of code. For integration and acceptance tests, we can use GraphQL Faker which takes an existing schema and allows modifying it, useful for when our tests benefit from a mixture of real and mock data.

It’s also of huge benefit when front-end and back-end teams are working in parallel on the same feature. As long as the schema change has been agreed upon, the front-end team can simply use Faker to extend the schema with the proposed new changes and return mock data until the back-end is ready. Once they are, integrating their changes is as quick and simple as removing the mock.