Apollo Client patterns and data loading strategies.
Quick Reference
Use Apollo Client for:
GraphQL queries and mutations
Client-side caching
Optimistic updates
Key patterns:
One query per route. Only the query root (the route, a remote's top-level view, or shell chrome) calls useQuery (not useEffect + fetch)
Components below the root declare a Component_prop fragment, take a FragmentType prop, and read it with useFragment
useMutation for data updates
Apollo cache shared across component instances
Show a failure state for an Apollo error; don't report it. The shell's Apollo client reports it once (Error Handling)
Apollo Client Basics
useQuery for Fetching
UserList is the query root. It selects id on each user and spreads the fragment of the component it renders, UserCard. Children Read Fragments shows that fragment and how UserCard reads it.
useMutation for Updates
One Query per Route, Children Read Fragments
The route (the query root) makes one request. Every component below it declares the fields it reads in its own fragment and reads them with useFragment, which reads from the cache and never makes a request. Don't have each component call useQuery for its own data (query splitting, covered on the fragments page).Each child owns a fragment in a fragments.graphql next to it, named Component_prop:
The child takes a FragmentType prop and opens it with useFragment. Always pass fragmentName: it keeps working when someone later adds a ...Child spread to the fragment.
The route's one query returns the data for every child, and masking hands each component only what it selected. The fragment rules (one per component, never shared, spread by the parent) live on GraphQL Fragments.To test a component like UserCard on its own, see Testing a Fragment-Owning Component on Its Own.Apollo also deduplicates identical in-flight queries, so two useQuery calls with the same variables make one request. That is a safety net for edge cases, not the design: don't build components on it.
One Operation per Route
Prefer a single operation that selects everything the route needs. A second query that waits for the first (a waterfall) or several independent queries in one component are the patterns to avoid:
When two pieces of data are independent and have separate lifetimes (different polling, different loading states), put each in its own query root. Roots mounted together run in parallel automatically.
Skip Queries Conditionally
Polling
Polling belongs on the query root. IncidentCard declares an IncidentCard_incident fragment, and the query spreads it, so the polled response reaches the cards through the cache:
Optimistic Updates
Cache Updates
Error Handling
Show the user a failure state and stop. Don't call logger.errorAndReport for an Apollo error: the shell's Apollo client reports a network or transport failure once, after any retries, and the backend has already reported an error it returned in the response. See Who reports an error. (useQuery has no onError option in Apollo 4.)
Lazy Queries
Fetching in Components vs Hooks
In Components
Only a query root calls useQuery. A component below the root takes a FragmentType prop and reads it with useFragment; it doesn't take an id and fetch for itself:
The loading state belongs to the query root, which renders the card only once it has data.
In Hooks (Reusable Logic)
A custom hook in the query root is for logic that adds something, such as mapping the data or guarding access. A hook that only calls useQuery with a fixed Document adds a file and no behavior. See GraphQL Operations.
Event Listener Deduplication
Share global event listeners across component instances: