Andrew
Web development enthusiast with a keen interest in anything frontend.
In this post we learn how to create TypeScript typings from GraphQL schemas and then how to use that in our pages. Gone are the days of creating interfaces for each query
TypeScript makes a massive difference to the modern web development experience and building a website with Gatsby should be no exception. Given that static types are the key to getting the most out of TypeScript and that Gatsby deals with object structures generated from GraphQL queries how do we reconcile the two?
The answer comes from generating TypeScript typings from the GraphQL schema itself.
Previously a basic Gatsby page in TypeScript my have looked like this:
import * as React from 'react'
import Link from 'gatsby-link'
interface NotFoundQuery {
data: {
site: {
siteMetadata: {
title: string
}
}
}
}
const NotFoundPage = (query: NotFoundQuery) => (
<div>
<h1>Page not found</h1>
<p>It looks like the page you are looking for does not exist </p>
<Link to='/'>Return to {query.data.site.siteMetadata.title}</Link>
</div>
)
export const NotFoundQuery = graphql`
query NotFoundQuery {
site {
siteMetadata {
title
}
}
}`
Here we have created an interface specifically for the query we are running - but this duplicates the object schema that is already defined in GraphQL. To automate this we are going to use graphql-code-generator
To install:
npm install --save-dev graphql-code-generator
npm install --save-dev graphql-codegen-typescript-template
The first installs the glient while the second installs typescript specific support.
We now have the gql-gen
cli installed. We can add a script to this to package.json
to streamline the process:
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"graphql-types": "gql-gen --url http://localhost:8000/___graphql --template typescript --out ./src/graphql-types.d.ts",
The above command will connect to your running Gatsby instance's GraphQL server to access the schema and will output the typings in the source directory.
This does mean however that we need to run this while the server is running so make sure that gatsby develop
has been run first. Ensure that you re-run this each time one of your plugin sources has changed object schema.
Our above code can now be implemented as:
import * as React from 'react'
import Link from 'gatsby-link'
import { RootQueryType } from '../graphql-types';
const NotFoundPage = (query: {data: RootQueryType}) => (
<div>
<h1>Page not found</h1>
<p>It looks like the page you are looking for does not exist </p>
<Link to='/'>Return to {query.data.site ? query.data.site.siteMetadata ? query.data.site.siteMetadata.title : '' : ''}</Link>
</div>
)
export const NotFoundQuery = graphql`
query NotFoundQuery {
site {
siteMetadata {
title
}
}
}`
export default NotFoundPage
It is also worth noting that the types cover the entire schema and will not match exactly what has been received in the query response. For this reason most fields are optional meaning that they can be null. This will simply mean that it forces you to null check each field.
This has saved me a lot of time writing additional interfaces and in VS Code helps with the autocompletion over the query objects.
Web development enthusiast with a keen interest in anything frontend.