Plugins
TypeScript
react-query

Plugin Details

Package name@graphql-codegen/typescript-react-query
Weekly Downloadsdownloads
Versionlicense
Licenselicense
UpdatedAug 8th, 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 the legacyMode option to true.

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 environment fetch method. You can also use process.env.MY_VAR as endpoint or header value.

  • file#identifier - You can use custom fetcher method that should implement the exported ReactQueryFetcher interface. Example: ./my-fetcher#myCustomFetcher.

  • graphql-request: Will generate each hook with client argument, where you should pass your own GraphQLClient (created from graphql-request).

exposeDocument

type: boolean default: false

For each generate query hook adds document field with a corresponding GraphQL query. Useful for queryClient.fetchQuery.

Usage Examples

queryClient.fetchQuery(
  useUserDetailsQuery.getKey(variables),
  () => gqlRequest(useUserDetailsQuery.document, variables)
)

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

type: boolean default: false

For each generate mutation hook adds getKey() function. Useful for call outside of functional component.

Usage Examples

const mutation = useUserDetailsMutation(...)
const key = useUserDetailsMutation.getKey()

exposeFetcher

type: boolean default: false

For each generate query hook adds fetcher field with a corresponding GraphQL query using the fetcher. It is useful for queryClient.fetchQuery and queryClient.prefetchQuery.

Usage Examples

await queryClient.prefetchQuery(userQuery.getKey(), () => userQuery.fetcher())

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

config:
  strictScalars: true

defaultScalarType

type: string default: any

Allows you to override the type that unknown scalars will have.

Usage Examples

config:
  defaultScalarType: unknown

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

type: string default: (empty)

Prefixes all the generated types.

Usage Examples

config:
  typesPrefix: I

typesSuffix

type: string default: (empty)

Suffixes all the generated types.

Usage Examples

config:
  typesSuffix: I

skipTypename

type: boolean default: false

Does not add __typename to the generated types, unless it was specified in the selection set.

Usage Examples

config:
  skipTypename: true

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

config:
  nonOptionalTypename: true

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.

schema: MY_SCHEMA_PATH
documents: './src/**/*.graphql'
generates:
  ./generates.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-query
    config:
      fetcher: fetch

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:

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

And if you wish to have more control over the value, or even provide it in runtime, you can use environment variables:

schema: MY_SCHEMA_PATH
documents: './src/**/*.graphql'
generates:
  ./generates.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-query
    config:
      fetcher:
        endpoint: 'process.env.ENDPOINT'

You can even use a custom variable from your code, and add custom imports with add plugin:

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'

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).

schema: MY_SCHEMA_PATH
documents: './src/**/*.graphql'
generates:
  ./generates.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-query
    config:
      fetcher: graphql-request

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.

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

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
          }
        }
      }
    }
  )
}
Last updated on August 10, 2022