Apollo Client Basics
Getting Started with Apollo Client
Apollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. It's the most popular GraphQL client for React, Vue, Angular, and vanilla JavaScript applications.
What is Apollo Client?
Apollo Client provides intelligent caching, declarative data fetching, and powerful developer tools. It handles network requests, caching, error handling, and optimistic UI updates automatically.
- InMemoryCache: Normalized caching for efficient data storage
- Declarative data fetching: React hooks like useQuery and useMutation
- Automatic updates: UI updates when cache changes
- Error handling: Built-in error states and retry logic
- DevTools: Browser extension for debugging queries and cache
- TypeScript support: Full type safety with generated types
Installation and Setup
# Install Apollo Client and GraphQL npm install @apollo/client graphql # For React applications npm install @apollo/client graphql react
Creating Apollo Client Instance
Set up Apollo Client with your GraphQL endpoint and cache configuration.
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
const client = new ApolloClient({
uri: 'https://api.example.com/graphql',
cache: new InMemoryCache()
});
export default client;
import {
ApolloClient,
InMemoryCache,
HttpLink,
ApolloLink
} from '@apollo/client';
// HTTP connection to the API
const httpLink = new HttpLink({
uri: 'https://api.example.com/graphql'
});
// Middleware to add auth token to headers
const authLink = new ApolloLink((operation, forward) => {
const token = localStorage.getItem('auth_token');
operation.setContext({
headers: {
authorization: token ? `Bearer ${token}` : ''
}
});
return forward(operation);
});
// Create client with auth and cache
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
fetchPolicy: 'cache-and-network'
}
}
});
export default client;
ApolloProvider - Providing Client to React
Wrap your React app with ApolloProvider to make the client available throughout the component tree.
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './apollo-client';
import UserList from './components/UserList';
function App() {
return (
<ApolloProvider client={client}>
<div className="App">
<h1>My GraphQL App</h1>
<UserList />
</div>
</ApolloProvider>
);
}
export default App;
useQuery Hook - Fetching Data
The useQuery hook executes GraphQL queries and returns loading, error, and data states.
import React from 'react';
import { useQuery, gql } from '@apollo/client';
const GET_USERS = gql`
query GetUsers {
users {
id
name
email
avatar
}
}
`;
function UserList() {
const { loading, error, data } = useQuery(GET_USERS);
if (loading) return <p>Loading users...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h2>Users</h2>
<ul>
{data.users.map(user => (
<li key={user.id}>
<img src={user.avatar} alt={user.name} />
<strong>{user.name}</strong> - {user.email}
</li>
))}
</ul>
</div>
);
}
export default UserList;
Query Variables
Pass dynamic variables to your queries for filtering and parameterization.
import React from 'react';
import { useQuery, gql } from '@apollo/client';
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
posts {
id
title
}
}
}
`;
function UserProfile({ userId }) {
const { loading, error, data } = useQuery(GET_USER, {
variables: { id: userId }
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
const { user } = data;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
<h3>Posts</h3>
<ul>
{user.posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
export default UserProfile;
Refetching and Polling
Refetch queries manually or set up automatic polling for real-time updates.
import React from 'react';
import { useQuery, gql } from '@apollo/client';
const GET_STATS = gql`
query GetStats {
stats {
users
posts
comments
lastUpdated
}
}
`;
function Dashboard() {
const { loading, error, data, refetch } = useQuery(GET_STATS, {
pollInterval: 5000 // Poll every 5 seconds
});
if (loading) return <p>Loading stats...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h2>Dashboard</h2>
<div>Users: {data.stats.users}</div>
<div>Posts: {data.stats.posts}</div>
<div>Comments: {data.stats.comments}</div>
<div>Last Updated: {data.stats.lastUpdated}</div>
<button onClick={() => refetch()}>
Refresh Stats
</button>
</div>
);
}
export default Dashboard;
Network Fetch Policies
Apollo Client offers different fetch policies to control how data is fetched and cached.
// cache-first (default): Check cache first, fetch if not found
const { data } = useQuery(QUERY, {
fetchPolicy: 'cache-first'
});
// cache-and-network: Return cached data, then fetch from network
const { data } = useQuery(QUERY, {
fetchPolicy: 'cache-and-network'
});
// network-only: Always fetch from network, update cache
const { data } = useQuery(QUERY, {
fetchPolicy: 'network-only'
});
// no-cache: Always fetch, don't cache result
const { data } = useQuery(QUERY, {
fetchPolicy: 'no-cache'
});
// cache-only: Only return cached data, never fetch
const { data } = useQuery(QUERY, {
fetchPolicy: 'cache-only'
});
- Use
cache-firstfor static data that rarely changes - Use
cache-and-networkfor data that updates frequently - Use
network-onlyfor authentication or critical real-time data - Use
no-cachefor sensitive data that shouldn't be cached
Error Handling
import React from 'react';
import { useQuery, gql } from '@apollo/client';
const GET_POSTS = gql`
query GetPosts {
posts {
id
title
}
}
`;
function Posts() {
const { loading, error, data } = useQuery(GET_POSTS, {
onError: (error) => {
console.error('Query error:', error);
// Log to error tracking service
},
onCompleted: (data) => {
console.log('Query completed:', data);
}
});
if (loading) return <p>Loading...</p>;
if (error) {
// Network error
if (error.networkError) {
return <p>Network error. Please check your connection.</p>;
}
// GraphQL errors
if (error.graphQLErrors.length > 0) {
return (
<div>
{error.graphQLErrors.map((err, i) => (
<p key={i}>Error: {err.message}</p>
))}
</div>
);
}
return <p>Something went wrong.</p>;
}
return (
<ul>
{data.posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
export default Posts;
- Set up Apollo Client in a React app with authentication headers
- Create a component that fetches a list of blog posts using useQuery
- Add a refetch button that manually refreshes the data
- Implement proper loading and error states with user-friendly messages
- Experiment with different fetch policies (cache-first, cache-and-network, network-only) and observe the behavior