skip to content

What is a GraphQL block string, and how does the parser treat its indentation?

level: middleimportance: nice to knowfreq 18%

answer

  1. Three quotes, not one
  2. Layout in the file, not in the value
  3. Almost nothing is escaped inside
  4. Common indent after the first line goes

basics

~20 s

A string literal delimited by triple quotes that may span lines and contain unescaped quotes. The parser normalises line endings, strips the indentation common to every line after the first, and removes leading and trailing blank lines.

solid answer

~50 s

GraphQL has two string literal forms. The ordinary one is delimited by a single pair of double quotes and supports the usual backslash escapes. The **block string** is delimited by triple quotes, may span multiple lines, and may contain unescaped double quotes — the only escape it recognises is an escaped triple quote, and sequences like a backslash followed by `n` stay literal rather than becoming a newline. The parser then post-processes the raw text into the value: line terminators are normalised, the leading whitespace **common to every non-blank line after the first** is removed from each of those lines, and leading and trailing blank lines are dropped. That last step is the point of the construct: it lets a multi-line value be indented to match the surrounding document without that indentation leaking into the value. Block strings are the customary way to write schema descriptions, and they are legal anywhere a string value is.

code

graphql · 12 lines
graphql
mutation UpdateBlurb {
  updateListingBlurb(
    id: "LST-4417"
    body: """
      Corner unit, two beds, south-facing.

      Parking included.
    """
  ) {
    id
  }
}

go deeper

for a junior

Recognise the triple-quote form when you see it and know that it is a string value, not a comment — it can appear anywhere a quoted string can.

for a middle

Explain the two things the parser does to the raw text: it strips the indentation common to every non-blank line after the first, and it interprets no backslash escapes except an escaped triple quote.

for a senior

Be able to predict the exact value a block string yields, including the dropped leading and trailing blank lines, before it reaches a resolver or a description. The difference shows up in golden-file tests and in generated documentation.

for a principal

Treat it as a small instance of a general rule: any format that humans hand-edit must define precisely which characters are content and which are layout, or every consumer invents its own trimming rule and they disagree.

## Two string forms A GraphQL string literal comes in two shapes. The familiar one is a **single-quoted string** — single pair of double quotes, `"like this"` — which lives on one line and interprets backslash escape sequences such as a backslash-n newline, a backslash-t tab, an escaped double quote and a Unicode escape. Note in passing that a single quote character is *not* a string delimiter in GraphQL at all; there is no `'like this'` form. The second is the **block string**, delimited by three double quotes on each side. It may contain line terminators, so it spans as many lines as you like, and it may contain lone double quotes without escaping them. It exists because writing a paragraph as a single-quoted string with backslash-n between every sentence is unreadable, and unreadable is fatal for the thing block strings are mostly used for: descriptions in a schema. ## Escaping, or the near-absence of it Inside a block string the parser recognises exactly **one** escape sequence: an escaped triple quote, written as a backslash followed by three double quotes, which produces a literal triple quote in the value. Everything else is taken literally. A backslash followed by `n` inside a block string is a backslash and the letter n, not a newline. A backslash followed by `u` and four hex digits is six literal characters, not a Unicode code point. This trips people up because it is the opposite of the ordinary string form, and because most other languages' multi-line string syntaxes do process escapes. The rule to remember is that a block string is close to raw text: what you see between the delimiters, minus indentation handling, is what the value contains. ## The indentation algorithm The raw text between the delimiters is not the value. The specification defines a transformation, and it has four steps worth knowing precisely: 1. Split the raw text on line terminators, normalising them so the value uses a single newline form regardless of whether the source used carriage returns. 2. Compute the **common indentation**: the smallest number of leading whitespace characters on any non-blank line *after the first*. The first line is excluded, because it is the remainder of the line that opened the delimiter and normally starts immediately after it. 3. Remove that many leading whitespace characters from every line except the first. 4. Remove leading blank lines and trailing blank lines, then join what remains with newlines. Step two is the reason the construct is pleasant to use. You can indent a description to line up with the definition it documents, and the layout of the file does not become part of the string. Step four is why a block string that opens and closes on lines of its own does not produce a value with a newline at each end. ## A worked example In a real-estate listings graph, a mutation carries a multi-line description as an argument: ```graphql mutation UpdateBlurb { updateListingBlurb( id: "LST-4417" body: """ Corner unit, two beds, south-facing. Parking included. """ ) { id } } ``` The raw text begins with a newline (the first line after the opening delimiter is empty), then three lines indented six spaces, then a line of six spaces before the closing delimiter. The common indentation of the non-blank lines after the first is six, so six spaces come off each of them; the leading blank line and the trailing blank line are dropped. The resolver receives a value of exactly `Corner unit, two beds, south-facing.` then a blank line then `Parking included.` — no leading spaces, no surrounding newlines. ## Where you meet them Overwhelmingly in a schema, as descriptions: a block string placed immediately before a type, field, argument or enum value definition becomes that definition's documentation, and unlike a `#` comment it is retained and exposed through introspection, which is how an explorer or a generated reference shows help text. That schema-authoring side is a topic of its own; what belongs here is the lexical fact that the same literal form is legal *anywhere a string value is* — an argument in an executable document, a default value, a directive argument. ## Pitfalls The common ones: expecting backslash-n to insert a newline (it will not; press return instead); trying to include a triple quote without escaping it, which terminates the string early and usually produces a syntax error further along; and being surprised that trailing whitespace on a line is *not* stripped — only leading indentation, and only the amount common to every line, is removed. If one line is accidentally indented less than its neighbours, the common indentation drops to that line's, and every other line keeps the difference. ## Interview framing This is a curiosity question rather than a gate: nobody's offer turns on it. It comes up when a candidate is asked to write a schema on a whiteboard and reaches for the triple-quote form, or when a generated documentation page shows text with stray indentation and the interviewer wants to know whether you can explain it. Answer with the two facts that actually matter: no escape processing except the escaped triple quote, and common indentation after the first line is stripped.

  • Which escape sequences work inside a GraphQL block string?
    Only one: an escaped triple quote, a backslash followed by three double quotes, which yields a literal triple quote. Every other backslash sequence is literal text, so a backslash and `n` stay two characters rather than becoming a newline. To put a newline in the value you write an actual line break, which is the whole reason the form exists.
  • How does a block string differ from a hash comment for documenting a schema?
    A comment is an ignored token: it is discarded when the document is lexed and never leaves the file. A block string placed before a definition is a description — part of the type system, retained by the server and readable through introspection, which is how explorers and generated reference pages show help text. Comments are for whoever opens the file, descriptions for whoever consumes the API.
  • Why is the first line excluded from the common-indentation calculation?
    Because content on the first line sits immediately after the opening delimiter, so its leading whitespace is whatever separates it from the delimiter rather than an indentation level. Including it would usually compute a common indent of zero and defeat the whole mechanism. In practice most authors leave the first line empty and start the text on the next one, which the trailing step then tidies away.

It behaves like a quoted block in a well-formatted email: you indent it so it reads nicely in the surrounding text, and the reader sees the passage without your indentation.

saying these in an interview costs you the question

  • Expects backslash-n to become a newline inside it
  • Calls a triple-quoted string a multi-line comment
  • Thinks the value keeps the file's indentation
  • Claims single quotes can delimit a GraphQL string
  • Assumes trailing whitespace on each line is stripped too

context