GUIDE / api
How to Test GraphQL APIs
Learn how to test GraphQL APIs: queries, mutations, schema validation, error handling, Postman tips, subscriptions, and introspection risks.
GraphQL rarely fails with a clean REST-style 404; it often returns partial data beside errors while resolvers and auth disagree field by field. How to test GraphQL APIs means checking schema contracts, query shape, authorization per field, and that response model, not only status codes on a single URL.
This guide covers GraphQL testing fundamentals, differences from REST, how to test queries and mutations, schema validation, error handling, Postman workflows, introspection risks, subscriptions, performance notes, security checks, worked examples, and common mistakes.
GraphQL Testing Fundamentals
A typical GraphQL HTTP request looks like this:
POST /graphql
Content-Type: application/json
Authorization: Bearer <token>
{
"query": "query User($id: ID!) { user(id: $id) { id name email } }",
"variables": { "id": "42" },
"operationName": "User"
}
A successful response might be:
{
"data": {
"user": {
"id": "42",
"name": "Asha",
"email": "asha@example.com"
}
}
}
A failed or partial response might include:
{
"data": {
"user": null
},
"errors": [
{
"message": "Not authorized to field email",
"path": ["user", "email"],
"extensions": { "code": "FORBIDDEN" }
}
]
}
Your assertions must cover transport, GraphQL operation outcomes, and business side effects.
How GraphQL Testing Differs From REST Testing
| Dimension | REST | GraphQL |
|---|---|---|
| Endpoint model | Many resource URLs | Often one /graphql endpoint |
| Client selects fields | Limited by resource representation | Client chooses fields per operation |
| Success signal | Primarily HTTP status families | HTTP status plus data / errors |
| Not found behavior | Often HTTP 404 | Often null field + error object |
| Documentation | OpenAPI common | Schema SDL and introspection |
| Over-fetch risk | Server-defined payloads | Client can request expensive graphs |
| Mutation style | Verbs via HTTP methods | Explicit mutation operations |
REST knowledge still helps. Auth headers, TLS, rate limits, and idempotency still matter. But if you only assert HTTP 200, you will miss GraphQL failures hiding in the body. Revisit REST API status codes so you know when HTTP status still matters at the gateway layer, then add GraphQL-specific assertions.
What to Test in a GraphQL API
A practical coverage map:
- Schema contract: types, fields, nullability, enums, deprecations.
- Queries: happy paths, filters, pagination, empty results.
- Mutations: create, update, delete, domain validation, side effects.
- AuthN and AuthZ: anonymous, wrong role, field-level permissions.
- Errors: validation errors, resolver errors, partial responses.
- Performance: expensive queries, depth and complexity limits.
- Security: injection-style inputs, batching abuse, introspection exposure.
- Subscriptions: stream auth, payload shape, fan-out filters.
- Regression: operations your real clients actually send.
If you need a broader API testing foundation first, use the API testing tutorial and then specialize with this guide.
Step-by-Step: How to Test GraphQL Queries
Step 1: Identify Client Operations
Do not invent random queries only from the full schema. Start from real client operations:
- frontend feature queries
- mobile operations
- partner integrations
- admin console operations
Real operations reveal the fields and arguments that matter.
Step 2: Build a Golden Operation Set
Create a suite of named operations with variables. Keep them readable.
query GetOrder($id: ID!) {
order(id: $id) {
id
status
totalCents
items {
sku
quantity
}
}
}
Step 3: Assert Data Shape and Values
Check:
errorsis absent or empty on full success.- Required fields are present.
- Types are correct.
- Nested lists have expected counts for the fixture.
- Unknown ids return the designed null/error behavior.
Step 4: Assert Authorization Around Fields
Field-level authorization is a GraphQL hallmark. A user may read order.status but not order.internalCost.
Test both operation access and field access.
Step 5: Test Pagination and Filters
For list queries, cover:
- first page
- middle page
- empty page
- invalid cursor
- sort order stability
- filter combinations
Cursor bugs are common and user-visible.
Step-by-Step: How to Test GraphQL Mutations
Mutations change state. Treat them like write APIs.
Example Mutation
mutation CreateOrder($input: CreateOrderInput!) {
createOrder(input: $input) {
id
status
totalCents
}
}
Mutation Test Matrix
| Case | Intent | Expected outcome |
|---|---|---|
| Valid create | Happy path | data.createOrder populated, no errors, record exists |
| Missing required input | Validation | GraphQL validation or domain error |
| Invalid quantity | Domain rule | Stable error code, no partial create |
| Anonymous user | AuthN | Unauthorized error |
| Viewer role | AuthZ | Forbidden error |
| Duplicate idempotency key | Safety | Same order returned or conflict policy honored |
| Downstream payment fail | Resilience | Controlled error, compensating state correct |
Always verify side effects with a follow-up query or database check in lower environments.
Schema Validation for Testers
The schema is the contract.
What to review
- Nullable vs non-null fields.
- Enum completeness.
- Deprecated fields still used by clients.
- Argument nullability.
- Input object required fields.
- Breaking changes between releases.
Breaking vs non-breaking changes
| Change | Usually breaking? | Tester action |
|---|---|---|
| Remove field | Yes | Fail release if clients still query it |
| Rename field | Yes | Require migration plan |
| Add optional field | No | Add regression coverage if clients adopt it |
| Make nullable field non-null | Risk | Verify all resolvers can guarantee values |
| Make non-null field nullable | Often yes for generated clients | Coordinate consumer updates |
| Add enum value | Sometimes | Check client exhaustive switches |
Schema diffing in CI is one of the highest value GraphQL quality practices.
Validating GraphQL Errors
Do not only assert that an error exists. Assert the quality of the error contract.
Useful checks:
messageis understandable and non-leaky.pathpoints to the failing field when relevant.extensions.codeis stable for client branching.- Production errors do not include stack traces.
- Partial data behavior matches design.
- Validation errors differ from runtime authorization errors in a documented way.
Example assertion style:
expect(response.status).toBe(200);
expect(response.body.errors[0].extensions.code).toBe("FORBIDDEN");
expect(response.body.data.user).toBeNull();
expect(JSON.stringify(response.body)).not.toMatch(/SELECT |stack|password/i);
GraphQL API Testing With Postman
Postman is a strong exploratory and collection tool for GraphQL.
Practical setup:
- Create a request to the GraphQL endpoint.
- Use the GraphQL body mode when available.
- Store tokens and base URLs in environments.
- Keep operations in a collection per feature.
- Write tests for
dataanderrors. - Use collection variables for ids created by mutations.
- Run the collection in CI with Newman if that fits your pipeline.
Example Postman-style test script:
pm.test("no GraphQL errors", function () {
const json = pm.response.json();
pm.expect(json.errors).to.be.undefined;
});
pm.test("order status is PLACED", function () {
const json = pm.response.json();
pm.expect(json.data.createOrder.status).to.eql("PLACED");
});
Postman will not replace schema diff automation or contract workflows by itself, but it is excellent for human-driven GraphQL API testing with Postman collections.
Introspection Testing Risks
Introspection lets clients ask the server for its schema. That is wonderful in development and staging. In production, unrestricted introspection can reveal:
- undocumented admin mutations
- internal field names
- argument structures useful to attackers
- operation surface area you thought was private
Tester checklist:
- Is introspection disabled in production?
- If enabled, is it limited to authorized operators?
- Do error messages or playgrounds leak schema details anyway?
- Are staging credentials and production credentials strictly separated?
Treat open production introspection as a security finding unless product and security explicitly accept the risk.
Testing GraphQL Subscriptions
Subscriptions add transport complexity, often over WebSockets.
What to validate:
- Connection authentication and authorization.
- Subscription start payload and acknowledgements.
- Event shape against schema.
- Filtering so users only receive their events.
- Behavior under reconnect and duplicate delivery assumptions.
- Unsubscribe and server-side cleanup.
- Backpressure or rate controls if documented.
Example risk: a subscription for orderUpdated(orgId) that forgets to verify the requester belongs to orgId. That is a data leak. Build explicit negative tests for cross-tenant subscription attempts.
Authorization Patterns to Probe
GraphQL authorization bugs are common because resolvers are easy to implement incompletely.
Probe these:
- Operation blocked but nested type still reachable another way.
- List endpoints returning foreign tenant ids.
- Mutations authorized while follow-up queries are not, or the reverse.
- Field resolvers that skip auth when parent objects already loaded data.
- Batch loaders that overfetch restricted entities and rely on later filtering.
Pair this section with API authentication testing for token lifecycle concerns, then specialize for field-level GraphQL rules.
Performance and Abuse Cases
GraphQL’s flexibility can be abused.
Test ideas:
- Deeply nested queries beyond expected depth.
- Wide queries requesting huge object graphs.
- Alias batching that multiplies expensive resolvers.
- N+1 patterns hidden behind list fields.
- Large string inputs in filters and mutations.
- Complex queries from authenticated low-trust roles.
Quality expectations often include:
- max query depth
- complexity costing
- timeouts
- persisted queries for production clients
- rate limits
If limits exist, verify both enforcement and clear error behavior when exceeded.
Contract Testing and GraphQL
Consumer-driven contracts are still valuable when multiple clients depend on stable operations. The contract may capture an operation string, variables, and expected response fragments rather than REST paths only.
For microservices that expose GraphQL or hybrid APIs, combine schema checks with contract testing with Pact where independent deployability matters.
Worked Example: Order Feature Test Pack
Requirement sketch:
- Buyers can fetch their orders.
- Buyers can create an order with one or more items.
- Buyers cannot see other buyers’ orders.
- Admins can see all orders.
internalNotesis admin-only.- Invalid quantities are rejected.
Suggested operations:
- Buyer queries own order: success.
- Buyer queries foreign order: null/forbidden as designed.
- Buyer requests
internalNotes: forbidden or field omitted by schema/auth. - Admin requests
internalNotes: success. - Create order with valid items: success and persisted.
- Create order with quantity 0: domain error, no row.
- Create order while unauthenticated: unauthorized.
- List orders pagination: stable ordering.
- Deprecated field still works until removal date.
- Production introspection restricted.
This pack already exceeds a shallow “GraphQL 200 OK” smoke test.
Tooling Landscape for GraphQL QA
| Approach | Strength | Watch-out |
|---|---|---|
| Postman / Insomnia | Fast exploration | Easy to skip schema CI |
| Playwright/Cypress with network stubs | End-to-end UI confidence | May not cover API-only clients |
| Schema diff in CI | Catches breaking changes | Needs consumer impact analysis |
| Contract tests | Protects specific consumers | Must not over-specify |
| Unit tests for resolvers | Fast logic checks | Misses wiring and auth integration |
| Load tools with GraphQL bodies | Capacity insight | Operation realism matters |
Choose based on risk. A public GraphQL API needs stronger security and complexity testing than an internal admin graph with few clients.
Common Mistakes When Testing GraphQL APIs
Mistake 1: Asserting Only HTTP Status
HTTP 200 with errors is still a failure for many client operations. Assert data and errors explicitly.
Mistake 2: Testing the Whole Schema Randomly
Random field crawling finds some issues, but risk-based operation packs tied to real clients find better defects.
Mistake 3: Ignoring Field-Level Authorization
Operation-level checks are not enough when sensitive fields sit beside public fields.
Mistake 4: No Mutation Side-Effect Verification
A mutation can return a plausible object while failing to persist, double-writing, or partially updating related records.
Mistake 5: Leaving Introspection Open Everywhere
Dev convenience is not a production requirement. Verify environment-specific controls.
Mistake 6: Forgetting Error Contract Stability
If mobile clients branch on extensions.code, changing codes is a breaking change.
Mistake 7: Skipping Subscription Negative Tests
Streaming endpoints need the same tenant isolation rigor as queries.
Mistake 8: No Complexity Limits Testing
If product claims protection against expensive queries, prove those protections with tests.
Practical Workflow for a New GraphQL Feature
- Read the schema diff and client operation documents.
- List queries, mutations, and subscriptions touched.
- Define auth roles and field restrictions.
- Prepare fixtures and tenant-separated test data.
- Write golden success operations.
- Add validation and authorization negatives.
- Assert error shapes and side effects.
- Check pagination and filtering edges.
- Run complexity and introspection checks if relevant.
- Add CI coverage for stable high-value operations.
- Explore once with hostile or weird queries.
- Record any new contract obligations for consumers.
Sample End-to-End Assertion Helper
function assertGraphqlSuccess(body, pathChecks = {}) {
if (body.errors?.length) {
throw new Error(`Unexpected GraphQL errors: ${JSON.stringify(body.errors)}`);
}
for (const [path, expected] of Object.entries(pathChecks)) {
const actual = path.split(".").reduce((acc, key) => acc?.[key], body.data);
if (actual !== expected) {
throw new Error(`Expected data.${path} to be ${expected}, got ${actual}`);
}
}
}
function assertGraphqlErrorCode(body, code) {
if (!body.errors?.length) {
throw new Error("Expected GraphQL errors, got none");
}
const codes = body.errors.map((e) => e.extensions?.code);
if (!codes.includes(code)) {
throw new Error(`Expected error code ${code}, got ${JSON.stringify(codes)}`);
}
}
Helpers like these keep suites consistent and readable.
Release Checklist
- Schema changes reviewed for breakages.
- Client golden operations updated.
- Query success and empty-result paths covered.
- Mutation side effects verified.
- AuthN and AuthZ cases covered at operation and field levels.
- Error codes and messages reviewed for safety and stability.
- Introspection policy verified per environment.
- Complexity/depth limits verified if advertised.
- Subscription auth and isolation tested when applicable.
- Monitoring can detect spikes in GraphQL errors, not only HTTP 5xx.
Nullability Bugs That Escape Casual Testing
Nullability is a frequent GraphQL defect class. A schema may mark a field non-null while the resolver sometimes returns null, producing runtime GraphQL errors that only appear for certain data states.
Tester habits:
- Prefer fixtures where optional relations are missing, not only fully populated graphs.
- Include soft-deleted parents and orphaned children when the product allows them.
- Check list fields that should return
[]rather than null. - Verify client handling when a non-null field error nulls out a parent object according to GraphQL error propagation rules.
These cases are easy to miss if every test record is a perfect happy-path seed.
Federation and Multi-Graph Testing Notes
Large organizations often split GraphQL across subgraphs or federated gateways. That architecture creates extra test layers:
- subgraph schema compatibility with the supergraph
- entity reference resolvers across services
- gateway auth propagation to subgraphs
- partial outage behavior when one subgraph is down
- field ownership changes during service migrations
Suggested checks:
- Composition or gateway build succeeds in CI after schema changes.
- Critical federated operations still resolve after a subgraph deploy.
- Auth headers required by subgraphs are forwarded correctly.
- A subgraph failure returns controlled errors rather than cascading timeouts for unrelated fields when that is the design.
If your company is moving from a monolith graph to federated graphs, freeze a golden operation pack first. That pack becomes the migration safety net.
N+1 Problems and Observability for Testers
GraphQL quality is not only functional correctness. It is also whether a single client query triggers an explosion of database or service calls.
What N+1 looks like
A query requests 50 orders and each order resolves customer with a separate lookup. One GraphQL operation becomes 1 + 50 downstream calls. The response may still be correct and still fail production under load.
How testers contribute
- Ask for resolver-level tracing in non-prod.
- Compare query plans or logs before and after list field changes.
- Add at least one list-heavy performance smoke case for critical operations.
- Verify batching or DataLoader-style caching is active where claimed.
- File defects with evidence: operation document, response time, and downstream call count if available.
Functional GraphQL testing that ignores N+1 will bless slow features.
Persisted Queries and Client Allow Lists
Many production GraphQL deployments prefer persisted queries or allow-listed operations so clients send a hash instead of arbitrary query text.
Tester checks:
- Unknown operation ids are rejected.
- Deprecated operation ids are retired on a schedule.
- Allow list updates are covered by regression packs.
- Development still has a controlled way to explore new operations.
- Error messages for unknown operations do not overshare internal naming.
Persisted queries reduce attack surface and make golden operation testing even more valuable because production traffic maps to known documents.
File Uploads and Multipart GraphQL
If your API supports uploads through multipart GraphQL requests, add cases for:
- valid image or document upload
- unsupported MIME type
- oversized file
- missing file part
- auth failure during upload
- malware scanning hooks if present
- partial failure when one file in a batch is invalid
Upload paths often bypass the clean JSON-only assumptions in the rest of your suite. Give them explicit coverage.
Collaboration Checklist for Frontend and QA
GraphQL quality improves when frontend engineers and QA share operation ownership.
Working agreement ideas:
- Every new UI feature opens with the exact GraphQL operations it will send.
- QA reviews those operations for over-fetching and missing auth assumptions.
- Schema changes require a consumer impact note.
- Deprecated fields have removal dates and search results across clients.
- Error codes used by UI branches are documented and tested.
- Storybook or UI tests can mock the same operation names the backend suite uses.
This reduces “works in GraphiQL, fails in the app” surprises.
Final Takeaways
Learning how to test GraphQL APIs means learning to test operations, schemas, resolvers, and error semantics as one system. The teams that succeed treat the schema as a living contract, assert data and errors with equal seriousness, and put authorization at the center of the suite.
Use REST instincts for transport and security hygiene. Add GraphQL-native thinking for field selection, partial responses, and schema evolution. Build operation packs from real clients, automate the stable ones, and explore the flexible query surface for abuse and performance risks. Watch N+1 behavior, persisted query policies, and upload paths so correctness does not hide operational failure.
When you want deliberate practice, sign up at QABattle and run API track battles that force you to justify expected responses before you inspect them. That habit transfers directly to better GraphQL test design.
FAQ
Questions testers ask
How do you test GraphQL queries and mutations?
Test queries and mutations by sending operations to the GraphQL endpoint with controlled variables, then asserting HTTP transport behavior, the data shape, field values, and the errors array. Cover happy paths, authorization, invalid arguments, partial responses, and side effects for mutations such as creates, updates, and deletes.
How is GraphQL testing different from REST testing?
REST testing centers on many resource URLs and HTTP status codes. GraphQL usually exposes one endpoint where clients choose fields, so testing focuses on operations, schemas, resolvers, variable validation, and the errors array. HTTP 200 can still contain GraphQL errors, which changes assertion strategy compared with classic REST status checks.
How do you validate GraphQL schema and errors?
Validate schema by checking types, required fields, deprecations, and breaking changes between versions. Validate errors by asserting error message stability where required, extensions codes, path information, and that sensitive internals are not leaked. Confirm clients can distinguish partial data with errors from full failures.
Can you test GraphQL APIs with Postman?
Yes. Postman supports GraphQL bodies, variables, and collections. You can build operations, attach auth headers, and write scripts that assert data fields and errors. Postman is strong for exploration and regression collections. Pair it with schema checks and automated CI tests for larger systems.
What are GraphQL introspection testing risks?
Introspection can expose the full schema, including internal or admin operations, to anyone who can query it. That is useful in development and dangerous in production if left open without controls. Testers should verify whether introspection is disabled or restricted in production and whether schema information leaks through other channels.
How do you test GraphQL subscriptions?
Test subscriptions by validating connection auth, subscription initialization, event payloads, filtering, reconnect behavior, and unsubscribe cleanup. Use tools that support WebSockets or server-sent events depending on the transport. Assert that clients only receive authorized events and that payloads match the schema.
RELATED GUIDES
Continue the route
API Testing Tutorial: A Beginner's Complete Guide
API testing tutorial for beginners: learn REST API checks, CRUD test cases, tools, status codes, and a practical checklist for reliable API quality.
REST API Status Codes: The Complete Reference for Testers
Master REST API status codes with a complete tester cheat sheet for 2xx, 4xx, and 5xx responses, error validation, and practical test design.
API Authentication Testing: OAuth, JWT, and API Keys
Learn API authentication testing for OAuth 2.0, JWT expiry and refresh flows, API keys vs bearer tokens, and broken authentication test cases.
Contract Testing with Pact: Consumer-Driven Contracts
Learn contract testing with Pact, consumer-driven contracts, provider verification, Pact Broker flow, and how it differs from Postman and integration tests.