Product Blog

iOS at Quartz: Putting REST to rest with GraphQL

The above tweet is a joke, but it’s mostly correct; for app developers, JSON is ubiquitous. Nearly every app with dynamic content is an interface built on top of a JSON API.

Until recently, that was true of the Quartz app. With version 2.1.0 of our app, we transitioned to our GraphQL API for fetching content. GraphQL solves a whole host of problems that used to happen when moving data from our servers to the device in your pocket. This transition is already paying dividends for us, and we think it’s the smart choice for any organization building apps on top of APIs.

Let’s walk through an API example to illustrate how migrating to GraphQL helps us. Our server might represent a Quartz member in JSON as:

{
  "id": 359651,
  "firstName": "Hal",
  "lastName": "Lee"
}

There’s plenty to like about JSON. The example above is simple and readable. It’s hard to imagine what could go wrong here, but let’s pose some questions that this JSON example can’t answer:

  • Does every member have a lastName?
  • Is id always a number?
  • Are there member properties available that are missing from this example?

This example JSON response can’t answer these questions. Further, no matter how many member responses from the API we look at, we still couldn’t answer these questions with certainty. Even if all our current members have a last name, a future member response might be missing one.
Knowing the answers to these sorts of questions is especially important when we’re consuming our API with a strongly-typed language like Swift. We might define a Swift representation of a member based on that example API response. For example:

struct QuartzMember {
    var id: Int
    var firstName: String
    var lastName: String
}

We need this Swift model to parse data from our server, but this also sets up a hard expectation of what a QuartzMember type looks like. A future request to our JSON API might return slightly different looking data, like:

{
  "id": "dXNlcjozNTk2NTE=",
  "firstName": "Jane"
}

This member’s id isn’t a number, it’s a string, and they have no last name. Although the server has returned this as a valid member, if we tried to parse this JSON into a QuartzMember, Swift would throw an error. Our app expects a member to look a certain way. Our JSON API server has a slightly different idea of what a member looks like. When those expectations don’t line up, our app can’t turn the JSON into rectangles. Not good!

Ideally, our app developers would communicate with our server developers to ensure that our app and our server share the same definition of a QuartzMember. Mistakes inevitably happen, though, and we might change our server implementation and forget to update our app’s implementation.

It would be better if our system prevented these types of miscommunication by design. GraphQL does exactly that by introducing something called a schema. A GraphQL schema, provided by every GraphQL server, is a strongly-typed representation of all the data a server can possibly provide. Using the schema, our app can answer the questions that JSON can’t answer. Going back to our QuartzMember example, our GraphQL server might define a QuartzMember in its schema as:

type QuartzMember {
  id: ID!
  firstName: String!
  lastName: String
}

This looks really similar to the structure of the first JSON sample, but with some critical extra information. Note the exclamation marks — in GraphQL, those denote required properties. Since lastName lacks one, we know that not every Member will have a last name. On the other hand, every member must have an id and a firstName by definition.

Taking this a step further, we could use the schema to generate a more accurate Swift QuartzMember type. If we matched our Swift type exactly to the GraphQL schema, we could be sure that our app could always parse any given QuartzMember returned by the server. Even better: there’s enough information in the GraphQL schema to automatically generate these Swift types, without any manual translation work on the part of our app developers. We use Apollo iOS to generate native Swift types for every possible data type our GraphQL server returns. This saves us a lot of work — we no longer have to write boilerplate Swift code for parsing JSON responses — and also makes our app less prone to data-parsing errors. We’ve created a type-safe bridge from the content in our databases to the app on your phone.

Despite the ubiquity of JSON, at Quartz we think strongly-typed APIs like GraphQL are far better suited as the back-end for an app. If you’re tired of turning JSON into rectangles, and you’d like to try turning GraphQL into rectangles instead, get in touch!