← All guides

Formatting a GraphQL Query Without a Schema — What a Pretty-Printer Can and Can't Check

Re-indenting a GraphQL document is mostly a brace-counting problem: every { increases the indent level, every } decreases it, and a field goes on its own line. That's enough to turn a query pasted as one long line into something readable, without needing to know a single thing about the schema the query is actually written against.

Comments don't survive formatting

This is the one behavior worth knowing before you paste something real: the tokenizer treats # as "skip to end of line" and never emits a token for what follows. A formatted query comes back with correct indentation and zero comments — not comments moved to a different line, removed entirely. If you've annotated a query with # TODO: paginate this or # deprecated, remove after Q3, formatting it here will silently drop that line. This is a pretty-printer for the query structure, not a source-preserving reformatter — keep your commented, annotated version in your editor and treat this tool's output as what you paste into a GraphQL client, not what you commit back to your codebase.

What it checks vs. what it assumes is correct

The formatter validates exactly two things: that every { has a matching }, and every ( has a matching ). Everything else — field names that don't exist on the type, an argument that's missing, a variable referenced but never declared — passes through unformatted-but-unvalidated. A query that would be rejected instantly by your actual GraphQL server can still come out of this tool perfectly indented. That's the trade for not needing your schema as input: schema-aware validation (the kind a GraphQL IDE plugin does) requires the schema; structural pretty-printing doesn't.

Advertisement

Directives and block strings are recognized, not just tolerated

Two GraphQL-specific syntax forms get handled explicitly rather than falling out of generic tokenizing. Directives (@include(if: $showDetails)) attach directly to the field they follow instead of starting a new line, and triple-quoted block strings (used for multi-line description arguments) are parsed as one atomic token so a newline inside the quotes doesn't confuse the brace-depth tracker:

query GetUser($id: ID!, $showDetails: Boolean!) {
  user(id: $id) {
    name
    email @include(if: $showDetails)
    bio(format: MARKDOWN)
  }
}

Spacing rules inside arguments vs. inside the selection set

Commas behave differently depending on where they appear. Inside parentheses (an argument list), a comma gets a trailing space — (first: 10, after: $cursor). Outside parentheses, at the top level of a selection set, GraphQL doesn't actually require commas between fields at all (newlines serve that role), so the formatter doesn't try to normalize spacing there — each field lands on its own indented line regardless of how the input was separated.

Advertisement

Try the GraphQL Query Formatter