In GraphQL, what does the execution info argument give a resolver, and when is reading it justified?
answer
- Ask what a resolver knows about position
- Not specified; servers converged on it anyway
- Parent type, return type, path, schema, operation
- The path uses response keys and list indices
- Cross-cutting reads yes, business branching no
basics
~20 sExecution info is the convention-only fourth resolver argument, describing where the field sits: parent type, declared return type, response path, schema and parsed operation. Read it for cross-cutting concerns such as per-field instrumentation; keep business logic out of it.
solid answer
~50 sThe info argument is not in GraphQL's specification -- it is what servers added so a resolver can learn its **position** in the request. It typically exposes the parent type, the field's declared return type and nullability, the response path from the root, the executable schema, and the parsed operation with its variable values. The genuinely valuable part is the path: response keys plus zero-based list indices, using the alias where the client aliased a field, which is the same path shape the specification requires on an error entry. That makes it the right key for per-field timings and error attribution when one field name appears under two aliases and across a list. Defensible reads are cross-cutting: instrumentation, generic behaviour keyed on the declared return type, authorization that depends on the parent type. Branching business logic on the operation name is not -- and every read couples the resolver to one server's shape and makes it harder to test.
code
graphql · 8 linesquery JobSearch($q: String!) {
jobSearch(query: $q, first: 24) {
id
title
hiring: company { headcount }
parentCo: company { headcount }
}
}go deeper
Recognise the fourth resolver argument by name and know it describes the field's position in the request rather than its data. You are not expected to use it yet.
Explain what it carries and, in particular, how the response path is built -- response keys and list indices, with aliases as the client wrote them -- and why that differs from the schema field name.
Demonstrate judgement: use it for cross-cutting attribution such as per-field timings against a latency budget, and refuse it for business branching. Be explicit that it is unspecified and server-specific.
Decide how much of the execution engine your resolver authors are allowed to see. Exposing info everywhere buys instrumentation and costs portability and testability; the usual answer is to wire it once in a framework layer and keep it out of domain code.
## A parameter the specification never mentions The fourth argument in the conventional resolver signature -- usually called the info, resolve-info or execution-info argument -- appears nowhere in GraphQL's specification. The specified algorithm calls a field's internal resolver with the parent value and the coerced arguments, and leaves everything about that function to the type system. The info argument is the ecosystem's answer to a real need: a resolver sometimes has to know *where in this request it is running*, not just what it is resolving. ## What it carries Contents vary between servers, but the recurring set is: - the **parent type** and the **field's declared return type**, including its nullability and list wrappers; - the **field name as declared** in the schema; - the **response path** from the root of the response to this field; - the **executable schema**, so generic code can inspect types and their directives; - the **parsed operation** -- its name, type and variable values -- and the document's fragment definitions; - the field's own **selection nodes**. Reading them to project a narrower backend query is a distinct capability with its own tradeoffs, and it is not what most info reads are for. Because the shape is unspecified, it varies. Treat the info argument as a server-specific surface, not a portable one. ## The response path is the genuinely useful part The path is a list of segments from the root of the *response*: response keys for fields and zero-based integers for list positions. Two properties make it the right identifier for anything cross-cutting. First, it is unique. A field name is not: `headcount` may appear four times in one document and once per item of a 24-element list. The path distinguishes all of them. Second, when a field is aliased, the path segment is the **alias**, because it describes a position in the response rather than in the document. That is not merely a convention -- the specification requires exactly this for the `path` entry on an error, since the path exists so a client can line an error up with the hole in its own response. Using the same path shape for a measurement means measurement and error entry refer to the same place by the same name. ## Worked example: a 340 ms p99 budget A job search has a 340 ms p99 budget for the whole document, and it was blowing through it: ```graphql query JobSearch($q: String!) { jobSearch(query: $q, first: 24) { id title hiring: company { headcount } parentCo: company { headcount } } } ``` `Company.headcount` is selected twice under two aliases and 24 times over. Timing by field name gives one meaningless aggregate. Timing keyed by the response path attributes it exactly: `jobSearch.11.parentCo.headcount` showed 214 ms, and it turned out the parent-company lookup missed the batch entirely while the hiring-company one did not. The reason to reach into the info argument here is that the path is the only value in the resolver that identifies *this* invocation; the parent value and the arguments are identical for both aliases. ## Reads that are defensible - **Cross-cutting instrumentation**: spans, per-field timings, per-field error attribution -- all keyed by the path, and wired once rather than written per resolver. - **Generic behaviour driven by the declared return type**: one implementation serving many fields, deciding how to complete or authorise based on the type it was asked for. - **Authorization that legitimately depends on the parent type**, where the same field is reachable from two positions with different rules. ## Reads that will bite you - **Branching business logic on the operation name.** It is client-supplied and unvalidated. Renaming a document then silently changes server behaviour, and nothing in a test suite forces you to notice. - **Digging an argument value out of the parsed operation.** If a resolver needs a value, declare it as an argument; reading the AST bypasses coercion, defaults and validation. - **Treating info as request state.** It describes one field's position, not the request; per-request state belongs in the context. Every read also costs portability and testability. A resolver that takes a parent value and coerced arguments is a plain function you can call from a unit test in one line. A resolver that reads three properties off an execution-info object now needs that object constructed or faked, and it is pinned to one server's shape of it. The default should be to pass what the resolver needs -- as an argument, or as a value on the context -- and to reserve info for concerns that are genuinely about position in the request. ## Specified versus conventional Specified: nothing about the info argument itself; and separately, that an error entry's `path` uses response keys and list indices, with aliases as the client wrote them. Conventional: the info argument's existence, its name, and every field on it -- including the path that mirrors the specified error path.
- Why is the response path a better key for a per-field measurement than the field name?A field name is not unique: the same field can be selected twice under different aliases and once per item of a list. The path -- response keys plus zero-based indices -- identifies one position in one response, and it is the same shape the specification mandates for an error's path, so a measurement and the error a client saw refer to the same place by the same name.
- A resolver reads the operation name out of the info argument to skip an expensive check. What is wrong with that?The operation name is client-supplied and never validated against anything, so it is not a trustworthy signal, and behaviour now changes when someone renames a document. If the check depends on a caller property, derive it during authentication and put it on the context; if it depends on an input, declare it as an argument.
- How do you keep a resolver testable when it genuinely needs the field's declared return type?Pass the type, or the behaviour keyed on it, in at construction so the resolver stays a plain function of parent value and arguments. Reserve the info object for concerns wired once for every field -- tracing, metrics, error attribution -- where faking it in a unit test is not needed because unit tests do not exercise that layer.
saying these in an interview costs you the question
- Calls the info argument part of the specification
- Branches business rules on the operation name
- Assumes path segments are schema field names, not aliases
- Digs argument values out of the parsed operation
- Believes every server exposes identical info fields
- Treats the info object as per-request mutable state