Let's start with the respect it's owed: @graphql-tools/mock is the standard way to mock GraphQL in JavaScript โ 1.36 million downloads a week (npm, Aug 19โ25, 2026), maintained by The Guild, and it's what Apollo Server's own mocking documentation tells you to use. The core move is genuinely great: hand addMocksToSchema an executable schema and every resolver is auto-mocked from the type system. For schema-first unit tests inside a JS process, it is the right tool, full stop.
But if you're on this page, you've probably hit the moment where the mock needs to leave your process โ and that's the wall it can't cross, plus a few smaller ones that cost real time. We verified everything below against v9.1.13 in August 2026 by running it.
1. The mock only exists inside your JS process. addMocksToSchema returns a schema object. Unless you also stand up and operate an HTTP server around it, there is no URL โ curl, Postman, your Flutter/iOS/Android app, a backend service in another language, a teammate on another machine, and CI running against a deployed preview all get nothing. And if you do wrap it in a yoga/apollo-server instance, you now own hosting, uptime, and state for a mock server.
2. Default data is unusable for anything demo-shaped. Out of the box, every String field is literally "Hello World", every Float is a random number that can be negative, and every list has exactly 2 items. Our actual first query:
{ products { id title price inStock } }
// โ [{ "title": "Hello World", "price": 54.243850259423, ... },
// { "title": "Hello World", "price": -85.07702399873587, ... }]
// a product priced at -85.08, named Hello World โ for every product
Fixable, sure โ by writing and maintaining a mock function per scalar and per type. That's mock config as code, forever.
3. Mutations ignore your arguments by default. This one surprises everyone the first time:
mutation { createProduct(title: "X") { id title } }
// โ { "title": "Hello World" } โ your argument went nowhere
4. Statefulness is DIY โ and the wiring is subtle. The MockStore can hold state, but you must write resolvers for it, and here's the gotcha we hit: wiring only the mutation through the store isn't enough โ store.set(...) in the mutation silently doesn't round-trip until the query field is also resolved from the store:
const mocked = addMocksToSchema({ schema, resolvers: (store) => ({
Query: { product: (_, { id }) => store.get('Product', id) }, // โ required too!
Mutation: { setTitle: (_, { id, title }) => {
store.set('Product', id, 'title', title);
return store.get('Product', id);
}},
})});
With both wired it works โ and it's still in-memory in one instance. New process, new test file, teammate's machine: amnesia.
5. You need the SDL first. The library mocks a schema you already have and maintain. If what you actually have is a db.json, a CSV, an OpenAPI spec, or just a list of resources, the SDL is another artifact to write.
Mockbird starts from data instead of SDL: define resources (or import an OpenAPI spec, db.json, or CSV), get realistic seeded records (faker-style names, prices, emails โ never "Hello World", never a negative price), and a hosted GraphQL endpoint is generated from them โ typed schema, introspection, GraphiQL when you open the URL in a browser. Mutations persist for real, no resolver wiring. This transcript ran against the shared demo project before publishing:
# arguments are honored (limit, sortBy, order, typed where filters):
curl https://mockbird.mockbird.workers.dev/m/demo/graphql \
-H 'content-type: application/json' \
-d '{"query":"{ products(limit: 2, sortBy: \"price\", order: \"desc\") { id name price inStock } productsCount }"}'
# โ 2 top-priced products + "productsCount": 30
# mutations take input and PERSIST:
# mutation { createProduct(input: { name: \"Guide check\", price: 12.5 }) { id } } โ id 31
# { product(id: 31) { name } } โ "Guide check"
# REST GET /m/demo/products/31 โ the same record โ one store, two protocols
# mutation { deleteProduct(id: 31) } โ true; product(id: 31) โ null
The URL is the whole point: paste it into a mobile app, a Go service, a teammate's Slack, a Playwright config on CI, a deployed preview's env var. Nobody installs anything.
| @graphql-tools/mock | Mockbird | Notes |
|---|---|---|
hand-written SDL + makeExecutableSchema | resources with typed fields | schema generated for you; or import OpenAPI / db.json / CSV |
| mock function per scalar/type ("Hello World" defaults) | 30+ field types, realistic seeds | no per-type mock code to maintain; no LLM involved |
MockStore + resolver wiring for state | mutations just persist | backed by a real database; REST and GraphQL share it; webhooks fire |
| in-memory, per-instance state | persistent across requests/machines | snapshots save/restore named data states |
| one store per test worker | snapshot pinning per request | X-Mockbird-Snapshot: empty โ parallel workers, zero races |
| list length via mock config (default: always 2) | real record counts | ?seed=200 at create time; productsCount is honest |
| error simulation via throwing resolvers | ?mock_status, ?mock_chaos, ?mock_delay | works on the GraphQL endpoint too โ test retry/timeout paths from any client |
| schema exists only in JS | hosted URL + exports | openapi.json, types.ts / Zod, Postman collection generated from the same source |
preserveResolvers partial mocking of a real schema | no equivalent | honest: mixing real resolvers with mocks is theirs alone |
This isn't either/or. Keep @graphql-tools/mock for fast, offline unit tests of resolver-level logic. Point everything that needs a network at a Mockbird URL: integration tests, deployed preview environments, the mobile team, demos, and any client that isn't JavaScript. One pattern we like: the component test suite runs against addMocksToSchema in-process, and the same queries run in CI against a Mockbird project pinned to a named snapshot โ ephemeral per-workflow-run mock APIs make that a 3-line job step.
# GraphiQL in your browser:
# https://mockbird.mockbird.workers.dev/m/demo/graphql
curl https://mockbird.mockbird.workers.dev/m/demo/graphql \
-H 'content-type: application/json' \
-d '{"query":"{ products(limit: 2) { id name price } }"}'
# same data over REST:
curl 'https://mockbird.mockbird.workers.dev/m/demo/products?limit=2'
Or create your own: one-click ecommerce project โ the GraphQL endpoint is at /m/<your-project>/graphql immediately.
| @graphql-tools/mock | Mockbird | |
|---|---|---|
| Price | free, open source (MIT) | free while in beta |
| Where the mock lives | inside your JS process | hosted URL, any client |
| Schema shapes | any SDL โ unions, interfaces, custom scalars, arbitrary nesting | generated from resources (flat-ish types + relations); no SDL import |
| Default data | "Hello World", random floats, lists of 2 | realistic seeded records |
| Stateful mutations | DIY resolver + store wiring, in-memory | built in, persistent |
| Custom resolver logic | arbitrary JS โ anything you can code | fixed semantics + custom routes with templating |
| Partial mocking of a real schema | yes (preserveResolvers) | no |
| Works offline / zero latency | yes | no |
| Request caps | none | 10,000 req/project/day free |
| Non-JS clients, teammates, CI-vs-preview | no (unless you host a server yourself) | the whole point |
| Maintenance | actively maintained (The Guild) | actively developed |
Written by the Mockbird maker โ bias disclosed. Where @graphql-tools/mock genuinely wins: it mocks your exact SDL including unions, interfaces, and custom scalars โ Mockbird cannot host an arbitrary hand-written schema shape today; preserveResolvers lets you mock only part of a real schema, which has no equivalent here; resolver mocks are arbitrary JavaScript; and it runs offline with zero network latency and no caps. Every claim above was verified by us in August 2026: the "Hello World" / negative-price / lists-of-2 / arguments-ignored transcripts come from running v9.1.13 locally (graphql 16.x), the store-wiring gotcha from our own working repro, the download count from npm's public API for the Aug 19โ25, 2026 week (1,361,661), and the Mockbird transcript from the live demo project before publishing. If any of this changes, we'll update the page.
Full GraphQL reference in the docs. More guides: free GraphQL mock API ยท graphql-faker alternative ยท json-graphql-server alternative ยท MSW alternative ยท deterministic test data ยท free mock API tools compared. Create your API โ