We now recommend using the client-preset
package for a better developer experience and smaller impact on bundle size.
Get started on our "React/Vue" guide.
TypeScript React-Query
Package name | Weekly Downloads | Version | License | Updated |
---|---|---|---|---|
@graphql-codegen/typescript-react-query | Nov 24th, 2022 |
Installation
yarn add -D @graphql-codegen/typescript-react-query
Usage Requirements
In order to use this GraphQL Codegen plugin, please make sure that you have GraphQL operations (query
/ mutation
/ subscription
and fragment
) set as documents: …
in your codegen.yml
.
Without loading your GraphQL operations (query
, mutation
, subscription
and fragment
), you won't see any change in the generated output.
This plugin generates React-Query
Hooks with TypeScript typings.
It extends the basic TypeScript plugins: @graphql-codegen/typescript
, @graphql-codegen/typescript-operations
- and thus shares a similar configuration.
If you are using the
react-query
package instead of the@tanstack/react-query
package in your project, please set thelegacyMode
option totrue
.
Config API Reference
fetcher
type: HardcodedFetch | object | string
Customize the fetcher you wish to use in the generated file. React-Query is agnostic to the data-fetching layer, so you should provide it, or use a custom one.
The following options are available to use:
-
'fetch' - requires you to specify endpoint and headers on each call, and uses
fetch
to do the actual http call. -
{ endpoint: string, fetchParams: RequestInit }
: hardcode your endpoint and fetch options into the generated output, using the environmentfetch
method. You can also useprocess.env.MY_VAR
as endpoint or header value. -
file#identifier
- You can use custom fetcher method that should implement the exportedReactQueryFetcher
interface. Example:./my-fetcher#myCustomFetcher
. -
graphql-request
: Will generate each hook withclient
argument, where you should pass your ownGraphQLClient
(created fromgraphql-request
).
exposeDocument
exposeQueryKeys
type: boolean
default: false
For each generate query hook adds getKey(variables: QueryVariables) function. Useful for cache updates. If addInfiniteQuery is true, it will also add a getKey function to each infinite query.
Usage Examples
const query = useUserDetailsQuery(...)
const key = useUserDetailsQuery.getKey({ id: theUsersId })
// use key in a cache update after a mutation
exposeMutationKeys
exposeFetcher
errorType
type: string
default: unknown
Changes the default "TError" generic type.
addInfiniteQuery
type: boolean
default: false
Adds an Infinite Query along side the standard one
legacyMode
type: boolean
default: true
If false, it will work with @tanstack/react-query
, default value is true.
dedupeOperationSuffix
type: boolean
default: false
Set this configuration to true
if you wish to make sure to remove duplicate operation name suffix.
omitOperationSuffix
type: boolean
default: false
Set this configuration to true
if you wish to disable auto add suffix of operation name, like Query
, Mutation
, Subscription
, Fragment
.
operationResultSuffix
type: string
default: (empty)
Adds a suffix to generated operation result type names
documentVariablePrefix
type: string
default: (empty)
Changes the GraphQL operations variables prefix.
documentVariableSuffix
type: string
default: Document
Changes the GraphQL operations variables suffix.
fragmentVariablePrefix
type: string
default: (empty)
Changes the GraphQL fragments variables prefix.
fragmentVariableSuffix
type: string
default: FragmentDoc
Changes the GraphQL fragments variables suffix.
optimizeDocumentNode
type: boolean
default: true
If you are using documentNode: documentMode | documentNodeImportFragments
, you can set this to true
to apply document optimizations for your GraphQL document.
This will remove all "loc" and "description" fields from the compiled document, and will remove all empty arrays (such as directives
, arguments
and variableDefinitions
).
pureMagicComment
type: boolean
default: false
This config adds PURE magic comment to the static variables to enforce treeshaking for your bundler.
experimentalFragmentVariables
type: boolean
default: false
If set to true, it will enable support for parsing variables on fragments.
strictScalars
type: boolean
default: false
Makes scalars strict.
If scalars are found in the schema that are not defined in scalars
an error will be thrown during codegen.
Usage Examples
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
// ...
generates: {
'path/to/file': {
// plugins...
config: {
strictScalars: true,
},
},
},
};
export default config;
defaultScalarType
type: string
default: any
Allows you to override the type that unknown scalars will have.
Usage Examples
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
// ...
generates: {
'path/to/file': {
// plugins...
config: {
defaultScalarType: 'unknown'
},
},
},
};
export default config;
scalars
type: ScalarsMap
Extends or overrides the built-in scalars and custom GraphQL scalars to a custom type.
namingConvention
type: NamingConvention
default: change-case-all#pascalCase
Allow you to override the naming convention of the output.
You can either override all namings, or specify an object with specific custom naming convention per output.
The format of the converter must be a valid module#method
.
Allowed values for specific output are: typeNames
, enumValues
.
You can also use "keep" to keep all GraphQL names as-is.
Additionally, you can set transformUnderscore
to true
if you want to override the default behavior,
which is to preserve underscores.
Available case functions in change-case-all
are camelCase
, capitalCase
, constantCase
, dotCase
, headerCase
, noCase
, paramCase
, pascalCase
, pathCase
, sentenceCase
, snakeCase
, lowerCase
, localeLowerCase
, lowerCaseFirst
, spongeCase
, titleCase
, upperCase
, localeUpperCase
and upperCaseFirst
See more
typesPrefix
typesSuffix
skipTypename
type: boolean
default: false
Does not add __typename
to the generated types, unless it was specified in the selection set.
Usage Examples
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
// ...
generates: {
'path/to/file': {
// plugins...
config: {
skipTypename: true
},
},
},
};
export default config;
nonOptionalTypename
type: boolean
default: false
Automatically adds __typename
field to the generated types, even when they are not specified
in the selection set, and makes it non-optional
Usage Examples
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
// ...
generates: {
'path/to/file': {
// plugins...
config: {
nonOptionalTypename: true
},
},
},
};
export default config;
dedupeFragments
type: boolean
default: false
Removes fragment duplicates for reducing data transfer. It is done by removing sub-fragments imports from fragment definition Instead - all of them are imported to the Operation node.
inlineFragmentTypes
type: InlineFragmentTypeOptions
default: inline
Whether fragment types should be inlined into other operations. "inline" is the default behavior and will perform deep inlining fragment types within operation type definitions. "combine" is the previous behavior that uses fragment type references without inlining the types (and might cause issues with deeply nested fragment that uses list types).
emitLegacyCommonJSImports
type: boolean
default: true
Emit legacy common js imports.
Default it will be true
this way it ensure that generated code works with non-compliant bundlers.
Usage Examples
Note: all generated hooks are just wrappers around react-query
original functions. This codegen plugin just burns
the generated TypeScript types into the operation, and provides flexibility to choose your fetcher
.
Using default fetch
By default, this plugin will generate a fetcher
based on the environment global fetch
definition.
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-query'],
config: {
fetcher: 'fetch'
}
}
}
}
export default config
To use the generated hooks, import it, and then specify the endpoint and optionally fetchParams
:
import { useMyQuery } from './generated'
export const MyComponent = () => {
const { status, data, error, isFetching } = useMyQuery({
endpoint: 'http://localhost:3000/graphql',
fetchParams: {
headers: {
'My-Header': 'XYZ'
}
}
})
}
Using fetch
with Codegen configuration
If you wish to avoid specifying endpoint
and fetchParams
on each hook usage, you can specify those in the codegen.yml
file:
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-query'],
config: {
fetcher: {
endpoint: 'http://localhost:3000/graphql',
fetchParams: {
headers: {
'My-Header': 'SomeValue'
}
}
}
}
}
}
}
export default config
And if you wish to have more control over the value, or even provide it in runtime, you can use environment variables:
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-query'],
config: {
fetcher: {
endpoint: 'process.env.ENDPOINT'
}
}
}
}
}
export default config
You can even use a custom variable from your code, and add custom imports with add
plugin:
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: [
{
add: {
content: "import { endpointUrl, fetchParams } from './my-config';"
}
},
'typescript',
'typescript-operations',
'typescript-react-query'
],
config: {
fetcher: {
endpoint: 'endpointUrl',
fetchParams: 'fetchParams'
}
}
}
}
}
export default config
The generated hooks doesn't require you to specify anything, you can just use it as-is:
import { useMyQuery } from './generated'
export const MyComponent = () => {
const { status, data, error, isFetching } = useMyQuery({})
}
Using graphql-request
If you are using graphql-request
, you can set fetcher
to graphql-request
, and then the generated React Hook will expect you to pass the GraphQLClient
instance (created by graphql-request
library).
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-query'],
config: {
fetcher: 'graphql-request'
}
}
}
}
export default config
And the, while using, provide your client
instance:
import { useMyQuery } from './generated'
import { client } from './my-graphql-request-client'
export const MyComponent = () => {
const { status, data, error, isFetching } = useMyQuery(client, {})
}
Using Custom Fetcher
If you wish to create a custom fetcher, you can provide your own function as a Mapper string (file#identifier
). Codegen will take care of importing it and use it as a fetcher.
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'MY_SCHEMA_PATH',
documents: './src/**/*.graphql',
generates: {
'./generates.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-query'],
config: {
fetcher: {
func: './my-file#myFetcher',
isReactHook: false // optional, defaults to false, controls the function's signature. Read below
}
}
}
}
}
export default config
As a shortcut, the fetcher
property may also directly contain the function as a mapper string:
# …
config:
fetcher: './my-file#myFetcher' # isReactHook is false here (the default version)
Codegen will use myFetcher
, and you can just use the hook directly:
import { useMyQuery } from './generated'
export const MyComponent = () => {
const { status, data, error, isFetching } = useMyQuery({})
}
Depending on the isReactHook
property, your myFetcher
should be in the following signature:
isReactHook: false
type MyFetcher<TData, TVariables> = (operation: string, variables?: TVariables, options?: RequestInit['headers']): (() => Promise<TData>)
isReactHook: true
type MyFetcher<TData, TVariables> = (operation: string, options?: RequestInit['headers']): ((variables?: TVariables) => Promise<TData>)
Usage example (isReactHook: false
)
export const fetchData = <TData, TVariables>(
query: string,
variables?: TVariables,
options?: RequestInit['headers']
): (() => Promise<TData>) => {
return async () => {
const res = await fetch('https://api.url', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...options
},
body: JSON.stringify({
query,
variables
})
})
const json = await res.json()
if (json.errors) {
const { message } = json.errors[0] || {}
throw new Error(message || 'Error…')
}
return json.data
}
}
Usage example (isReactHook: true
)
export const useFetchData = <TData, TVariables>(
query: string,
options?: RequestInit['headers']
): ((variables?: TVariables) => Promise<TData>) => {
// it is safe to call React Hooks here.
const { url, headers } = React.useContext(FetchParamsContext)
return async (variables?: TVariables) => {
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...headers,
...options
},
body: JSON.stringify({
query,
variables
})
})
const json = await res.json()
if (json.errors) {
const { message } = json.errors[0] || {}
throw new Error(message || 'Error…')
}
return json.data
}
}
Note: The return value is an async function, with no params, that returns a Promise
with the actual data.
Using Infinite Query
If you wish to use infinite query for pagination or infinite scroll you can with the addInfiniteQuery
config setting. This will however setup an infinite query for every request whether in reality it can do it or not.
To use this you need to return an object of new queries, and it blends them in to the query.
Usage example (addInfiniteQuery: true
)
with the following query:
query AnimalsQuery($catsRange: Int, $catsStarting: Int, $dogsRange: Int, $dogsStarting: Int) {
cats(range: $catsRange, starting: $catsStarting) {
# …
}
dogs(range: $dogsRange, starting: $dogsStarting) {
# …
}
}
import { useInfiniteMyQuery } from './generated'
export const MyComponent = () => {
const { status, data, error, isFetching } = useInfiniteAnimalsQuery(
{
catsRange: 5,
catsStarting: 0,
dogsRange: 10,
dogsStarting: 0
},
{
getNextPageParam(lastPage, allPages) {
const totalLocal = (allPages.length ?? 0) * (queryParams.limit ?? 1)
const totalDogs = lastPage.dogs.items?.length ?? 0
if (totalLocal < totalDogs) {
return {
catsStarting: totalLocal * 5,
dogsStarting: totalLocal * 10
}
}
}
}
)
}