GraphQL Formatter
Format or minify a GraphQL query, mutation, fragment or SDL schema with a real parser — syntax errors show the exact line and column, nothing invented.
GraphQL
query, mutation, fragment or SDLFormatted
256 B → 358 Bquery GetUser($id: ID!, $first: Int = 10, $withPosts: Boolean!) {
user(id: $id) {
...UserFields
posts(first: $first) @include(if: $withPosts) {
edges {
node {
id
title
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
fragment UserFields on User {
id
name
email
}
Document
- Operations
- 1
- Fragments
- 1
- Type definitions
- 0
- Field selections
- 12
- Max depth
- 5
- Size change
- 40%
1 comment was dropped. GraphQL comments are not part of the syntax tree, so no printer can place them back.
Parsed with a real GraphQL lexer in your browser — queries and schemas are never uploaded. Max depth is the number a server-side depth limit is set against.
About the GraphQL Formatter
This GraphQL formatter parses your document and prints it back, so queries, mutations, fragments and SDL all come out in the standard two-space layout — or on a single line if you want it minified. If the document does not parse, you get the message, the line and the column instead of a guess.
It is a real lexer and parser rather than a regex reformatter, and that distinction is load-bearing in GraphQL specifically. Commas are insignificant, so a formatter cannot use them as landmarks. Comments can appear between any two tokens. Block strings can contain braces, quotes and # characters that a text-level formatter will cheerfully treat as syntax and destroy. Building the syntax tree and printing it back is the only approach where the output is guaranteed to mean what the input meant.
Minifying strips every optional character. It also inserts a space in the one place it is needed — between two names that would otherwise merge into one — so `{a{b}c}` stays valid while `truefalse` never happens. The output parses back to the same tree, which the test suite asserts on every construct the parser supports.
Alongside the output you get the document's shape: how many operations and fragments, how many field selections, and the maximum selection depth. That last number is the one a server-side depth limit is configured against, and it is the cheapest defence against a malicious nested query.
- Formats queries, mutations, subscriptions, fragments and full SDL schemas
- Minify mode that removes every optional character and still round-trips
- Syntax errors with a line and column, naming what was expected
- Descriptions preserved, as block strings when they span lines and quoted strings when they do not
- Handles variables, directives, aliases, inline fragments, enum values and input objects
- Reports operation, fragment, field and maximum-depth counts
- Says how many comments it dropped, rather than losing them silently
- 2 or 4-space indent, chosen from the format panel
How to use it
- Paste a GraphQL query, mutation, fragment or schema into the left pane.
- Choose Format for readable output or Minify to send it over the wire.
- If it will not parse, the error names the line and column and what was expected there.
- Check Max depth against whatever your server enforces — a query above the limit is rejected at runtime, not at build time.
- Copy the result.
Real-world use cases
Frontend developers (Apollo, urql, Relay)
Clean up a query copied out of a network tab, a minified bundle or a teammate's Slack message before it goes into a component or a .graphql file.
Backend & API developers
Format SDL type definitions during a schema review, or check that a federation directive like @key parses the way it is meant to before it ships.
QA & API testers
Confirm a query is syntactically valid, with the exact line and column of any typo, before pasting it into GraphiQL, Postman or a test fixture.
Security-conscious backend engineers
Check a suspicious or user-submitted query's Max depth against the limit the server enforces, without waiting to find out at runtime that it exceeds it.
Platform engineers running persisted queries
Minify a query into the compact, whitespace-free form used as a persisted-query cache key, and confirm it still parses to the identical document.
Examples
A compacted query, formatted
query Hero{hero{name friends{name}}}query Hero {
hero {
name
friends {
name
}
}
}The shape most people paste in, straight out of a network tab or a log line.
Minified for the wire
query GetUser($id: ID!) {
user(id: $id) {
name
}
}query GetUser($id:ID!){user(id:$id){name}}Commas, newlines and every optional space removed. This is the form worth sending in a persisted-query cache key.
SDL with an implements clause
type User implements Node&Entity @key(fields:"id"){id:ID! posts(first:Int=10):[Post!]!}type User implements Node & Entity @key(fields: "id") {
id: ID!
posts(first: Int = 10): [Post!]!
}Federation directives, default argument values and non-null list types all survive the round trip.
A syntax error, located
{ user(id: 1 }Unclosed argument list — expected a ')'. Line 1, column 14.
A closing brace where a parenthesis belongs is the most common GraphQL typo, and the message names it rather than reporting an unexpected token.
Common errors
| Message | Cause | Fix |
|---|---|---|
| Syntax Error: Expected Name, found } | Usually a trailing comma or an empty selection set. `{ user { } }` is not valid — a selection set must select something. | This tool reports 'A selection set cannot be empty' for that case specifically. Remove the empty braces or add a field. |
| Cannot query field X on type Y | A schema error, not a syntax error. The document parses; the field just does not exist on that type. | Nothing this tool can check — it has no schema. Use your server's introspection or a schema-aware editor extension. |
| Variable $x is never used | A variable declared in the operation and not referenced in the selection set. | Also not a syntax error, so it formats cleanly. It is a validation rule your server enforces at execution time. |
| Unterminated string | A quote was never closed, or a multi-line string used single quotes instead of triple quotes. | GraphQL has no multi-line single-quoted string. Use a block string with triple quotes. |
| Query depth limit exceeded | The server enforces a maximum selection depth and this query is past it — often via a relation that loops back on itself. | Check the Max depth figure shown above. Inline fragments do not add depth; nested field selections do. |
| My comments disappeared after formatting | GraphQL comments are not part of the syntax tree, so there is nowhere for a printer to put them back. | Expected behaviour, and the tool tells you how many it dropped. Put explanatory text in a description string, which is part of the document, rather than in a comment. |
Frequently asked questions
›Is my query or schema uploaded?
No. The lexer, parser and printer all run in your browser, so a query containing internal field names or a schema you have not shipped yet stays on your machine. There is no server involved at all, which you can verify by using the page offline.
›Why are my comments removed?
Because GraphQL comments are ignored tokens — the parser skips them exactly as it skips whitespace and commas, so they never reach the syntax tree and no printer can decide where to put them back. Every AST-based GraphQL formatter has this property. The tool counts them and tells you rather than letting you find out after pasting the output over your file. If the text is worth keeping, a description string is part of the document and survives.
›Is the minified output safe to send?
Yes. It removes only ignored tokens and inserts a space in the one position where two adjacent names would otherwise merge into a single name. The round trip is asserted in the test suite: every construct the parser handles is minified, re-parsed and re-formatted, and must produce the same result as formatting the original.
›What does Max depth actually count?
The deepest chain of nested field selections. `{ a }` is 1 and `{ a { b { c } } }` is 3. Inline fragments deliberately do not count, because they are a type condition rather than a level of data nesting, and that is how server-side depth limiters count too. It is the number to check a query against before finding out at runtime that it exceeds the limit.
›Does it validate against a schema?
No, and it says so rather than implying otherwise. Everything here is syntax: whether the document is well-formed GraphQL. Whether a field exists, a variable is used, or a fragment matches its type condition all require the schema, which a pasted query does not contain.
›Does it handle SDL as well as queries?
Yes — type, interface, union, enum, input, scalar, schema and directive definitions, plus every `extend` form. Descriptions are preserved, printed as block strings when they span lines and as quoted strings when they do not, which is the shorter and equivalent form.
›Why does formatting change my block string?
Block strings have specified value semantics: the common indentation of every line after the first is stripped, and leading and trailing blank lines are dropped. The formatter applies those rules and prints the resulting value, so an indented block in a schema comes back without its indentation. The string's value is unchanged — only its written form is.
›Can I choose the indent width?
Yes — 2 or 4 spaces, in the format panel next to the Format/Minify toggle. It only applies to Format mode, since Minify removes all optional whitespace regardless of the setting.
Last updated