|
| 1 | +--- |
| 2 | +description: Execute GraphQL queries type-safe in React Query with GraphQL Code Generator. |
| 3 | +--- |
| 4 | + |
| 5 | +# GraphQL Codegen with React Query |
| 6 | + |
| 7 | +In this guide, we will learn how to use the GraphQL Code Generator client preset with React Query to generate type-safe operations and wire it up to a GraphQL server that supports the |
| 8 | +GraphQL over HTTP protocol. |
| 9 | + |
| 10 | +## Prerequisites |
| 11 | + |
| 12 | +For this guide, we assume that you are querying your GraphQL server from within a Browser environment. |
| 13 | +So you already have your Vite, Next.js, Node.js or any other vanilla |
| 14 | +project with TypeScript setup. |
| 15 | + |
| 16 | +We are going to use the public [Star Wars GraphQL API](https://swapi-graphql.netlify.app/) as our |
| 17 | +GraphQL endpoint. |
| 18 | + |
| 19 | +## Setting up GraphQL Code Generator |
| 20 | + |
| 21 | +The GraphQL Code Generator client preset is the preferred and built-in way to generate type-safe |
| 22 | +operations for any GraphQL client library and also vanilla JavaScript. |
| 23 | + |
| 24 | +To get started, install the following dependencies |
| 25 | + |
| 26 | +- **`@graphql-codegen/cli`**: Codegen CLI for running code generation |
| 27 | +- **`@parcel/watcher`**: Enable watch mode for the codegen CLI |
| 28 | +- **`@graphql-codegen/schema-ast`**: Plugin for generating the schema file from the GraphQL API |
| 29 | + endpoint (optional if you already have a schema file) |
| 30 | +- **`@0no-co/graphqlsp`**: TypeScript language server plugin for GraphQL auto-complete (optional) |
| 31 | + |
| 32 | +Feel free to omit the optional dependencies if you don't need them. |
| 33 | + |
| 34 | +```sh npm2yarn |
| 35 | +npm install --save-dev @graphql-codegen/cli @parcel/watcher |
| 36 | +npm install --save-dev @graphql-codegen/schema-ast |
| 37 | +npm install --save-dev @0no-co/graphqlsp |
| 38 | +``` |
| 39 | + |
| 40 | +After that, we can create a `codegen.ts` file in the root of our project with the following |
| 41 | +contents: |
| 42 | + |
| 43 | +```typescript filename="GraphQL Codegen Configuration" |
| 44 | +import type { CodegenConfig } from '@graphql-codegen/cli' |
| 45 | + |
| 46 | +const config: CodegenConfig = { |
| 47 | + schema: 'https://swapi-graphql.netlify.app/.netlify/functions/index', |
| 48 | + documents: ['src/**/*.tsx'], |
| 49 | + ignoreNoDocuments: true, |
| 50 | + generates: { |
| 51 | + './src/graphql/': { |
| 52 | + preset: 'client', |
| 53 | + config: { |
| 54 | + documentMode: 'string' |
| 55 | + } |
| 56 | + }, |
| 57 | + './schema.graphql': { |
| 58 | + plugins: ['schema-ast'], |
| 59 | + config: { |
| 60 | + includeDirectives: true |
| 61 | + } |
| 62 | + } |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +export default config |
| 67 | +``` |
| 68 | + |
| 69 | +Next, we adjust our `tsconfig.json` to load `@0no-co/graphqlsp`. |
| 70 | + |
| 71 | +```json filename="tsconfig.json" |
| 72 | +{ |
| 73 | + "compilerOptions": { |
| 74 | + "plugins": [ |
| 75 | + { |
| 76 | + "name": "@0no-co/graphqlsp", |
| 77 | + "schema": "./schema.graphql" |
| 78 | + } |
| 79 | + ] |
| 80 | + } |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +Finally, we also need to prompt Visual Studio Code to use the local TypeScript version by creating a |
| 85 | +`.vscode/settings.json` file with the following contents: |
| 86 | + |
| 87 | +```json filename=".vscode/settings.json" |
| 88 | +{ |
| 89 | + "typescript.tsdk": "node_modules/typescript/lib", |
| 90 | + "typescript.enablePromptUseWorkspaceTsdk": true |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +## Running the Code Generator |
| 95 | + |
| 96 | +Now we can run the following command to generate the `schema.graphql` file and the GraphQL Code |
| 97 | +Generator client code. **Note:** We are not yet writing GraphQL operations in our codebase, we just |
| 98 | +generate the client boilerplate code. |
| 99 | + |
| 100 | +```sh filename="Run GraphQL Code Generator" |
| 101 | +npx graphql-codegen --config codegen.ts |
| 102 | +``` |
| 103 | + |
| 104 | +After running the command, you should see a new `schema.graphql` file in the root of your project |
| 105 | +and a new folder `src/graphql` with the generated client code. |
| 106 | + |
| 107 | +You almost never need to touch the files within `src/graphql` as they are generated and overwritten |
| 108 | +by GraphQL Code generator. |
| 109 | + |
| 110 | +We will now use the generated client code to write our type-safe GraphQL operations. |
| 111 | + |
| 112 | +## Writing GraphQL Operations |
| 113 | + |
| 114 | +Let's start GraphQL Code Generator in watch mode to generate the client code whenever we write our |
| 115 | +code. |
| 116 | + |
| 117 | +```sh filename="Run GraphQL Code Generator in watch mode" |
| 118 | +npx graphql-codegen --config codegen.ts --watch |
| 119 | +``` |
| 120 | + |
| 121 | +Next within any file in our projects `src` folder, we will import the `graphql` function from within |
| 122 | +`src/graphql`. |
| 123 | + |
| 124 | +```typescript filename="src/index.ts" |
| 125 | +import { graphql } from './graphql' |
| 126 | +``` |
| 127 | + |
| 128 | +This function allows us to define a GraphQL operation. |
| 129 | + |
| 130 | +Thanks to the TypeScript GraphQL LSP plugin, we get auto-complete for our GraphQL operations while |
| 131 | +writing them. |
| 132 | + |
| 133 | + |
| 134 | + |
| 135 | +With that, we will write a simple query operation to get the total count of people in the Star Wars |
| 136 | +universe. |
| 137 | + |
| 138 | +```typescript filename="src/index.ts" |
| 139 | +import { graphql } from './graphql' |
| 140 | + |
| 141 | +const PeopleCountQuery = graphql(` |
| 142 | + query PeopleCount { |
| 143 | + allPeople { |
| 144 | + totalCount |
| 145 | + } |
| 146 | + } |
| 147 | +`) |
| 148 | +``` |
| 149 | + |
| 150 | +As we now save the file in our editor, the GraphQL Code Generator will generate the corresponding |
| 151 | +types, and as you hover over the `PeopleCountQuery` variable, you will see the following: |
| 152 | + |
| 153 | + |
| 154 | + |
| 155 | +`TypedDocumentString` is a container type that holds the query operation string and also the |
| 156 | +TypeScript type for that operations response. |
| 157 | + |
| 158 | +We can now leverage this to build a type-safe function that executes the GraphQL operation against |
| 159 | +our GraphQL server. |
| 160 | + |
| 161 | +## Type-Safe GraphQL Operation Execution |
| 162 | + |
| 163 | +We can build a simple wrapper around `fetch` that takes a `TypedDocumentString` parameter and |
| 164 | +returns a typed response. |
| 165 | + |
| 166 | +```typescript filename="src/graphql/execute.ts" |
| 167 | +import type { TypedDocumentString } from './graphql' |
| 168 | + |
| 169 | +export async function execute<TResult, TVariables>( |
| 170 | + query: TypedDocumentString<TResult, TVariables>, |
| 171 | + ...[variables]: TVariables extends Record<string, never> ? [] : [TVariables] |
| 172 | +) { |
| 173 | + const response = await fetch('https://swapi-graphql.netlify.app/.netlify/functions/index', { |
| 174 | + method: 'POST', |
| 175 | + headers: { |
| 176 | + 'Content-Type': 'application/json', |
| 177 | + Accept: 'application/graphql-response+json' |
| 178 | + }, |
| 179 | + body: JSON.stringify({ |
| 180 | + query: document, |
| 181 | + variables |
| 182 | + }) |
| 183 | + }) |
| 184 | + |
| 185 | + if (!response.ok) { |
| 186 | + throw new Error('Network response was not ok') |
| 187 | + } |
| 188 | + |
| 189 | + return response.json() as TResult |
| 190 | +} |
| 191 | +``` |
| 192 | + |
| 193 | +We can now use this function to execute our `PeopleCountQuery` operation. |
| 194 | + |
| 195 | +```typescript filename="src/index.ts" |
| 196 | +import { graphql } from './graphql' |
| 197 | +import { execute } from './graphql/execute' |
| 198 | + |
| 199 | +const PeopleCountQuery = graphql(` |
| 200 | + query PeopleCount { |
| 201 | + allPeople { |
| 202 | + totalCount |
| 203 | + } |
| 204 | + } |
| 205 | +`) |
| 206 | + |
| 207 | +execute(PeopleCountQuery).then(data => { |
| 208 | + console.log(data) |
| 209 | +}) |
| 210 | +``` |
| 211 | + |
| 212 | +When we now hover over the `data` parameter in the `then` callback, we can see that the response is |
| 213 | +fully typed. |
| 214 | + |
| 215 | + |
| 216 | + |
| 217 | +## Executing Query Operations with React Query |
| 218 | + |
| 219 | +We can now leverage the `execute` function to execute our GraphQL operations with React Query. |
| 220 | + |
| 221 | +```tsx filename="Example usage of executing a Query Operation with React Query" |
| 222 | +import { useQuery } from '@tanstack/react-query' |
| 223 | +import { graphql } from './graphql' |
| 224 | +import { execute } from './graphql/execute' |
| 225 | + |
| 226 | +const PeopleCountQuery = graphql(` |
| 227 | + query PeopleCount { |
| 228 | + allPeople { |
| 229 | + totalCount |
| 230 | + } |
| 231 | + } |
| 232 | +`) |
| 233 | + |
| 234 | +function App() { |
| 235 | + const { data } = useQuery({ |
| 236 | + queryKey: ['films'], |
| 237 | + queryFn: () => execute(PeopleCountQuery) |
| 238 | + }) |
| 239 | + |
| 240 | + return <div>There are {data?.allPeople.totalCount} people</div> |
| 241 | +} |
| 242 | +``` |
| 243 | + |
| 244 | +## Conclusion |
| 245 | + |
| 246 | +In this article, we learned how to use GraphQL Code Generator preset with React Query. |
| 247 | + |
| 248 | +If you want to learn more about GraphQL Code Generator, check out the |
| 249 | +[client preset documentation](/plugins/presets/preset-client). |
| 250 | +E.g. you want to reduce bundle size by using the |
| 251 | +[client preset babel plugin](/plugins/presets/preset-client#reducing-bundle-size) |
| 252 | +or enable |
| 253 | +[persisted documents](/plugins/presets/preset-client#persisted-documents) |
| 254 | +in production for security and performance reasons. |
0 commit comments