Drew PowersEngineering @ Manifold

GraphQL, pioneered at Facebook, has been improving web and native apps since being introduced to the public in 2015. Adoption has been very rapid because it has several benefits over traditional REST APIs.

Advantage 1: Less data

Most endpoint data you consume gets served from a database somewhere. If you’ve worked with SQL databases directly, you know that it’s more efficient to request the fields you need rather than all fields.

In a REST setup, it’s rarely possible to specify only the fields you need. As a result, you typically get “the firehose” and way more data than you need. Need to request multiple records at once? Now you’ve got an exponential problem on your hands.

At Manifold, we were struggling with all the data our REST endpoint was returning.

REST

1https://api.catalog.manifold.co/v1/products

This URL returns 141 KB of data in JSON format, and that’s not even our heaviest endpoint. (Our heaviest REST endpoint returned over half a megabyte of JSON!) For this endpoint, this data gets loaded onto the client any time we need any product data. And based on the way REST works, this was only one of several calls we needed to render a page.

Compare that to our GraphQL endpoint:

GraphQL

If we only need three fields, our GraphQL endpoint only returns what we need. And it clocks in at 9.4 KB, 95% smaller.

Furthermore, there were many times where we’d have to retrieve a product’s plan data with subsequent REST requests (we’ll talk about chaining next). For every product we query, we also sometimes needed to query its plans. Here’s an example of how the total weight of those multiple queries could get combined into one GraphQL query (along with some other scenarios for comparison):

ScenarioREST weightGraphQL weightReduction
All Products + All Plans562.4 KB15.4 KB97%
All Products141 KB9.4 KB95%
Single Product4.1 KB0.2 KB95%

And to think: this savings is just for one user. Imagine what that does at scale—both to improve your users’ load times as well as your bandwidth costs.

Advantage 2: Combining requests

Another considerable advantage of GraphQL is the ability to combine multiple requests. If you’ve ever worked with REST before, you may be used to the “chain of pain:”

  1. We need to display a team. Let’s call the /team/:id endpoint.
  2. The team has a list of userIDs. We’ll need to call the /user/:id endpoint for each of those IDs, one per team user.
  3. We also want to show how many other teams that user is on. Now we call the /team/:id endpoint again, once per team, per user.

Let’s total that up:

1/team/:id 1
2/user/:id n (n: number of users on a team)
3/team/:id n × t (t: number of teams per user)

If this was a team of 20 users, and each user is on 3 teams, then we’ve just pinged 81 REST endpoints to render a single view.

Granted, you could find ways to optimize this REST chain, but you’d have to do so manually and possibly for this single scenario. These optimizations don’t come for free in REST.

Fortunately, they do in GraphQL. Here’s how you could combine all those REST requests into a single GraphQL query:

1query TEAM_USERS {
2 team(id: $teamID) {
3 users {
4 edges {
5 node {
6 avatarURL
7 displayName
8 teams {
9 edges {
10 node {
11 displayName
12 }
13 }
14 }
15 }
16 }
17 }
18 }
19}

In GraphQL you could accomplish the same in a single request. Using GraphQL not only improves performance but also reduces code complexity in your front-end app by only having to manage one call instead of three different queries recursively.

A note on query complexity

Since GraphQL can freely associate models and you can query one thing from another, there is a thing called “complexity” as it relates to your GraphQL server. That complexity can affect performance. Similar to how table JOINs in a SQL query aren’t “free,” neither are GraphQL resolvers. When we fetch a remote model via nesting in GraphQL, we call it “resolving.”

In our imaginary user/team scenario, for example, you may notice that querying teams from users or users from teams are both snappy. But if you kept on chaining users–teams–users–teams … over and over again, you’d notice latency at a certain point. How much latency—and at what level it becomes apparent—will differ based on your specific GraphQL server, middleware, and database architecture. But no matter what your setup, it’s a good idea to limit query depth at some level that will restrict expensive queries while still giving you the flexibility you need for your application (e.g., ten levels deep).

Advantage 3: Subscriptions

The last benefit of GraphQL is subscriptions—the ability to make a query or mutation and automatically get updates. Usually, this gets implemented on the GraphQL server-side using WebSockets.

Let’s say we wanted to create a chat app using GraphQL. We may do something like the following:

1subscription MESSAGES() {
2 messagesSubscribe(last: 200) {
3 edges {
4 node {
5 text
6
7 author {
8 avatarURL
9 userName
10 }
11 }
12 }
13 }
14}

In our app, messagesSubscribe.edges would be an array of messages that auto-updates every time a new message is sent. Without this, we’d have to send a request every second (or more), resulting in hundreds of calls in a short time.

With subscriptions, the only data transferred once the connection is open is when messages are sent or received (and possibly a trickle from the connection itself being open).

To take full advantage of subscriptions, you have to use a client that supports it, but most popular clients like Apollo Client have this built right in.

For a fun example of what GraphQL subscriptions can do, check out Eve Porcello’s talk at React Rally 2018, where five attendees created improv music using subscriptions. Check it out starting around 22:45 in (her explanation comes toward the end of the demonstration):

Does REST have any advantages?

We’ve talked up GraphQL so much here. Does REST retain any advantages? In fairness, GraphQL was built upon the foundation of REST, so its design is as an iteration forward. REST has been one of the most influential, foundational building blocks of the modern web, and GraphQL wouldn’t exist without it. So acknowledging REST’s influences, it’s fair to say that GraphQL is a marked improvement over REST in almost every way.

For us at Manifold, we didn’t find any features lacking in GraphQL. Our most significant pain point in migrating was a problem common among any newer piece of technology: the tooling is still maturing. Not every language you’re trying to build a GraphQL server in will have all the features you need. For us, running a Go microservices stack, many of the Go options lacked some of the features of the more popular, Node.js-based Apollo Server (but we still wanted to use Go for several reasons, including performance).

We eventually landed on 99designs/gqlgen to power our GraphQL services, with a bit of special tooling on top to make pagination and database optimizations easier.

We’re confident the GraphQL community will catch up, but in 2019 you won’t have the same variety and maturity with GraphQL clients and tooling as you do with REST.


Ready to create your own marketplace experience?


Conclusion

These are our top three advantages of using GraphQL in production over REST. What are yours?

If you’re interested in more GraphQL, we recommend the following resources:

  • GraphQL Weekly: a weekly GraphQL newsletter with articles, libraries, and tips
  • GraphQL Summit: the annual conference for GraphQL. Usually in San Francisco
  • Apollo Community: a great place to discuss GraphQL, share knowledge, or ask questions. Runs on Spectrum, a free, open discussion platform made for open source.
Stratus Background
StratusUpdate

Sign up for the Stratus Update newsletter

With our monthly newsletter, we’ll keep you up to date with a curated selection of the latest cloud services, projects and best practices.
Click here to read the latest issue.