Tooling your typescript create react app to work with GraphQL

Theophilus Omoregbee
codeburst
Published in
7 min readDec 8, 2020

--

I’m going to assume you’ve already bootstrapped your app with the typescript template, create-react-app.

In this article, we are going to learn how to tool your app development process to use GraphQL with a demo weather app by:

  • Using consistent filename to hold our queries, mutations, and fragments.
  • Generating typescript types (interfaces, enums, etc.) from a GraphQL server with graphql-codegen.
  • Generating custom React hooks for each query and mutation using Apollo client.
  • Adding a watch script to run graphql-codegen process on modification of our GraphQL queries.
  • Adding a Graphql explorer to explore server types, queries, and mutations.

Prerequisites

Before we dive in, we need to add a basic GraphQL package which is required by the other dependencies we are going to make use of.

yarn add graphql

Initializing our project with Graphql-codegen

Graphql-codegen is a tool for generating code based on a GraphQL schema and GraphQL operations. Read more here.

We are going to use this to achieve most of the things this post is going to cover. Let’s add graphql-codegen as a dev dependency to our app:

yarn add -D @graphql-codegen/clior npm install -D @graphql-codegen/cli 

We are going to use the above CLI to initialize our project, which uses different questions to set up your project.

yarn graphql-codegen initor npx graphql-codegen init

The above will show up with some questions which I will outline below with their corresponding answers:

? What type of application are you building? Application built with React
? Where is your schema?: http://localhost:4000
? Where are your operations and fragments?: src/**/*/queries.ts
? Pick plugins: TypeScript React Apollo (typed components and HOCs)
? Where to write the output: src/generated/graphql.tsx
? Do you want to generate an introspection file? Yes
? How to name the config file? codegen.yml
? What script in package.json should run the codegen? codegen
  • schema location will be updated to use our graphql server later on.
  • src/**/*/queries.ts is going to hold our queries that will eventually be converted to standard GraphQL AST.
  • We need the introspection file (graphql.schema.json) for setting up our local GraphQL explorer if the GraphQL server doesn’t have one for you to explore queries, mutations, and response types.

After running graphql-codegen init function, you should have ./codegen.yml

overwrite: true
schema: ""
documents: "src/**/*/queries.ts"
generates:
src/generated/graphql.tsx:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-react-apollo"
./graphql.schema.json:
plugins:
- "introspection"

And your package.json scripts section should have:

"codegen": "graphql-codegen --config codegen.yml"

Generating typescript types (interfaces, enums, etc.) from a GraphQL server

We are going to be using https://graphql-weather-api.herokuapp.com/ as our demo GraphQL server. If you need more public GraphQL APIs to play with, you can check the list here.

Open codegen.yml and update the schema field to:

schema: "https://graphql-weather-api.herokuapp.com/

Before we try any of the GraphQL query provided by our server, let’s add a dependency that will help convert our queries to standard GraphQL AST and also generate custom react hooks (next section):

yarn add @apollo/client

Let’s test run our process by creating ./src/components/Weather/queries.ts and adding the below query to retrieve weather report for a city:

import { gql } from '@apollo/client';export const GET_CITY_BY_NAME = gql`
query GetCityByName($name: String!) {
getCityByName(name: $name) {
id
name
country
weather {
summary {
title
description
icon
}
temperature {
actual
feelsLike
min
max
}
}
}
}
`;

Run the code below on your terminal:

yarn codegen

This should add two new files:

  • generated/graphql.tsx which should contain all the schema types from our server.
  • graphql.schema.json which is generated by the introspection plugin we mentioned during initialization.

Generating custom react hooks for each query and mutation using Apollo client

Before we use our GetCityByName, let’s enable custom React hooks for all mutations and queries that support typings for response fields and variables.

We are going to extend our codegen.yml generates configuration to support hooks creation for mutations/queries. Open codegen.yml and update it with:

overwrite: true
schema: "https://graphql-weather-api.herokuapp.com/"
documents: "src/**/*/queries.ts"
generates:
src/generated/graphql.tsx:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-react-apollo"
config:
withHooks: true

