GraphQL

Purpose and Scope

TanStack Query treats data fetching as a promise-producing operation, so GraphQL is not a special integration layer inside the React adapter. The GraphQL guide explains that React Query can work with any asynchronous fetching client, including GraphQL clients, because the query function only needs to return a promise. For application developers, this means a GraphQL request, a REST request, an SDK call, or any other backend operation can sit behind the same useQuery lifecycle, cache identity, refetching, and status model.

Sources: docs/framework/react/graphql.md

The main design boundary to understand is caching. TanStack Query stores query results by query key and manages freshness, observers, retries, and refetching around those results. It does not provide a normalized GraphQL entity cache. That distinction matters when comparing it with GraphQL-specific clients that split responses into entities and update shared records by type and id. The official guidance calls normalized caching a rare need for many applications, but it also makes clear that TanStack Query intentionally does not implement that model.

Sources: docs/framework/react/graphql.md

Relevant Source Files

  • docs/framework/react/graphql.md - The React GraphQL guide, including the promise-agnostic fetching statement, the normalized caching caveat, and the typed graphql-request plus GraphQL Code Generator example.

Core Primitives

A GraphQL query in TanStack Query is built from the same primitives as any other React Query query. The queryKey identifies the cached result, while the queryFn performs the asynchronous request. In the GraphQL guide, the request function calls graphql-request against the SWAPI GraphQL endpoint and passes a generated document plus variables. React Query does not inspect the GraphQL schema or operation text; it observes the returned promise and associates the result with the key supplied to useQuery. و Sources: docs/framework/react/graphql.md

The document helper named graphql comes from the generated code in the example project, not from TanStack Query itself. Its job is to turn a GraphQL operation string into a typed document that graphql-request can execute. When paired with GraphQL Code Generator, the operation result type and variable type flow into the request call, so the data returned by useQuery is typed without manually annotating every component. This keeps schema-driven type safety in the GraphQL tooling layer while leaving server-state orchestration to React Query.

Sources: docs/framework/react/graphql.md

Type-Safety and Code Generation Flow

The guide recommends combining React Query with graphql-request^5 and GraphQL Code Generator for fully typed GraphQL operations. The workflow starts with an operation document, such as a query that accepts $first: Int! and selects film id and title fields. Code generation turns that operation into a typed artifact. The React component then passes that artifact to request, together with the endpoint URL and a variables object. Because the variables are type checked, mistakes such as missing required variables or passing the wrong shape can be caught before runtime.

Sources: docs/framework/react/graphql.md

import request from 'graphql-request'
import { useQuery } from '@tanstack/react-query'
 
import { graphql } from './gql/gql'
 
const allFilmsWithVariablesQueryDocument = graphql(/* GraphQL */ `
  query allFilmsWithVariablesQuery($first: Int!) {
    allFilms(first: $first) {
      edges {
        node {
          id
          title
        }
      }
    }
  }
`)
 
function App() {
  const { data } = useQuery({
    queryKey: ['films'],
    queryFn: async () =>
      request(
        'https://swapi-graphql.netlify.app/.netlify/functions/index',
        allFilmsWithVariablesQueryDocument,
        { first: 10 },
      ),
  })
}

The important implementation choice in this example is that the queryFn stays small and backend-specific. It knows the endpoint, the generated operation document, and the variables. The surrounding component receives the ordinary React Query result object and can render based on the same pending, error, success, and background-fetching signals used for non-GraphQL data. That separation makes GraphQL adoption incremental: a team can add generated GraphQL operations without changing how components subscribe to cached server state.

Sources: docs/framework/react/graphql.md

System-to-Code Mapping

ConcernWhere it appears in the guidePractical meaning
Backend transportgraphql-request inside queryFnGraphQL execution is delegated to a promise-based client.
Cache identityqueryKey: ['films']React Query stores and matches the operation result by key.
Schema type safetyGenerated graphql documentGraphQL Code Generator supplies typed operation and variable contracts.
Component consumptionuseQuery({ queryKey, queryFn })React components consume GraphQL results through the standard query API.
Cache model boundaryNormalized caching noteTanStack Query caches query results, not normalized GraphQL entities.

This mapping is useful when designing real applications. Put stable resource identity and variables into the query key, put the GraphQL client call inside the query function, and let generated documents carry schema types. If multiple screens read the same GraphQL operation with the same variables, they can share the same cached result through a consistent key. If a mutation changes data that affects a query result, use TanStack Query invalidation or cache updates rather than expecting entity-level normalization to update every related operation automatically.

Sources: docs/framework/react/graphql.md

Implementation Details and Constraints

The GraphQL page is deliberately small because there is no GraphQL-specific runtime adapter in this part of the repository. Its purpose is to document the integration pattern rather than expose a separate package API. The only TanStack Query API shown is useQuery, and the only required contract is that the queryFn returns a promise. This keeps the React adapter backend agnostic and lets GraphQL libraries evolve independently, whether an application uses graphql-request, generated SDK functions, or another promise-returning GraphQL client.

Sources: docs/framework/react/graphql.md

The normalized caching caveat should shape expectations before choosing an architecture. If the application requires entity-level writes across many different GraphQL operations, a normalized client may still be part of the design. If the application primarily needs request deduplication, freshness control, background refetching, retries, pagination patterns, and mutation invalidation around operation results, TanStack Query fits naturally. The recommended typed setup gives GraphQL teams schema safety while keeping cache lifecycle behavior consistent with the rest of the TanStack Query model.

Sources: docs/framework/react/graphql.md

Next Steps

Start by defining one GraphQL operation and generating typed documents with GraphQL Code Generator. Then wrap the generated document in a useQuery call using a query key that includes the resource name and any variables that affect the result. After the first read path works, decide how mutations will refresh related reads: invalidating matching query keys is usually the simplest approach. For deeper cache behavior, continue with the query keys, query functions, invalidations from mutations, and QueryClient reference pages.