CodeToolProCodeToolPro
GitHub
Formatters·8 min read

GraphQL Formatter Guide: Format & Beautify GraphQL Queries

CodeToolPro Team·

GraphQL Formatter Guide: Format & Beautify GraphQL Queries

GraphQL operations are easy to write on one line and painful to read on one line. A GraphQL formatter rewrites a compact query, mutation, or subscription into a readable, indented tree so you can see the selection set at a glance. Try it with our GraphQL Formatter — it runs entirely in your browser, and no operation text ever leaves your machine.

What Is a GraphQL Formatter?

A GraphQL formatter is a tool that takes a GraphQL operation string and rewrites it with consistent indentation, turning a dense single line into a structure that reads like a tree. Unlike a strict parser, it does not need to build a typed AST to make the document readable — a lightweight, bracket-based re-indenter is enough for most human inspection tasks.

The core operations are:

  • Pretty-print — break the selection set across lines, adding 2 spaces of indentation per nesting level.
  • Normalize whitespace — collapse runs of spaces and trim trailing whitespace on every line.
  • Preserve semantics — field names, arguments, variables, and directives stay exactly as written.

Pretty-Print vs Minify

These are opposite layouts, and choosing the right one depends on where the query lives.

ModeResultUse case
PrettyNewlines + 2-space indentReading, code review, sharing in docs or tickets
MinifyOne line, no spacesEmbedding in a curl call, HTTP logs, or a single-line transport

Rule of thumb: pretty-print while reading and reviewing, keep it compact when pasting into a one-line transport like a URL, a log line, or a shell command.

How Bracket-Based Indentation Works

The formatter walks the string character by character. Three rules drive the layout:

  1. { increases the indent level and starts a new line.
  2. } decreases the indent level and closes on its own line.
  3. , (and any existing newline) starts a new line at the current indent.

Whitespace between tokens is collapsed to a single space — except it is dropped right after {, (, or another space, so user(id:$id){ stays tidy without inventing a space that could change meaning.

query GetUser($id: ID!) {
  user(id:$id) {
    name
    email
  }
}

Code Examples

JavaScript

The browser tool uses this exact function (copied from the live formatter):

function formatGraphQL(query) {
  const INDENT = "  ";
  let result = "";
  let indent = 0;
  let i = 0;
  const len = query.length;

  while (i < len) {
    const ch = query[i];
    if (ch === "{") {
      indent++;
      result += " {\n" + INDENT.repeat(indent);
      i++;
    } else if (ch === "}") {
      indent = Math.max(0, indent - 1);
      result += "\n" + INDENT.repeat(indent) + "}";
      i++;
    } else if (ch === "," || ch === "\n") {
      if (ch === ",") result += ",";
      i++;
      result += "\n" + INDENT.repeat(indent);
    } else if (ch === " " || ch === "\t") {
      const prev = result[result.length - 1];
      if (prev !== " " && prev !== "\n" && prev !== "{" && prev !== "(") {
        result += " ";
      }
      i++;
    } else {
      result += ch;
      i++;
    }
  }

  return result
    .split("\n")
    .map((line) => line.trimEnd())
    .join("\n")
    .trim();
}

Python

The same bracket-based algorithm, reimplemented in Python:

def format_graphql(query: str) -> str:
    INDENT = "  "
    result = ""
    indent = 0
    i = 0
    n = len(query)
    while i < n:
        ch = query[i]
        if ch == "{":
            indent += 1
            result += " {\n" + INDENT * indent
            i += 1
        elif ch == "}":
            indent = max(0, indent - 1)
            result += "\n" + INDENT * indent + "}"
            i += 1
        elif ch == "," or ch == "\n":
            if ch == ",":
                result += ","
            i += 1
            result += "\n" + INDENT * indent
        elif ch == " " or ch == "\t":
            prev = result[-1] if result else ""
            if prev not in (" ", "\n", "{", "("):
                result += " "
            i += 1
        else:
            result += ch
            i += 1
    return "\n".join(line.rstrip() for line in result.split("\n")).strip()

For production pipelines that must understand arguments, fragments, and directives, parse with a real GraphQL library (e.g. graphql in JS or graphql-core in Python) and re-print from the AST. The heuristic above is ideal for quick, ad-hoc inspection.

Common Pitfalls

  • Space-separated selections stay on one line. The formatter only breaks lines on , and {. A query written name email posts { … } keeps name email posts together — see the Hands-on section for proof.
  • Feeding already-formatted input leaves ragged output. Paste a multi-line, indented query and you get extra blank lines and double spaces, because the tool re-derives indentation from brackets rather than trusting your existing newlines.
  • It is not a validator. Bracket re-indentation never checks that fields exist on the schema or that variables are declared. Use a linter or the schema for that.
  • Arguments and directives are preserved verbatim. (id:$id) and @include(if: $x) are kept exactly as typed; the tool will not reorder or pretty-print inside them.

Hands-on: Tested with the Tool

I ran the live GraphQL Formatter (the same code shown above) on three real inputs. The outputs below are copied exactly from the tool.

Test 1 — the tool's default compact query

Input:

query GetUser($id: ID!){user(id:$id){name email posts{title content comments{body author{name}}}}}

Output:

query GetUser($id: ID!) {
  user(id:$id) {
    name email posts {
      title content comments {
        body author {
          name
        }
      }
    }
  }
}

Observation: the tree is now readable, but name email posts sit on a single line. That is expected — without commas, the re-indenter has no signal to split sibling selections, so it keeps them inline.

Test 2 — the same query with commas between fields

Input:

query GetUser($id: ID!){user(id:$id){name,email,posts{title,content,comments{body,author{name}}}}}

Output:

query GetUser($id: ID!) {
  user(id:$id) {
    name,
    email,
    posts {
      title,
      content,
      comments {
        body,
        author {
          name
        }
      }
    }
  }
}

Observation: every field now gets its own line because commas trigger a line break. If you want one field per line, add commas — both styles are valid GraphQL.

Test 3 — a mutation with a variable

Input:

mutation CreatePost($input: PostInput!){createPost(input:$input){id title status createdAt}}

Output:

mutation CreatePost($input: PostInput!) {
  createPost(input:$input) {
    id title status createdAt
  }
}

Observation: the variable declaration $input: PostInput! and the argument input:$input are preserved inline. The formatter only re-indents around braces; it never rewrites token contents.

Quirk confirmed (Test 4) — pre-formatted multi-line input

Pasting an already indented, multi-line query produced stray blank lines and a double space before some { characters, because the tool re-derives indentation purely from brackets and treats your existing newlines as additional break signals. For the cleanest result, paste the compact (minified) query — the formatter is built to take compact input and expand it, not to tidy already-formatted source.

Related Tools

When to Use This Tool Instead of Code

You can pipe a query through a GraphQL codegen tool or prettier with the GraphQL parser, but a browser formatter wins the moment you copy a query out of a network tab, a chat, or a doc and just want to read it — no install, no package to trust with your operation text. For repeatable, schema-aware formatting inside a build step, keep the library; for ad-hoc inspection, use the tool. For the payload side of the same request, our JSON Formatter guide explains pretty-printing the response body.