A demonstration of how to use Mock Service Worker (MSW) with Apollo Client to intercept GraphQL responses and add custom extensions
fields.
This project demonstrates how to:
- ✅ Intercept real GraphQL API responses using MSW
- ✅ Add custom
extensions
field to GraphQL responses - ✅ Access extensions in Apollo Client components
- ✅ Use a custom Apollo Link to capture extensions
- ✅ Display extensions data in your React components
apollo-client-test/
├── src/
│ ├── components/
│ │ ├── CountryList.tsx # Main component showing extensions
│ │ └── CountryCard.tsx # Individual country display
│ ├── graphql/
│ │ ├── queries.ts # GraphQL queries
│ │ └── types.ts # TypeScript types
│ ├── mocks/
│ │ ├── handlers.ts # MSW handlers (adds extensions)
│ │ └── browser.ts # MSW browser setup
│ └── main.tsx # Apollo Client + MSW setup
├── public/
│ └── mockServiceWorker.js # Required MSW service worker
└── package.json
- Calls the actual Countries GraphQL API (countries.trevorblades.com)
- Uses MSW to intercept responses without changing your code
- Adds
extensions: { noOfResults: number }
to every GraphQL response - Shows the count of results in the UI header
- Custom
extensionsLink
captures extensions from responses - Attaches extensions to
result.data.extensions
for easy access
npm install
npm run dev
Navigate to http://localhost:5173
- Apollo Client makes GraphQL request to countries.trevorblades.com
- MSW Service Worker intercepts the request
- MSW Handler forwards request to real API using
bypass()
- MSW Handler adds
extensions
field to the response - Apollo extensionsLink captures extensions and attaches to
result.data.extensions
- React Component displays the extensions data
export const handlers = [
graphql.operation(async ({ request, operationName }) => {
// Forward to real API
const response = await fetch(bypass(request));
const originalData = await response.json();
// Add extensions
const enhancedResponse = {
...originalData,
extensions: {
noOfResults: originalData.data?.countries?.length || 0
}
};
return HttpResponse.json(enhancedResponse);
}),
];
// Custom Apollo Link to capture extensions
export const extensionsLink = new ApolloLink((operation, forward) => {
return new Observable(observer => {
if (forward) {
forward(operation).subscribe({
next: (result: FetchResult) => {
// Attach extensions to result.data for easy access
if (result.extensions && result.data) {
(result.data as any).extensions = result.extensions;
}
observer.next(result);
},
// ...error and complete handlers
});
}
});
});
// Apollo Client with link chain
const client = new ApolloClient({
link: from([extensionsLink, httpLink]),
cache: new InMemoryCache(),
});
const CountryList = () => {
const client = useApolloClient();
useEffect(() => {
const fetchCountries = async () => {
const queryResult = await client.query({
query: GET_COUNTRIES,
fetchPolicy: "no-cache",
});
// Access extensions attached by extensionsLink
const extensions = queryResult.data.extensions;
console.log("Extensions:", extensions); // { noOfResults: 250 }
};
fetchCountries();
}, [client]);
// ...render component
};
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ React App │───▶│ Apollo Client │───▶│ extensionsLink │
│ (CountryList) │ │ (client.query) │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────────┘
▲ │
│ ▼
│ ┌─────────────────────┐
│ │ httpLink │
│ │ │
│ └─────────────────────┘
│ │
│ ▼
│ ┌─────────────────────┐
│ │ MSW Service │
│ │ Worker │
│ └─────────────────────┘
│ │
│ ▼
│ ┌─────────────────────┐
│ │ MSW Handlers │
│ │ (adds extensions) │
│ └─────────────────────┘
│ │
│ ▼
│ ┌─────────────────────┐
│ │ Real GraphQL API │
│ │ countries.trevor... │
│ └─────────────────────┘
│ │
└───────────────── Response with extensions ────┘
- Development Flexibility: Mock/enhance API responses without changing backend
- Extensions Access: Access GraphQL extensions field that Apollo Client normally hides
- Real API Testing: Test with real data while adding custom metadata
- Zero Code Changes: Works with existing Apollo Client setup
- Type Safety: Full TypeScript support throughout
- React 19 with TypeScript
- Vite for fast development
- Apollo Client for GraphQL
- Mock Service Worker (MSW) for request interception
- Countries GraphQL API for real data
public/mockServiceWorker.js
- MSW service worker (auto-generated)src/mocks/handlers.ts
- Defines what to intercept and modifysrc/mocks/browser.ts
- MSW browser setup
- MSW is enabled only in development (
import.meta.env.DEV
) - Uses
bypass()
to forward requests to real API - Extensions are automatically calculated from response data
When you run the app:
- Open browser console to see MSW logs
- Look for "Extensions Link" logs showing captured extensions
- Check the UI header showing "Countries Around the World (250)"
- The number in parentheses comes from the extensions field!
This pattern is perfect for:
- API Enhancement: Adding metadata to existing APIs
- Development Testing: Testing with real data + custom extensions
- Performance Monitoring: Adding timing/metrics to GraphQL responses
- Feature Flags: Conditionally modifying API responses
- A/B Testing: Testing different response formats
Ready to explore? Run npm run dev
and check your browser console! 🚀