GraphQL & TypeScript

Brett Jurgens bio photo Eight days ago by Brett Jurgens.

At Avant, we love GraphQL. We also love TypeScript. Although both of them are strongly typed, there’s not much crossover between the two and you wind up having to define your types twice.

We decided to fix that.

Introducing gql2ts.

gql2ts is a command line tool that takes a GraphQL schema (obtained using the introspection query) and outputs a file full of TypeScript interfaces.

It’s run simply by passing in a schema.

$ gql2ts schema.json

Where schema.json is a GraphQL schema, that looks like:

{
  "data": {
    "__schema": {
      "queryType": {
        "name": "Query"
      },
      "mutationType": {
        "name": "Mutation"
      },
      "subscriptionType": null,
      "types": [
        {
          "kind": "OBJECT",
          "name": "Query",
          "description": "The query root for this schema",
          "fields": [
            {
              "name": "customer",
              "description": "Find a Customer by ID",
              "args": [
                {
                  "name": "id",
                  "description": "Id for record",
                  "type": {
                    "kind": "NON_NULL",
                    "name": "Non-Null",
                    "ofType": {
                      "kind": "SCALAR",
                      "name": "ID",
                      "ofType": null
                    }
                  },
                  "defaultValue": null
                }
              ],
              "type": {
                "kind": "OBJECT",
                "name": "Customer",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "Customer",
          "description": "A customer of our company",
          "fields": [
            {
              "name": "id",
              "description": "The customer's id",
              "args": [],
              "type": {
                "kind": "NON_NULL",
                "name": "Non-Null",
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "uuid",
              "description": "The customer's uuid",
              "args": [],
              "type": {
                "kind": "NON_NULL",
                "name": "Non-Null",
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "name",
              "description": "The customer's name",
              "args": [],
              "type": {
                "kind": "NON_NULL",
                "name": "Non-Null",
                "ofType": {
                  "kind": "SCALAR",
                  "name": "String",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "SCALAR",
          "name": "ID",
          "description": null,
          "fields": null,
          "inputFields": null,
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "SCALAR",
          "name": "String",
          "description": null,
          "fields": null,
          "inputFields": null,
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "SCALAR",
          "name": "Int",
          "description": null,
          "fields": null,
          "inputFields": null,
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "SCALAR",
          "name": "Boolean",
          "description": null,
          "fields": null,
          "inputFields": null,
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        }
      ]
    }
  }
}

This will now export a file to graphqlInterfaces.d.ts, which looks like:

// graphql typescript definitions

declare module AvantGQL {
  // description: The query root for this schema
  export interface IQuery {
    customer: ICustomer;
  }

  // description: A customer of our company
  export interface ICustomer {
    id: string;
    uuid: string;
    name: string;
  }
}

export default AvantGQL;

There are some more options which you can see in the readme on github.

This can be useful as part of a build step for your GraphQL server. Perhaps it can run in CI and then the ouput gets pushed to a repository (or npm, bower, etc) which is included in your typings.json. That way your interfaces will always be up-to-date.

There’s still some more work to be done here, but we think this is a good starting point!

We hope to open source more tools we use soon, including more TypeScript & GraphQL integration.

P.S. I’ll be at React-Europe this year; if you’re going let’s get a beer and chat.