Typesafe GraphQL queries with quicktype

graphql

GraphQL is the hot new API technology that the cool kids are talking about. Here’s how to use quicktype to easily generate typesafe code for GraphQL queries.

If you’d rather jump straight into sample code, please see the GraphQL sample repo.

What is GraphQL and why is it cool?

With traditional APIs, you usually have to make multiple queries to collect the data needed by your app. For example, to get the followers for all of a user’s GitHub repos, you’d first query for a list of the user’s repos, then perform a query for each repo to get the followers. For a user with n repos, that’s n+1 queries, which means your app will be slow and will need a cumbersome UI (e.g. loading spinners, lazy lists).

With GraphQL, on the other hand, you specify exactly what information you want up front, so you get all the data your app needs in a single query.

Let's try it out by writing some queries against the GitHub API! You can run these queries in your browser using GitHub’s GraphiQL explorer. Here's a simple one for starters—it returns the current user's name and login info:

query {
  viewer {
    login
    name
  }
}

Here's the response I get for this query:

{
  "data": {
    "viewer": {
      "login": "schani",
      "name": "Mark Probst"
    }
  }
}

Let's try a more complex query—suppose we want the names of my three most-starred repositories, including the number of stars for each:

query {
  viewer {
    login
    repositories(first: 3, orderBy: { field: STARGAZERS, direction: DESC }) {
      nodes {
        name
        stargazers {
          totalCount
        }
      }
    }
  }
}

The result of this GraphQL query is:

{
  "data": {
    "viewer": {
      "login": "schani",
      "name": "Mark Probst",
      "repositories": {
        "nodes": [
          { "name": "clojurec", "stargazers": { "totalCount": 845 } },
          { "name": "quicktype", "stargazers": { "totalCount": 256 } },
          { "name": "metapixel", "stargazers": { "totalCount": 70 } }
        ]
      }
    }
  }
}

The first thing to notice is that the structure of the JSON we get back depends on the query. Unlike traditional APIs, which return a small, predefined set of types in response to queries, GraphQL APIs can return a huge permutation of types that are unique per query. For this reason, GraphQL API providers cannot provide strongly typed client libraries.

The second thing to notice is that the queries don’t specify types. For example, the last query doesn't specify that totalCount is a number, so how do we know what type to use? The answer lies in the GraphQL schema, which defines the types for all fields in all possible queries. GraphQL servers allow clients to download this schema through an “introspection query”.

Let’s see how quicktype can download a GraphQL schema using an introspection query, then generate types and helper code for our GraphQL queries.

To send an introspection query (or any GraphQL query) to GitHub, you need an access token. Follow these steps to get one and save it in an environment variable:

TOKEN=<your token goes here>

Make sure your token works by running this query on the command line:

curl -H “Authorization: bearer $TOKEN” \
     -X POST -d '{ "query": "query { viewer { login }}" }' \
     https://api.github.com/graphql

You should get back something like

{ "data": { "viewer": { "login": "schani" } } }

Now you can ask quicktype to download the GraphQL schema from the GitHub API server:

quicktype --graphql-server-header “Authorization: bearer $TOKEN” \
          --graphql-introspect https://api.github.com/graphql \
          --graphql-schema github.gqlschema

You should end up with a file named github.gqlschema that's a bit over 1MB. Here’s a simplified excerpt from that file that describes the totalCount field::

{
    "name": "totalCount",
    "description": "Identifies the total count of items in the connection.",
    "type": {
        "kind": "NON_NULL",
        "ofType": {
            "kind": "SCALAR",
            "name": "Int",
        }
    }
}

Now that you have the GraphQL schema, save one of the queries from above to the file query.graphql and tell quicktype to generate types for you:

quicktype --src-lang graphql --lang csharp \
          --graphql-schema github.gqlschema \
          query.graphql

I used the first query, which asked for my login and full name, and these are the C# types quicktype generated (if csharp isn’t your cup of tea, you can substitute elm, swift, java, c++, go, or typescript in the command line above):

public partial class Query
{
	[JsonProperty("viewer")]
	public User Viewer { get; set; }
}

public partial class User
{
	[JsonProperty("login")]
	public string Login { get; set; }

	[JsonProperty("name")]
	public string Name { get; set; }
}

quicktype generated C# classes that perfectly represent the type of the data returned by the query, adding JsonProperty annotations to ensure that the JSON is unmarshalled correctly. It also generated code to convert JSON to and from those nice types.

If you’d like to start using GraphQL right away, please check out our repository with sample code which shows you how to do a complete GraphQL query from start to finish using quicktype in C# and TypeScript.

Mark

Mark