Commas are whitespace in GraphQL, so indentation carries the meaning
GraphQL treats commas as insignificant. The grammar lists them alongside spaces and tabs, which is why {id name email} and {id, name, email} parse to the same document. Fields have no separator of their own and no terminator either. Nesting is the only thing telling you which selection belongs to which parent.
Strip the line breaks and the document still runs. It stops being readable. A query copied from the Network tab of your browser arrives as one long line, a query pulled out of a template literal arrives with the indentation of the JavaScript file rather than of the query, and a schema pasted from a chat message arrives with whatever the client did to it. This page rebuilds the shape.
The parsing here is a tokenizer, not a regular expression sweep. That distinction shows up the moment your document contains something with braces inside a string:
mutation{search(q:"a { b }" ,first:5){id title}}mutation {search(q: "a { b }", first: 5) {id
title}}The braces inside "a { b }" are string content, so they never touch the indent counter. Formatters built on find and replace break on that input, indenting the rest of the document one level too deep from the point the string appears.
What the printer changes, and what it leaves alone
Formatting is a rewrite of whitespace and punctuation only. No field is added, removed, renamed, or reordered, so the formatted document sends the same request as the one you pasted.
| You paste | You get | Reason |
|---|---|---|
user(id:$id,first:10) | user(id: $id, first: 10) | Arguments stay on one line with a comma between them, which is how nearly every GraphQL codebase writes them. |
...on Admin | ... on Admin | An inline fragment gets a space, a named spread like ...UserParts does not. |
id ,, name , | id and name on two lines | Stray commas between fields are whitespace, so they are dropped rather than preserved. |
posts: [Post!]! | posts: [Post!]! | Type expressions never break across lines. Non null and list markers sit tight against the type. |
A """description""" | Re-indented to the field below it | Block string content is dedented to the shortest common indent, then rewritten at the depth of the thing it documents. |
# a note | Its own line, or gone | Comments are kept on their own line while the checkbox is ticked. Compact output drops them, since the wire has no use for them. |
@include(if:$x) | @include(if: $x) | Directives print after the field they attach to, arguments spaced the same as any other argument list. |
Max depth is the number your server watches
Public GraphQL endpoints reject queries past a depth limit, usually somewhere between 7 and 15. The reason is cyclic types. A User has posts, a Post has an author, and that author has posts again, so a short document nests far enough to make the resolver walk a large chunk of the database. Depth limiting is the cheapest defence, which is why it is the one most servers ship.
The depth counter above reads the formatted output. Every opening brace adds a level, so an operation body starts at 1 and each nested selection set adds one more. Relay style connections inflate it fast, because edges and node each take a level before you reach a real field:
query { viewer { repositories(first: 10) { edges { node { issues(first: 5) {Six levels in, and the query has fetched nothing but wrappers. If an endpoint returns a depth error and the query looks short to you, format it here and read the counter before rewriting anything. Splitting one deep operation into two shallower ones is the usual fix.
Compact output is for the wire, not for your editor
Switch Output to Compact and the same document comes back on a single line with comments removed and spacing cut to the minimum the grammar accepts. Two situations call for it. Sending a query as a URL parameter on a GET request, where browsers and proxies start truncating somewhere around 2,000 characters. And registering a document in a persisted query store, where the hash has to match byte for byte across every client.
The size change counter reports the raw byte difference. Treat it as a rough figure rather than a saving. Every transport worth using applies gzip or brotli, and repeated indentation compresses close to nothing, so a 40 percent drop in raw bytes is often a couple of percent on the wire. Compact mode earns its place through length limits and hash stability, not through bandwidth.
One thing compact output keeps is block strings. A """ string preserves its own line breaks as content, so collapsing it would change the value being sent.
This page reads syntax, not your schema. Nothing here knows what types your API defines. A query asking for usre instead of user formats cleanly, because the misspelling is a valid field name as far as the grammar is concerned. The same goes for a wrong argument name, a variable declared and never used, or a fragment spread on a type it does not apply to. Those need a schema to catch, which means your IDE plugin, a GraphQL client with introspection loaded, or the server itself. Format here, validate there.
The errors this page does report
Structural problems surface with a line and column, and the Open that line button puts the cursor on them. Three account for most of what people paste:
- Unbalanced braces. The most common result of copying a query out of a larger file and stopping one line short. The reported position is the brace left open, not the end of the document, which is the useful half of the message.
- Unterminated string. A quote opened and never closed swallows the remainder of the document into a string. Ordinary strings cannot span a line break, so the error lands on the line where the quote sits.
- Unexpected character. Usually a smart quote from a document editor, or a stray backtick left behind when a query was lifted out of a template literal.
Load the missing brace sample to see the shape of the report.
When another page fits the job better
Reach for this one when you have a document in hand and want it readable. If you are going the other way, turning a JSON payload into type definitions, the GraphQL schema generator starts from the data. If what you have is the response rather than the request, a GraphQL reply is ordinary JSON under an outer data key, so the JSON beautifier or the API response formatter is the page that will parse it. Pasting a response here reports an unexpected character on the opening brace of the JSON object, since GraphQL has no syntax for a quoted key.
