GQL2TS 1.0.0 and Beyond

Brett Jurgens
4 min readApr 17, 2017

gql2ts is a tool that is used to generate TypeScript Interfaces from a GraphQL Schema.

Last Friday, a massive PR was merged into gql2ts that makes it infinitely more extensible and useful.

All of the packages referenced below have been published on npm with the tag next. It will be published as latest after some more testing this week.

Here’s a synopsis of the changes:

1. Using a Query to Generate Interfaces

The initial goal of gql2ts was to give insight into responses from a GraphQL API. It was simple: take a schema, generate a namespace, gain more confidence. But, it was too general and included too much information. For instance, if the schema looked like:

type Query {
customer: Customer;
}
type Customer {
id: ID!;
name: String!;
occupation: String;
}

then the interfaces would look like:

namespace GraphQL {
interface IQuery {
__typename: string;
customer: ICustomer;
}
interface ICustomer {
__typename: string;
id: string;
name: string;
occupation: string | null;
}
}

But if the query looked like:

{
customer {
id
}
}

Then the interface would have three extraneous fields (__typename, name, occupation) that were not actually present in the response.

Additionally, if the query had an alias, then the interfaces would be invalid, for instance:

query CustomerWithJob {
customer {
id
job: occupation
}
}

The interfaces are, once again, incorrect. occupation will still be on the ICustomer interface, but it should actually be job.

This use-case encompasses most of the perceived utility of the library and not having it covered was a sore spot. In v1.0.0 this will change. There is now an option to include a file with a query and have it generate the interfaces based on that. (The old use case is untouched, just don’t provide a query!)

So for the above query, the generated interface will look something like:

interface CustomerWithJob {
customer: SelectionOnCustomer;
}
interface SelectionOnCustomer {
id: string;
job: string;
}

You may have noticed the SelectionOnCustomer interface, I’ll get to that next.

Sub Selections as Separate Interfaces

A problem with other tools that have a similar function is that they wind up generating code that looks like:

interface Customer {
employer: {
name: string;
employees: Array<{
id: string;
name: string;
}>;
}
}

At some point it gets to be quite hard to read. Also, since there’s no good way to get a “subtype” from a TypeScript interface, it makes writing actual code a bit difficult. Consider the following code:

const customer: Customer = getCustomer();
const formatEmployer = employer => { }
return formatEmployer(customer.employer)

What type can you use for employer? You’ll either have to write your own interface that can become inconsistent with your query or use the any type.

This is why gql2ts will, by default, return an interface like:

interface Customer {
employer: SelectionOnEmployer;
}
interface SelectionOnEmployer {
name: string;
employees: Array<SelectionOnEmployees>;
}
interface SelectionOnEmployees {
id: string;
name: string;
}

Now we can easily add type-checking to the previous code example:

const customer: Customer = getCustomer();
const formatEmployer = (employer: SelectionOnEmployer) => { }
return formatEmployer(customer.employer);

This also has a built-in caching mechanism, so duplicate names are not generated (e.g. two interfaces named SelectionOnCustomer). If the name has been used before and it is requesting different fields, a “high-tech counter” will append a sequential number to the end of the name. 😉

If, for some reason, the subtype feature is undesirable, it can be switched off by providing an alternate generateSubTypeInterfaceName function which returns null (or, if not type-checking, any falsey value).

2. Programmatic Usage

gql2ts was originally designed as a simple CLI. However, in v1.0.0 it provides first-class support for programmatic usage. Installing the packages @gql2ts/from-schema or @gqlts/from-query will give you access to the same functions that the CLI uses. This is extremely useful if you generate your type definitions as a part of a grunt/gulp/brunch/etc. task or a webpack loader. In fact, at Avant, we use the @gql2ts/from-query package in this exact way. Our script reads query.graphql files and generates complimentary query.graphql.d.ts files with the type definitions.

3. Ability to Override Default Formatters

Not everyone’s code style is the same, so hardcoding default formatters can be harmful to other users. gql2ts now provides a way to pass overrides for defaults. You simply just need to make a file that provides these overrides and make a default export (or module.exports = { default: { ...overrides } }). Additionally these overrides can be published on npm and reused in other projects.

4. Language Support

gql2ts was obviously created to support TypeScript. However, TypeScript is not the only strongly-typed language that people use. Because default formatters can be overridden, this makes supporting different targets incredibly simple. For instance, @gql2ts/language-flow is an example of targeting flow instead of TypeScript. Docs for developing new languages are forthcoming, but until then, the @gql2ts/types package has the type that is expected from a language.

5. Lerna

gql2ts has made the move to using lerna to manage its repo. This allows extremely easy development with the new modular components.

6. Rewritten in TypeScript

It’s kind of funny that a tool developed for TypeScript wasn’t written in it, but it started as a quick (100 or so line) hack. Naturally, Javascript was the language of choice.

7. Drop Support for node 4/5

In order to use Map/Set without additional polyfills, node 4/5 had to be dropped. Previous polyfills were also removed. Node 8 is set to be released this month, so 6/7/8 should be a reasonable requirement.

I’m quite excited about this release and the potential future that this more-extensible and featured version of gql2ts has. We’ve been using the alpha versions at Avant and it has definitely improved the quality of our typings.

--

--