How to Mock GraphQL APIs with Fake JSON Data (Apollo & urql)
Mocking a GraphQL API is a different problem than mocking REST. There's no fixed set of URLs to intercept — every request hits the same single endpoint with a different query — so you match on the operation name instead of the path. Here's how to do it properly with both Apollo Client and urql, the two most common GraphQL clients.
Step 1: Generate the Fake Data First
Shape your mock response to match your actual GraphQL schema's return type — including nested fields your query selects. Build that structure in Dummy JSON Generator before writing any mock resolver code.
{
"id": "42",
"title": "Deep Work",
"author": { "id": "7", "name": "Cal Newport" },
"reviews": [{ "id": "1", "rating": 5, "comment": "Excellent." }]
}Option A: Apollo Client's MockedProvider
Apollo ships a first-party testing utility, MockedProvider, that matches mocks to queries by comparing the query document and its variables exactly.
import { MockedProvider } from '@apollo/client/testing'
import { GET_BOOK } from './queries'
import bookFixture from './mock/book.json'
const mocks = [
{
request: { query: GET_BOOK, variables: { id: '42' } },
result: { data: { book: bookFixture } },
},
]
render(
<MockedProvider mocks={mocks} addTypename={false}>
<BookPage id="42" />
</MockedProvider>
)Gotcha: MockedProvider matches variables exactly — if your component sends an extra variable you didn't include in the mock, the match silently fails and the query hangs in a loading state. Set addTypename={false} unless your fixtures include __typename fields.
Option B: MSW with graphql.query / graphql.mutation
If you're using urql, Vue Apollo, or want the same mocking approach across REST and GraphQL, MSW's GraphQL handlers match by operation name instead of exact query text — often more forgiving for larger apps with many similar queries.
import { graphql, HttpResponse } from 'msw'
import bookFixture from './mock/book.json'
export const handlers = [
graphql.query('GetBook', ({ variables }) => {
return HttpResponse.json({ data: { book: bookFixture } })
}),
]This works identically whether your client is Apollo, urql, or a plain fetch call to a GraphQL endpoint, since MSW intercepts at the network layer rather than inside the client library.
Handling Pagination and Lists
GraphQL APIs commonly paginate with cursor-based connections. Generate a list of fake items, then slice it inside your handler based on the incoming after/first variables so your mock actually behaves like a real paginated resolver instead of always returning page one.
graphql.query('GetBooks', ({ variables }) => {
const { first = 10, after = 0 } = variables
const page = allBooks.slice(after, after + first)
return HttpResponse.json({
data: {
books: {
edges: page.map(b => ({ node: b })),
pageInfo: { hasNextPage: after + first < allBooks.length },
},
},
})
})Testing GraphQL Errors
GraphQL returns errors inside a 200 OK response body rather than an HTTP error status — a detail that trips up mocks written by developers used to REST. Make sure your error-state mocks follow the same shape.
graphql.query('GetBook', () => {
return HttpResponse.json({
errors: [{ message: 'Book not found' }],
data: null,
})
})Which Approach Should You Use?
- Small app, only using Apollo Client? →
MockedProvider— no extra dependency. - Multiple clients, or want shared mocks across REST and GraphQL? → MSW's GraphQL handlers.
- Testing pagination or complex resolvers? → MSW, since it can implement real logic instead of static fixtures.