Calling fetch by hand works for a demo, but a real React application needs loading states, error handling, caching, and a way to keep the screen in sync after a mutation. Apollo Client provides all of that through hooks. After this lesson you will be able to configure the client, read data with useQuery, write data with useMutation, attach authentication headers, and choose a fetch policy.
Apollo Client 4 splits its React bindings into a separate entry point, so hooks and the provider come from @apollo/client/react:
npm install @apollo/client graphqlimport { ApolloClient, InMemoryCache, HttpLink, ApolloLink } from "@apollo/client";
import { ApolloProvider } from "@apollo/client/react";
const authLink = new ApolloLink((operation, forward) => {
const token = localStorage.getItem("token");
operation.setContext({ headers: token ? { authorization: `Bearer ${token}` } : {} });
return forward(operation);
});
const client = new ApolloClient({
link: authLink.concat(new HttpLink({ uri: "http://localhost:4000/graphql" })),
cache: new InMemoryCache(),
});
export function App() {
return (
<ApolloProvider client={client}>
<Posts />
</ApolloProvider>
);
}link describes how requests reach the server; links form a chain, so the auth link runs before the terminating HttpLink. cache stores every result; the next chapter explains how. Create the client once at module level, never inside a component.
Operations are written with the gql tag and passed to useQuery, which runs the query on mount and re-renders the component as the state changes:
import { gql } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
const GET_POSTS = gql`
query GetPosts($first: Int!) {
posts(first: $first) { id title }
}
`;
export function Posts() {
const { data, loading, error, refetch } = useQuery(GET_POSTS, { variables: { first: 10 } });
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <ul>{data.posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;
}Always name operations (GetPosts); names appear in dev tools, server logs and refetchQueries. When variables change, the hook fetches again automatically. refetch() forces a network request, and pollInterval: 5000 re-runs the query on a timer.
| fetchPolicy | Behaviour |
|---|---|
| cache-first (default) | Serve from cache if complete, otherwise fetch |
| cache-and-network | Show cached data immediately, then update from the network |
| network-only | Always fetch, but store the result in the cache |
| no-cache | Always fetch and never store |
useMutation does not run on mount. It returns a function you call from an event handler plus a result object:
const ADD_POST = gql`
mutation AddPost($title: String!) {
addPost(title: $title) { id title }
}
`;
const [addPost, { loading: saving, error }] = useMutation(ADD_POST, {
refetchQueries: [GET_POSTS],
});
<button disabled={saving} onClick={() => addPost({ variables: { title } })}>
Add
</button>refetchQueries is the simplest way to update the list after the mutation completes: Apollo re-runs GetPosts and the Posts component re-renders. For large lists it costs an extra round trip; the caching chapter shows how to update the cache directly instead. addPost returns a promise resolving to { data }, so you can await it and navigate afterwards.
gql documents inside the component body; move them to module scope so they are parsed once.data.posts before checking loading; data is undefined on the first render.id in operations; without it Apollo cannot normalise objects and cache updates stop working.ApolloClient per render, which throws away the cache every time.Where do `useQuery` and `ApolloProvider` come from in Apollo Client 4?
ApolloClient with a link chain and an InMemoryCache, and provide it with ApolloProvider from @apollo/client/react.useQuery runs on mount, tracks loading, error and data, and refetches when variables change.useMutation returns a trigger function and status; use refetchQueries for simple list updates.ApolloLink placed before HttpLink.fetchPolicy controls the balance between cached and fresh data per query.Next lesson: Client Caching and Normalization: Apollo, urql and Relay — see how results are normalised, update the cache after mutations, and compare the major clients.