Skip to main content

GraphQL for the Win

by John Zaccone
January 03, 2017

GraphQL is a powerful query language for APIs that gives the clients the power to ask for exactly what they need and nothing more. GraphQL makes it easier to evolve APIs without the need for introducing new versions.

In GraphQL, every request is a POST request to a single endpoint: /graphql. The request consists of a mirror of the data object that contains the fields you want to see in the response.

For example, this request:

{
  hero {
    name
  }
}

returns the data:

{
  "data": {
    "hero": {
      "name": "R2-D2"
    }
  }
}

Allowing the client to specify which fields to see in the response makes it easier to solve for problems such as resource granularity.

Resource Granularity

Suppose we had an insurance policy entity that contained a lot of nested data. A policy contains basic information such as an ID, policy holder, and a date range, but it also includes much more data such as a list of reasons for valid claims.

{
  id: 12345,
  policyHolder: "John Zaccone",
  startDate: "2016-12-20"
  endDate: "2017-12-20",
  claimReasons: [
    {
       title: "reason 1",
       description: "the description for reason 1"
    },
    ...
  ]
}

If you are only building a policy summary page, you don't need or want all of the claim reasons in the response.

One approach might be to build two endpoints:

/policies/{id}
/policies/{id}/details

Deciding appropriate resource granularity is a common problem when building RESTful APIs. I have personally stressed over this problem for many hours trying to build the best, most user-friendly APIs. But stress no more! GraphQL solves this problem simply by allowing the user to query for exactly the data they are looking for.

Simply build the request and list the fields that you want back in the response:

{
  policy(id: "1") {
    id
    policyHolder
    startDate
    endDate
    # We can leave out claimReasons if we want.
    claimReasons {
       title
       description
    }
  }
}

In this request, the client can choose whether or not they want claimReasons to be in the response.

Versioning

Versioning APIs is hard. It is so hard, that the best approach may be to simply avoid it. That's one advantage of using GraphQL.

When there is limited control on the data being returned from an API endpoint, any change can break existing clients. With GraphQL, logic about what data to retrieve lives in the client, so it is much easier to evolve APIs without introducing a new version.

Arguments and Other Features

GraphQL is littered with a bunch of really helpful features that makes the query language even more powerful. Learn about all of them in the docs.

In REST, arguments can be sent through as query string parameters or as URL segments. Such as:

policies/5
policies?policyHolder="Zaccone"

This is limited in the number of arguments that can be passed in. In GraphQL, every field in the data model can be given an argument. You can also use arguments to tell the server to perform data manipulation such as converting a price to the correct currency.

{
  policy {
    id
    price(currency: "USD")
    policyHolder(name: "John Zaccone")
    startDate
    endDate
    claimReasons {
       title(title: "reason 1")
       description
    }
  }
}

These arguments are hardcoded into the query request, but you can use variables to create reusable queries with dynamic inputs.

For example:

query PolicyQuery($policyHolder: String) {
  policy {
    id
    price(unit: "USD")
    policyHolder(name: $policyHolder)
    startDate
    endDate
    claimReasons {
       title(title: "reason 1")
       description
    }
  }
}

{
  policyHolder: "Zaccone"
}

This allows us to pass in the policy holder name dynamically instead of performing string interpolation as we build each query.

Another cool feature is directives. We can use the @include(if: Boolean) directive to dynamically include or exclude the claimReasons in the response.

query PolicyQuery($policyHolder: String, $withClaimReasons: Boolean!) {
  policy {
    id
    price(unit: "USD")
    policyHolder(name: $policyHolder)
    startDate
    endDate
    claimReasons @include(if: $withClaimReasons){
       title(title: "reason 1")
       description
    }
  }
}

Again, this feature allows us to avoid doing manual string interpolation to build dynamic queries ourselves.

Mutating Data

To mutate data, use mutation methods.

mutation CreateInsurancePolicy($policy: Policy!) {
  createPolicy(policy: $policy) {
    id
    policyHolder
  }
}

The id and policyHolder will return for the newly created policy. It is best practice to only manipulate data explicitly using a mutation method.

Summary

In summary, GraphQL is great when building APIs because it helps:

  • Eliminate multiple URL requests
  • Save time on API design by avoiding decisions such as resource granularity and data format
  • Evolve APIs without managing multiple versions
  • Keep clients clean by allowing them to retrieve the data they want

There are a number of GraphQL libraries for both client and server including java, javascript, swift, node, python, scala and more.

This is just the tip of the iceberg for the capabilities of GraphQL, but hopefully it piqued your interest enough to look further into their documentation. Personally, I am convinced because this tool solves a lot of the headaches I have faced in the past when designing APIs.

Tags:

API
Post by John Zaccone
January 03, 2017

Comments