./graphql.schema.json:
plugins:
- "introspection"

withHooks set to true so we should be able to re-run yarn codegen to see our custom react hooks added to our generated/graphql.tsx.

In order to start using the generated hooks and types in our app, we need to wrap the root app component in <ApolloProvider> like so:

...
import {
ApolloProvider,
HttpLink,
ApolloClient,
InMemoryCache,
} from "@apollo/client";
const graphQLink = new HttpLink({
uri: "https://graphql-weather-api.herokuapp.com/",
});
const cache = new InMemoryCache();const client = new ApolloClient({
link: graphQLink,
cache,
credentials: "include",
resolvers: {},
});
ReactDOM.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>,
document.getElementById("root")
);
...

This simply inits our app with apollo client and points to our GraphQL server. You can read more about initializing your app with Apollo client here.

We can now move on to using our generated custom react hooks to retrieve weather information. Add a new file to our weather component folder, ../src/components/Weather/index.tsx , with the following codes:

Weather/index.tsx

Observe how, instead of using useQuery which comes with no types, we used useGetCityByNameQuery which is typed with the fields specified on our queries.ts for both variables and response. It also supports the generation of lazy queries too e.g. useGetCityByNameLazyQuery which works the same way a useQueryLazy does. Read more about apollo client Hooks here.

vscode intellisense

Import the Weather component into your App.tsx.

Our app should be up and running when you run this next command:

yarn codegenyarn start
weather app

Adding a watch script to run the graphql-codegen process on modification of our GraphQL queries

From the last step, we have to stop our create React script local dev server to run codegen again before starting the app. We are going to run the process in parallel with the Create React App start script using npm-run-all. Let’s install dependency:

yarn add -D npm-run-all

Open your package.json and add the following scripts:

...
"scripts": {
"start:react": "react-scripts start",
"codegen:watch": "graphql-codegen --config codegen.yml --watch",
"start":"run-p codegen:watch start:react",
"codegen": "graphql-codegen --config codegen.yml",
...
},
...

What we have done is add the --watch flag to graphql-codegen CLI to watch for changes in src/**/*/queries.ts which was specified in our codegen.yml. We moved the oldstart script to start:watch then updated our start with npm-run-all run-p which is to run in parallel codegen in watch mode and start the react app local dev server.

yarn start
watch code changes and react app with yarn start

Adding a Graphql explorer to explore server types, queries, and mutations

Lastly, let’s add a GraphQL explorer to easily see which queries and mutations are available for us to use, and the required parameters or allow fields we can send or retrieve respectively.

We are going to make use of the following dev dependencies which allow us to run a local node server to view our GraphQL explorer:

yarn add -D express express-graphql @graphql-tools/load @graphql-tools/json-file-loader

Add a new file server.jsto your root directory with the code below:

GraphQL explorer server

This server code simply relies on the introspection plugin we added to our codegen.yml; it loads the schema using a JsonFileLoader since we are using ./graphql.schema.json (for more loaders, see here).

The loader should resolve with a schema that is compatible with our express-graphql middleware, and then serve our content with express on route /graphql.

Now, open package.json and add this script to run our server.js:

...
"scripts": {
...
"graphql": "npm run codegen && node server.js"
},
...

What we have done is made sure that we have our introspection file ready by running codegen before running our server. Open your terminal and run:

yarn graphql

Our explorer should be served in http://localhost:4000/graphql:

GraphiQL explorer

I personally add the ./graphql.schema.json to .gitignore since it will always be generated when your app is started.

Conclusion

I hope you have found this article helpful. Please see below for some additional tips, and feel free to leave feedback or questions in the comments section!

If you lint your project with ESLint, there’s a codegen plugin that adds custom code, imports, comments, and more to your output file. We can use it to add /* eslint-disable */ to the top of src/generated/graphql.tsx like so:

yarn add -D @graphql-codegen/add

Open codegen.yml and add the add plugin under our output file plugins:

...
generates:
src/generated/graphql.tsx:
plugins:
- add:
content: "/* eslint-disable */"

- "typescript"
- "typescript-operations"
...

Repo

--

--