GraphQL Tester

A free online GraphQL tester for sending queries and mutations to any public GraphQL endpoint. The method is already set to POST and the body is pre-filled with a valid GraphQL payload — paste your endpoint URL, edit the query string, add an auth header if you need one, and press Send.

The body is JSON, not raw GraphQL. It needs a "query" string and may also carry "variables" and "operationName".

GraphQL servers reject bodies sent without Content-Type: application/json, so that header is set for you. Clear its name to drop it.

Sent as Authorization: Bearer <token> — the usual scheme for GraphQL APIs.

Base64-encoded and sent as Authorization: Basic.

Credentials are used only to sign this request. They are never logged or stored.

Rarely needed for GraphQL, but some gateways expect an API key or tenant id in the query string.

No request sent yet.
Send a query and the GraphQL response will appear here.

Try a public GraphQL API: or press Ctrl+Enter to send.

How a GraphQL request actually travels over HTTP

GraphQL is a query language and an execution engine, not a transport. When you use it over the web it rides on ordinary HTTP, and the convention is remarkably narrow: one endpoint, one method, one content type. Every operation — reading a single field or writing a dozen records — goes to the same URL, usually something like https://api.example.com/graphql, as a POST carrying the header Content-Type: application/json.

The body is where newcomers most often go wrong. It is not raw GraphQL text. It is a JSON object wrapping your GraphQL document as a string value:

FieldRequiredWhat it holds
queryYesThe operation document as a string — used for mutations too, despite the name
variablesNoA JSON object of typed values referenced by $name in the document
operationNameNoWhich named operation to execute when the document defines more than one

That is why the Body tab above starts with {"query":"{ __typename }"}. It is the smallest complete GraphQL request there is: __typename is a meta-field every schema exposes, so it succeeds against any conforming server and is a reliable way to confirm you have the right URL and the right credentials before you debug anything more elaborate. Because the escaping is fiddly — your GraphQL is a string inside JSON, so every quote in it needs a backslash — the Format JSON button is genuinely useful for spotting a malformed payload before you blame the server.

Queries, mutations and why the field is still called "query"

A query reads data and should have no side effects; a mutation writes. Both are sent in the same query field of the JSON body, which trips people up constantly. To send a mutation you simply write a document that begins with the mutation keyword: {"query":"mutation { createOrder(quantity: 2) { id status } }"}. There is no separate transport field, no different endpoint and no different HTTP method.

The other structural difference from a REST API is that you decide the shape of the response. Because you enumerate the fields you want, adding one more field to your selection set is the difference between a lean payload and an expensive one — and the size counter beside the response makes that trade-off visible immediately. Requesting nested relations that each fan out to more rows is the classic way to make a fast GraphQL API slow, and comparing the reported response time between two selection sets is the cheapest profiling you will ever do.

Using variables instead of string interpolation

Never build a query by concatenating values into the document text. Declare typed variables in the operation signature — query GetUser($id: ID!) { user(id: $id) { name email } } — and pass their values in the sibling variables object. The server validates them against the declared types before execution, which catches mistakes early, keeps the document itself constant so the server can cache its parsed form, and removes any possibility of injection through the query string. The second sample button above sends exactly this shape if you would like to see it rendered.

Reading a GraphQL response: the 200 OK trap

This is the single most important thing to understand when testing GraphQL, and it is the opposite of the habit a REST API teaches you. GraphQL reports failures inside the response body, not in the HTTP status line. A query that references a field which does not exist, violates an authorisation rule or blows up in a resolver will still very often come back as 200 OK. If you write a client that only checks the status code, it will cheerfully treat total failure as success.

What you check instead is the errors array. A GraphQL response contains data, errors, or both. Each error entry carries a human-readable message, a path naming the exact field that failed, locations pointing into your document, and usually an extensions object holding a machine-readable code such as UNAUTHENTICATED or GRAPHQL_VALIDATION_FAILED. Partial success is normal and designed-in: three of four requested fields may resolve while the fourth returns null with an accompanying error. The syntax-highlighted response pane above makes that structure easy to scan.

HTTP status codes have not disappeared entirely, though, and when one does appear it tells you something specific: the failure happened before GraphQL execution began. A 400 usually means malformed JSON or a missing query field. A 401 means a gateway rejected your credentials without consulting the schema — if you authenticate with a bearer token, run it through the JWT decoder to check the exp claim before assuming the schema is at fault. A 405 almost always means the request went out as GET when the server only accepts POST. The full status code reference covers the rest.

Introspection, headers and practical debugging

GraphQL schemas describe themselves. Sending {"query":"{ __schema { queryType { name } types { name kind } } }"} asks the server to list its own types, which is how editors and documentation browsers discover what an API offers. Many production deployments switch introspection off on purpose to reduce their attack surface, so an error saying __schema is unavailable is a deliberate policy rather than a broken request. The lighter __typename meta-field is never disabled and remains the best connectivity check.

Beyond Content-Type and Authorization, GraphQL gateways lean on custom headers more than most REST APIs do — tenant selectors, schema version pins, persisted-query identifiers and client name and version headers used for per-client rate limiting are all common. Add them in the Headers tab. Our HTTP headers guide explains the standard ones, and the HTTP methods reference is worth a look if you want to understand why GraphQL's collapse of every operation onto a single POST costs you HTTP-level caching, which is the main trade-off against a REST API design.

When a query finally works, Copy as cURL turns the whole thing — endpoint, headers, auth and escaped body — into a shell command you can drop into a script, a CI check or a bug report. It is also the clearest way to hand a reproducible failing case to whoever maintains the schema.

Requests are sent from our proxy rather than your machine, so loopback and private network addresses are blocked. Put a development GraphQL server behind a public URL with a tunnelling service before testing it here.

Related tools and references

GraphQL testing FAQ

How do I send a GraphQL request over HTTP?

POST to the single GraphQL endpoint with Content-Type: application/json and a JSON body containing a required "query" string plus optional "variables" and "operationName". The query field carries your GraphQL document as text, whether that document is a query or a mutation.

Why does my request return 200 OK but no data?

GraphQL reports application-level problems in the body rather than the status line, so a failed operation still returns 200. Read the errors array: each entry has a message, the path to the field that failed and usually an extensions.code. A null data beside a populated errors array is the normal shape of a failure.

How do I pass variables?

Declare them in the signature — query GetUser($id: ID!) { user(id: $id) { name } } — and put their values in a sibling "variables" object such as {"id":"42"}. Never interpolate values into the query string; variables are typed, server-validated and injection-safe.

Can I run an introspection query?

Yes, when the server permits it. Send {"query":"{ __schema { types { name kind } } }"}. Many production servers disable introspection deliberately and will answer with an error saying __schema is unavailable — a configuration choice, not a problem with your request.

Do GraphQL requests have to be POST?

Not strictly. Some servers also accept a GET with the query URL-encoded into a parameter, which makes the response cacheable and suits persisted queries. POST with a JSON body is the near-universal default because it has no URL length limit and every server supports it, so start there and only try GET if the API documents it.

How do I send a mutation?

In the same "query" field, with a document that starts with the mutation keyword: {"query":"mutation { createOrder(quantity: 2) { id status } }"}. There is no separate field, endpoint or HTTP method for mutations.