React Native and GraphQL in 2026: Apollo Client, Queries and Interview Questions
Learn to integrate GraphQL with React Native using Apollo Client 4.0. This tutorial covers queries, mutations, caching strategies, and common interview questions about mobile GraphQL development.

GraphQL has become the preferred API layer for React Native applications that need flexible data fetching and efficient network usage. Apollo Client 4.0, released in late 2025, brings a leaner architecture with 20-30% smaller bundles and improved TypeScript support specifically designed for mobile environments.
Apollo Client 4.0 decouples React-specific exports to @apollo/client/react, supports React 19 Suspense and the React Compiler, and replaces the monolithic ApolloError with specific error classes for better debugging.
Setting Up Apollo Client 4 in React Native
The installation process remains straightforward. Apollo Client 4.0 works with both Expo and bare React Native projects.
# Installation
npm install @apollo/client graphqlThe client initialization requires an HttpLink for network requests and an InMemoryCache for local state management.
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
const httpLink = new HttpLink({
uri: 'https://api.example.com/graphql',
// Enable text streaming for @defer and subscriptions
fetchOptions: {
reactNative: { textStreaming: true },
},
});
export const apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
// Merge paginated results
products: {
keyArgs: ['category'],
merge(existing = [], incoming) {
return [...existing, ...incoming];
},
},
},
},
},
}),
});The typePolicies configuration handles cache normalization for paginated data, a common requirement in mobile apps with infinite scroll.
Wrapping the App with ApolloProvider
The provider pattern connects Apollo Client to the React component tree. Place it at the root level, typically in App.tsx or the Expo Router layout.
import { ApolloProvider } from '@apollo/client/react';
import { apolloClient } from './src/apollo/client';
import { RootNavigator } from './src/navigation';
export default function App() {
return (
<ApolloProvider client={apolloClient}>
<RootNavigator />
</ApolloProvider>
);
}Apollo Client 4.0 imports React hooks from @apollo/client/react rather than the main entry point. This change reduces bundle size when the core client runs in non-React contexts.
Writing Type-Safe GraphQL Queries
GraphQL Code Generator produces TypeScript types from the schema, eliminating runtime type mismatches. Install it alongside the client plugin.
# Install codegen tools
npm install -D @graphql-codegen/cli @graphql-codegen/client-presetThe codegen configuration points to the schema and specifies output locations.
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: 'https://api.example.com/graphql',
documents: ['src/**/*.tsx', 'src/**/*.ts'],
generates: {
'./src/gql/': {
preset: 'client',
config: {
documentMode: 'string',
},
},
},
};
export default config;Running npx graphql-codegen generates typed document nodes that the useQuery hook consumes.
Fetching Data with useQuery
The useQuery hook manages loading states, errors, and cache updates. Apollo Client 4.0 introduces the dataState API for more predictable state transitions.
import { useQuery } from '@apollo/client/react';
import { graphql } from '../gql';
import { ActivityIndicator, FlatList, Text, View } from 'react-native';
const GET_PRODUCTS = graphql(`
query GetProducts($category: String!, $limit: Int, $offset: Int) {
products(category: $category, limit: $limit, offset: $offset) {
id
name
price
imageUrl
}
}
`);
export function ProductListScreen({ category }: { category: string }) {
const { data, loading, error, fetchMore } = useQuery(GET_PRODUCTS, {
variables: { category, limit: 20, offset: 0 },
// Fetch from cache first, then network
fetchPolicy: 'cache-and-network',
// Keep previous data while fetching new
notifyOnNetworkStatusChange: true,
});
if (error) {
return <Text>Error: {error.message}</Text>;
}
const loadMore = () => {
fetchMore({
variables: { offset: data?.products.length ?? 0 },
});
};
return (
<FlatList
data={data?.products ?? []}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <ProductCard product={item} />}
onEndReached={loadMore}
onEndReachedThreshold={0.5}
ListFooterComponent={loading ? <ActivityIndicator /> : null}
/>
);
}The cache-and-network fetch policy displays cached data immediately while refreshing from the server. This pattern reduces perceived latency on mobile networks.
Ready to ace your React Native interviews?
Practice with our interactive simulators, flashcards, and technical tests.
Mutations and Optimistic Updates
Mutations modify server data and update the local cache. Optimistic responses provide instant feedback before the server confirms the change.
import { useMutation } from '@apollo/client/react';
import { graphql } from '../gql';
const ADD_TO_CART = graphql(`
mutation AddToCart($productId: ID!, $quantity: Int!) {
addToCart(productId: $productId, quantity: $quantity) {
id
items {
id
product {
id
name
}
quantity
}
totalPrice
}
}
`);
export function useAddToCart() {
const [addToCart, { loading }] = useMutation(ADD_TO_CART, {
// Optimistic response for instant UI feedback
optimisticResponse: ({ productId, quantity }) => ({
addToCart: {
__typename: 'Cart',
id: 'current-cart',
items: [],
totalPrice: 0,
},
}),
// Update cache after mutation
update(cache, { data }) {
if (!data?.addToCart) return;
cache.modify({
id: cache.identify({ __typename: 'Cart', id: 'current-cart' }),
fields: {
items: () => data.addToCart.items,
totalPrice: () => data.addToCart.totalPrice,
},
});
},
});
return { addToCart, loading };
}The cache.modify method directly updates cached fields without refetching the entire query. This approach minimizes network requests on cellular connections.
Handling Network Errors in Mobile Apps
Apollo Client 4.0 replaces the generic ApolloError with specific error classes. The static .is() methods enable type-safe error handling.
import { NetworkError, GraphQLErrors } from '@apollo/client/errors';
import { Text, Button, View } from 'react-native';
interface Props {
error: Error;
onRetry: () => void;
}
export function QueryErrorHandler({ error, onRetry }: Props) {
// Network failure (offline, timeout, DNS)
if (NetworkError.is(error)) {
return (
<View>
<Text>Connection failed. Check your internet.</Text>
<Button title="Retry" onPress={onRetry} />
</View>
);
}
// GraphQL validation or resolver errors
if (GraphQLErrors.is(error)) {
const firstError = error.graphQLErrors[0];
if (firstError?.extensions?.code === 'UNAUTHENTICATED') {
return <Text>Session expired. Please log in again.</Text>;
}
return <Text>Request failed: {firstError?.message}</Text>;
}
return <Text>Something went wrong</Text>;
}Distinguishing network errors from GraphQL errors allows different recovery strategies: retry for transient failures, redirect for authentication issues.
Offline Support with Cache Persistence
Mobile apps need offline access to previously fetched data. The apollo3-cache-persist library serializes the cache to AsyncStorage.
import AsyncStorage from '@react-native-async-storage/async-storage';
import { persistCache } from 'apollo3-cache-persist';
import { apolloClient } from './client';
export async function initializeCache() {
await persistCache({
cache: apolloClient.cache,
storage: AsyncStorage,
maxSize: 1024 * 1024 * 5, // 5 MB limit
debounce: 1000, // Write at most once per second
});
}Call initializeCache before rendering the app to restore cached data from disk. Users see their last-fetched content immediately, even without network access.
Real-Time Updates with GraphQL Subscriptions
Subscriptions push server events to the client over WebSocket. Apollo Client 4.0 supports the newer graphql-ws protocol.
import { split, HttpLink, ApolloClient, InMemoryCache } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { getMainDefinition } from '@apollo/client/utilities';
const httpLink = new HttpLink({ uri: 'https://api.example.com/graphql' });
const wsLink = new GraphQLWsLink(
createClient({
url: 'wss://api.example.com/graphql',
connectionParams: async () => ({
authToken: await getStoredToken(),
}),
})
);
// Route subscriptions to WebSocket, others to HTTP
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink
);
export const apolloClient = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});The split function directs operations to the appropriate transport based on the operation type.
Common Interview Questions: React Native GraphQL
Technical interviews often probe understanding of GraphQL's mobile-specific tradeoffs. The following questions appear frequently in React Native positions.
Why choose GraphQL over REST for mobile apps?
GraphQL reduces over-fetching by returning only requested fields. A single query replaces multiple REST endpoints, reducing round trips over high-latency cellular connections. The typed schema enables compile-time validation, catching API mismatches before runtime.
How does Apollo Client cache data?
Apollo normalizes responses by __typename and id, storing each entity once. Queries reference normalized objects by cache key. This deduplication saves memory and ensures consistency: updating a product in one list updates it everywhere.
What fetch policies does Apollo Client support?
| Policy | Behavior | Use Case |
|---|---|---|
cache-first | Return cache if available, else fetch | Default, minimizes requests |
cache-and-network | Return cache immediately, then update from network | Fresh data with instant display |
network-only | Always fetch, update cache | When stale data is unacceptable |
cache-only | Never fetch, fail if not cached | Offline mode |
no-cache | Fetch without caching | Sensitive data |
How do optimistic updates work?
The client predicts the mutation result and updates the cache before the server responds. If the server returns a different result, Apollo replaces the optimistic data with the actual response. If the mutation fails, Apollo rolls back to the previous state.
What causes an N+1 problem in GraphQL?
Nested resolvers that fetch related data individually create N+1 queries. Requesting a list of 50 products with their categories triggers 1 product query plus 50 category queries. Server-side DataLoader batches these into a single query.
How do you handle authentication tokens with Apollo Client?
The recommended approach uses an Apollo Link to attach tokens to every request.
import { setContext } from '@apollo/client/link/context';
import { getStoredToken } from '../auth/storage';
export const authLink = setContext(async (_, { headers }) => {
const token = await getStoredToken();
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});Chain this link before the HttpLink in the client configuration.
Debugging GraphQL in React Native
Apollo provides multiple debugging options for React Native development. The VS Code Apollo extension includes built-in Client DevTools that display cache contents and query history.
For standalone debugging, Flipper integrates with the community plugin react-native-apollo-devtools. This plugin inspects cache state, watches queries in flight, and replays mutations.
Network inspection through React Native Debugger requires enabling "Debug JS Remotely". The Network tab then shows GraphQL payloads with request and response bodies.
Key Takeaways for Mobile GraphQL Development
- Apollo Client 4.0 reduces bundle size by 20-30% through ESM-first packaging and decoupled React exports.
- The
cache-and-networkfetch policy balances instant display with data freshness on slow connections. - Optimistic updates provide immediate feedback;
cache.modifyavoids unnecessary refetches. - Typed error classes in Apollo Client 4.0 enable specific handling for network failures versus GraphQL errors.
- Cache persistence with
apollo3-cache-persistenables offline access to previously fetched data. - GraphQL subscriptions require the
graphql-wsprotocol and a split link configuration. - Interview questions focus on caching strategies, fetch policies, and mobile-specific tradeoffs like N+1 prevention.
Start practicing!
Test your knowledge with our interview simulators and technical tests.
Can you spot the bug in React Native?
One real snippet, one hidden bug, one attempt a day. No account needed to try.

Written by
Anthony Fillion-MailletFounder of SharpSkill
Full-stack developer for over 10 years. Runs SharpSkill and answers for everything published here.
Updated on August 20, 2026
Tags
Share
Related articles

Flutter vs React Native Performance Comparison: Benchmarks and Interview Questions 2026
Compare Flutter and React Native performance in 2026 with real benchmarks. Learn about Impeller, the New Architecture, frame rates, memory usage, and what interviewers expect you to know.

React Native 0.85 in 2026: New Animation Backend, Strict TypeScript API and Interview Questions
Deep dive into React Native 0.85's Shared Animation Backend, post-bridge architecture, Metro TLS support, and key interview questions about the new architecture in 2026.

React Native App Development Guide 2026: Building Production Apps and Interview Questions
Complete guide to React Native production development in 2026 covering Expo SDK 56, EAS Build, Hermes V1, the New Architecture, and interview preparation for React Native developers.