Skip to content

Instantly share code, notes, and snippets.

@ilovett
Last active April 26, 2023 13:35
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ilovett/05eb90de7305a1750863c5c5724e9efb to your computer and use it in GitHub Desktop.
Save ilovett/05eb90de7305a1750863c5c5724e9efb to your computer and use it in GitHub Desktop.
Using GraphQL API and codegen to automatically generate types and using with `react-query` and `axios`

The models are defined on the graphql api and so you never have to worry about manually syncing types on your client code. You will immediately know if your api changed something that will break your code and where.

I'm using the typescript-graphql-request codegen plugin just to generate the docs and types, and ignoring the generated hooks specific to graphql-request

One could write a react-query generator that creates a hook for each query that you write but may need some fetcher adapter.

I was going to wait a bit more to see a pattern emerge. I like to use axios and others use fetch or other libs.

Perhaps some kind of provider with an adapter for axios or fetch or whatever.

Then we could have the plugin generate hooks like so:

// GeneratedGraphQL.tsx

export const useRouteBasedComponentQuery = (variables, options) => {
  const fetcher = useFetcher();
  return useQuery(['RouteBasedComponent', variables], fetcher<RouteBasedComponentQuery>(RouteBasedComponentDocument, variables), options);
}
// RouteBasedComponent.tsx
import { useRouteBasedComponentQuery } from '../../GeneratedGraphQL'

...

const query = useRouteBasedComponentQuery({ objectId, size, weight }, { onSuccess: () => alert('WOOHOO!') });

This would simplify the boilerplate in RouteBasedComponent.tsx that connects useQuery and graphqlRequest

import axios from 'axios';
import { print, DocumentNode } from 'graphql';
export const graphqlQuery = <T = unknown>(query: DocumentNode, variables: any = {}) => {
return axios({
method: 'post',
data: {
query: print(query),
variables,
},
}).then((res) => {
// consider throwing an error if `res.errors.length` (can have 200 with errors)
return res.data;
}) as Promise<{ data: T; errors?: Error[] }>;
};
overwrite: true
schema: 'http://localhost:8080/graphql'
documents: 'src/graphql/operations/**/*.graphql'
watch:
- 'src/**/*.{tsx,ts,graphql}'
generates:
src/graphql/generated/GeneratedGraphQL.ts:
plugins:
- typescript
- typescript-operations
- typescript-graphql-request
query RouteBasedComponent($objectId: String, $size: SizeEnum, $weight: Int) {
inventory(objectId: $objectId, size: $size, weight: $weight) {
objectId
nestedRelations {
deepRelation {
name
created
}
images {
url
type
}
}
otherStuff
}
}
import React from 'react';
import { useQuery } from 'react-query';
import { RouteBasedComponentQuery, RouteBasedComponentDocument } from '../../GeneratedGraphQL';
import { graphqlQuery } from '../../axiosHelpers';
export const RouteBasedComponent: React.FC = () => {
// variables are plugged in to the axios graphqlQuery wrapper along with the query type and document (from codegen)
const query = useQuery(['RouteBasedComponent', { objectId, size, weight }], async (_, variables) => {
return graphqlQuery<RouteBasedComponentQuery>(RouteBasedComponentDocument, variables);
});
// alternatively if a codegen plugin is developed:
const query = useRouteBasedComponentQuery({ objectId, size, weight });
// response type is strongly typed
return (
<>
{query.data.data.nestedRelations.map((relation, index) => {
return (
<div key={index}>
<h2>{relation.deepRelation.name}</h2>
<Images images={relation.images} />
</div>
)
})}
</>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment