Serving the data for your app from GraphQL has so many benefits, a single endpoint to hit, not oversubscribing to data, to name a few. But when you couple this with a Typescript project the markup can get a little confusing and messy.
Having to write and provide types for all parts of your GraphQL queries and mutations can muddy your code. But because the schema is known ahead of time we can generate the Typescript types programmatically. This saves so much time and allows us to quickly write Typed GraphQL, React apps.
There are a number of packages that do just this but my favourite one at the moment is GraphQL code generator. It provides a simple to use CLI tool and also gives you the option to not only generate the types but also typed query and mutation components. These can then be imported. This really cleans up your code, making it much easier to read.
Setting up
To keep this article a reasonable length, I am assuming that you already have a React Typescript UI and a backend serving data over a GraphQL API.
First, we need to install the code generator:
yarn add -D @graphql-codegen/cli
# Once installed
graphql-codegen init
graphql-codegen init
will now run through a load of questions. This process will guide you through setting up your schema, selecting and installing plugins and choosing where to put all those sweet, sweet generated files!
Because we are writing our project in Typescript, make sure you add the necessary Typescript plugins. It will also add a generate
script to your package.json
file so you can easily regenerate your files.
The result of the init function is a config.yml
file that will look something like this:
schema: http://localhost:3000/graphql
generates:
./src/types.d.ts:
plugins:
- typescript
config:
withHooks: true
There is a lot that can be configured but the init function is enough to get you started.
When you are ready run npm run generate
to generate all your types and components. graphql-codegen
will read your queries, mutations and subscriptions and generate the required files for you. You should now have a generated folder in your project. You could name this folder anything you want to but I recommend keeping it as generated. This keeps it clear not to edit files in this folder as any changes will be over-written when you run generate again.
Connecting to your API
I use Apollo to connect to my backend for most of my projects and so we will do the same.
Let's install the packages needed to use Apollo as our GraphQL client
yarn add apollo-boost apollo-cache-inmemory graphql-tag graphql react-apollo apollo-link-http
Now let's edit our index.tsx
file to use Apollo
import ApolloClient from "apollo-boost";
import { InMemoryCache } from "apollo-cache-inmemory";
import React from "react";
import { ApolloProvider } from "react-apollo";
import ReactDOM from "react-dom";
import AppRouter from "./components/Router/Router";
import { HttpLink } from "apollo-link-http";
const link = new HttpLink({ uri: "https://example.com/graphql" });
const cache = new InMemoryCache({});
const client = new ApolloClient({
link,
cache
});
ReactDOM.render(
<ApolloProvider client={client}>
<AppRouter history={history} browser={browser} />
</ApolloProvider>,
document.getElementById("root")
);
With that all set up we can now start accessing our data. graphql-codegen
gives us the open to generate render props, HOC and hooks based code. This depends on how you have it configured. There are examples of render props and hooks below. I personally would choose hooks every day. It is so much neater.
// Render props example
import * as React from "react";
import { IconsLayer, QueryLoading } from ".";
import { GetEventsQueryComponent } from "../generated/apolloComponents";
interface IconsLayerProps {
[...]
}
export const EventsSitesIcons: React.FC<IconsLayerProps> = React.memo(props => {
return (
<GetEventsQueryComponent
variables={{ date: moment().format("YYYY-MM-DD") }}>
{({ data, loading, error }) => {
if (loading) return <QueryLoading />;
if (error) return <p>Error....</p>;
if (data && data.nodeQuery) {
return (
<IconsLayer
[...]
/>
);
}
return null;
}}
</GetEventsQueryComponent>
);
});
// Hooks example
import * as React from "react";
import { IconsLayer, QueryLoading } from ".";
import { useEventsHook } from "../generated/apolloComponents";
interface IconsLayerProps {
[...]
}
export const EventsSitesIcons: React.FC<IconsLayerProps> = React.memo(props => {
const {data, loading, error} = useEventsHook();
return (
if (loading) return <QueryLoading />;
if (error) return <p>Error....</p>;
if (data && data.nodeQuery) {
return (
<IconsLayer
[...]
/>
);
}
return null;
)
});
The GetEventsQueryComponent
component and hooks examples are typed but it's clean to implement because the adding of types is abstracted away. The component has all the benefits of Typescript (like Intellisense) but without being cluttered and hard to read. Obviously you could do the same yourself with graphql-codegen
but this is programmatic, faster and less error-prone.
Now every time, you add or refactor any of your GraphQL queries, mutations or subscriptions, make sure you run the generator and watch with amazement as your types and components are generated for you. MAGIC!
Conclusion
Using a code generator seems like a no-brainer to me. It allows me to get all the benefits of from using Typescript in all of my GraphQL code too.
Top comments (0)