March 2, 2016

Will GraphQL replace REST documentation?

Sashko Stubailo

Sashko Stubailo

In this post, I start investigating what it’s like to put GraphQL on top of REST endpoints. I’ll go over:

  1. The benefits of GraphQL schemas over documented REST endpoints
  2. A simple example of converting the Star Wars API REST schema to GraphQL
  3. Next steps — applying this concept to a more serious API

The conclusion is that a GraphQL schema provides a lot more useful information than even the most extensive REST documentation, and allows you to have tools to traverse your data in very natural ways.

GraphQL: a type system with a query language

While a REST server is mostly about a list of endpoints, a GraphQL server is more like a type system — you define a series of types (User, Post, Comment, etc.) and connections between them. Then GraphQL itself is just a simple language to traverse the relationships between those types and express which fields you want to read on each object.

This makes GraphQL inherently self-documenting. Every possible query, object, and field comes with a name, description, and type information that can be queried from the server in a standard way. It’s even part of the GraphQL spec, so any spec-compliant server needs to provide this information.

This powers awesome developer experiences like GraphiQL, an auto-completing query UI:

You can also do things like validate your queries before you send them to the server and get type information on fetched data.

What about well-documented REST?

Documenting and even self-documenting APIs are nothing new. In fact, there are whole companies built around API documentation and discovery.

But the main thing missing from almost every well-documented REST API is the ability to easily traverse from one API endpoint to another. Since there has never really been an easy way to call multiple REST endpoints based on relationships between objects, these documentation services don’t have a need to include that information. Most REST documentation sites treat each endpoint as a standalone entry in a list instead of a node in a graph of objects.

That got me wondering — could one leverage already existing information and add the missing relationships to query such APIs with GraphQL?

The Star Wars API

For the first stab at this question, I reached for the venerable Star Wars API (SWAPI), the source of data for basically all GraphQL examples. I thought, why not make another one! I went searching for a schema description.

Luckily, SWAPI has endpoints for getting basic schema information. Here’s a sample of what you get:

// http://swapi.co/api/people/schema

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "The name of this person."
    },
    "birth_year": {
      "type": "string",
      "description": "The birth year of this person. BBY (Before the Battle of Yavin) or ABY (After the Battle of Yavin)."
    },
    "vehicles": {
      "type": "array",
      "description": "An array of vehicle resources that this person has piloted"
    },
    ...
  },
  "$schema": "http://json-schema.org/draft-04/schema",
  "required": [
    "name",
    "height",
    ...
  ],
  "title": "People",
  "description": "A person within the Star Wars universe"
}

This is a great start! It’s always helpful to have a list of properties, their types, and some descriptions. Now we just have to transform this into a GraphQL schema.

Immediately we run into an awkward situation: Relationships are simply documented as type “array”. This is technically true, as they are arrays of URLs, and for a simple REST client that doesn’t need to be able to understand the data, that might be enough. But for GraphQL we want to be able to traverse those relationships.

It’s true that the lack of type information for relationships is partially a side-effect of the simple schema format chosen by SWAPI (or, more accurately, chosen by Django, their web framework), but most of the REST schemas I found on the web, in Swagger, OpenAPI, JSONSchema, or other formats, were missing some of this crucial information; and even when they provided information about relationships it wasn’t always clear how to fetch the related objects.

Making it work

Fortunately, I only had to apply a few “hacks” to transform this schema into a GraphQL-JS schema I could query:

  1. Convert every type into the appropriate GraphQL type — for example, “string” became “GraphQLString”.
  2. Convert the references into the correct type, for example, the “array” type on the “pilots” field for vehicles became type “GraphQLList(Person)”. This mostly had to be done by hand since the information was not available in the JSON schema.
  3. Unwrap paginated endpoints. The paginated root endpoints return a wrapper with some metadata, and in the super simple API I was creating I didn’t want to bother with this. A better version of this wrapper would have information about total counts, and the ability to fetch multiple pages at once. (It turns out that pagination is a relatively unsolved problem in REST, with no concrete standardized pattern. Hopefully GraphQL will eventually grow a standard for this, with some good contenders already.)

And that was it! With some metadata I fetched from a server and some somewhat sketchy loops, I had a working GraphQL schema that was infinitely more productive to query than the REST API itself. I was exploring different characters, vehicles, and more in no time, and with some simple caching to boot.

Check out the code — PRs and issues welcome to improve the example: apollostack/swapi-rest-graphqlswapi-rest-graphql – Query the Star Wars API with GraphQL, using the JSON Schemagithub.com

Next up: a serious API

I found that trying to model other APIs in GraphQL was very informative, even in this simple example. I want to do more of these and explore the tradeoffs between the simple endpoint-based REST model and GraphQL. I’m going to try to tackle something that has many more real-world concerns with pagination, nested responses, and years of features built up over time.

Next week, check in to see how the squeaky-clean type system of GraphQL holds up to the 600-endpoint REST API… of the open source Discourse forum software!

Written by

Sashko Stubailo

Sashko Stubailo

Read more by Sashko Stubailo