Profiles·Public

graphql

semver>=16.0.0 <18.0.0postconditions15functions8last verified2026-06-18coverage score80%

Postconditions — what we check

  • graphql · graphql-errors-not-thrown
    error
    Whengraphql() is called with a query string that has a syntax error, a schema validation violation, or resolvers that throw errors. Caller checks for thrown exception but does not check result.errors.
    ReturnsExecutionResult with errors array populated — parse errors, schema validation errors, and resolver errors are all captured in result.errors[], never thrown. result.data will be null or partial depending on which fields errored.
    Required handlingCaller MUST check result.errors after awaiting graphql(). The promise NEVER rejects for query/resolver errors — they are captured in result.errors[]. Pattern: const result = await graphql({...}); if (result.errors) { handle... } Only invalid argument types (non-Schema object) will cause rejection.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[1]
  • graphql · graphql-invalid-schema-errors
    error
    Whengraphql() is called with a schema that has validation errors (invalid type definitions, circular references, missing root types). The schema object exists but fails internal validation.
    ReturnsExecutionResult with errors[] containing schema validation GraphQLErrors. data will be undefined. No execution occurs.
    Required handlingValidate schema with validateSchema() during application startup. If using buildSchema() or buildASTSchema(), call assertValidSchema() immediately after. The result of graphql() with an invalid schema looks identical to query errors — only the error messages indicate it is a schema problem.
    costhighin proddegraded serviceusers seeservice unavailablevisibilitysilent
    Sources[2]
  • execute · execute-resolver-errors
    error
    Whenresolver throws error or promise rejects (database error, validation error, network error)
    Returns{errors: GraphQLError[], data: null | partial} - errors array contains resolver failures
    Required handlingCaller MUST check result.errors array before accessing result.data. Resolver errors are returned in errors array, not thrown. Use if (result.errors) to detect failures and handle appropriately.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]
  • execute · execute-abort-signal-rejection
    warning
    Whenexecute() is called with args.abortSignal (a v17+ AbortSignal). The signal aborts mid-execution — typically because the inbound HTTP request was cancelled, a per-request deadline elapsed, or an upstream supervisor cancelled the operation. The returned Promise rejects with AbortedGraphQLExecutionError (NOT GraphQLError, NOT a generic Error). The error carries `error.abortedResult` containing the partial ExecutionResult collected before the abort fired (data + errors collected so far). Without try/catch around the awaited execute() call, this rejection surfaces as an UnhandledPromiseRejection, the partial result is lost, and the calling HTTP handler returns 500 instead of a 499 / partial 200.
    ThrowsAbortedGraphQLExecutionError (new v17 class — extends Error, name === 'AbortedGraphQLExecutionError'). Distinct from GraphQLError. Properties: - message / cause: set from the AbortSignal's abort reason - abortedResult: PromiseOrValue<ExecutionResult> — the partial result with whatever data + errors were collected before the abort took effect
    Required handlingWhen execute() is called with an abortSignal, wrap the awaited call in try/catch and check for AbortedGraphQLExecutionError BEFORE treating the rejection as a server error: import { execute, AbortedGraphQLExecutionError } from 'graphql'; try { const result = await execute({ schema, document, rootValue, abortSignal: req.signal, }); return reply.send(result); } catch (error) { if (error instanceof AbortedGraphQLExecutionError) { // Cancellation, not failure. Optionally surface the partial result: const partial = await error.abortedResult; return reply.code(499).send(partial); // 499 Client Closed Request } throw error; // genuine server error — let the framework handle it } DO NOT swallow this error silently — losing the partial result discards work that already executed (potentially with side effects). DO NOT classify it as a 500 — abort-on-cancellation is a normal client behavior, not a server failure. For SSR frameworks (Apollo Server, Mercurius, GraphQL Yoga), this rejection propagates up through the request handler and MUST be recognized at the framework boundary.
    costlowin prodimmediate exceptionusers seelost datavisibilitysilent
    Sources[4][5]
  • subscribe · subscribe-result-not-checked
    error
    Whensubscribe() resolves but the caller does not check if the result is an AsyncGenerator or an ExecutionResult (error). Caller assumes subscribe always returns an async iterator and immediately starts iterating with for-await-of.
    ReturnsExecutionResult (not AsyncGenerator) when subscription field is missing, subscription resolver fails, schema lacks subscription type, or field is not defined. Iterating an ExecutionResult crashes with TypeError.
    Required handlingCaller MUST check the return type before iterating: const result = await subscribe({...}); if ('errors' in result) { handle subscription setup failure; return; } for await (const event of result) { handle event; } Checking Symbol.asyncIterator is also valid: if (Symbol.asyncIterator in result).
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[6][7]
  • subscribe · subscribe-system-error-thrown
    error
    WhenThe subscription field's subscribe() resolver throws a non-GraphQLError (e.g., database connection error, EventEmitter setup failure, pubsub initialization error). These are system-level errors distinct from business-logic GraphQLErrors.
    ThrowsAny non-GraphQLError thrown by the subscription source resolver is re-thrown as a Promise rejection. Database errors, pubsub errors, EventEmitter errors propagate uncaught if subscribe() is not wrapped in try-catch.
    Required handlingWrap subscribe() call in try-catch for non-GraphQLError system failures: try { const result = await subscribe({...}); if ('errors' in result) { return; } // schema/field errors for await (const event of result) { ... } } catch (error) { // system-level failure from subscribe resolver (DB, pubsub, etc.) logger.error('Subscription setup failed:', error); }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8]
  • subscribe · subscribe-event-errors-unchecked
    warning
    WhenIn a for-await-of loop over the subscribe() result, each yielded ExecutionResult event is used directly without checking event.errors. Database errors, auth failures, or resolver errors in individual events silently produce empty data.
    ReturnsEach yielded ExecutionResult may have errors[] populated for that specific event. data may be null or partial. The stream continues yielding subsequent events.
    Required handlingCheck errors on EACH event, not just on subscription setup: for await (const event of subscriptionResult) { if (event.errors) { logger.error('Event error:', event.errors); // decide whether to continue or break } processData(event.data); }
    costlowin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[9]
  • parse · parse-syntax-error
    error
    Whenquery string has syntax error (invalid GraphQL syntax, malformed query)
    ThrowsGraphQLError with syntax error details and location
    Required handlingCaller MUST wrap parse() in try-catch to handle syntax errors. Invalid query syntax throws GraphQLError that crashes application if unhandled.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[10]
  • validate · validate-schema-errors
    error
    Whenquery violates schema rules (unknown field, type mismatch, invalid argument)
    ReturnsGraphQLError[] - array of validation errors (empty array means valid)
    Required handlingCaller MUST check if validate() returns non-empty array before executing query. Validation errors indicate query will fail - check if (errors.length > 0) and handle before execution.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[11]
  • buildSchema · buildschema-syntax-error
    error
    WhenbuildSchema() is called with a SDL string containing syntax errors (unclosed braces, misspelled keywords like 'typo Query', invalid characters). The internal parse() call throws before schema construction.
    ThrowsGraphQLError with syntax error message and location info
    Required handlingWrap buildSchema() in try-catch at application startup. Syntax errors in SDL indicate a programming mistake — fail fast and log the error with schema source. Check err instanceof GraphQLError to identify parse errors. These errors should NEVER occur in production if schema is committed to source control.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[12]
  • buildSchema · buildschema-semantic-validation-error
    error
    WhenbuildSchema() is called with syntactically valid SDL that fails semantic validation: duplicate type definitions, fields referencing undefined types, invalid implements usage, circular interface references, or using scalar names as object types.
    ThrowsError (NOT GraphQLError) with all validation errors joined by '\n\n'. Cannot catch with: catch (e) { if (e instanceof GraphQLError) ... } — this check will miss semantic validation errors.
    Required handlingWrap buildSchema() in try-catch without instanceof filtering: try { const schema = buildSchema(sdlString); } catch (error) { // catches BOTH GraphQLError (syntax) AND Error (semantic validation) logger.error('Invalid schema SDL:', error.message); process.exit(1); // or throw — schema is required for server startup } Do not assume thrown errors are always GraphQLError instances.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[13]
  • experimentalExecuteIncrementally · experimental-incremental-result-not-discriminated
    error
    WhenexperimentalExecuteIncrementally() resolves but the caller treats the result as if it were always a plain ExecutionResult — accesses result.errors and result.data directly, ships the response body to the HTTP client as JSON, and ignores result.initialResult / result.subsequentResults. When the document contains @defer or @stream, the result is the INCREMENTAL shape — the JSON serializer captures only the (undefined) data field, the subsequent payloads never get sent, and the client hangs waiting for deferred fields that the server already produced but discarded.
    ReturnsExperimentalIncrementalExecutionResults: an object with `initialResult` (InitialIncrementalExecutionResult — has data, hasNext, pending) and `subsequentResults` (AsyncGenerator<SubsequentIncrementalExecutionResult>). The shape is DISTINCT from ExecutionResult — there is no top-level `data` or `errors` field. Naive JSON.stringify of the result drops the iterator.
    Required handlingCaller MUST discriminate before serializing. Pattern from the graphql-js docs (execute.d.ts example) and Apollo Router's incremental-delivery layer: import { experimentalExecuteIncrementally } from 'graphql'; const result = await experimentalExecuteIncrementally(args); if ('initialResult' in result) { // Incremental delivery path — use multipart/mixed or SSE transport await streamIncrementalResponse(result, reply); return; } // Non-incremental path — single response return reply.send(result); The `'initialResult' in result` check is the canonical discrimination — ExecutionResult never has this key. For HTTP transports, incremental delivery requires multipart/mixed or text/event-stream — sending the initial result alone is a protocol violation. The `subsequentResults` iterator MUST be consumed or explicitly returned (to allow internal cleanup) — abandoning it leaks a Publisher subscription and the executor's abort cleanup never fires. DO NOT call experimentalExecuteIncrementally() for documents you know contain no @defer/@stream — use execute() instead, which has the simpler return type contract.
    costmediumin prodsilent failureusers seelost datavisibilitysilent
    Sources[14][15]
  • experimentalExecuteIncrementally · experimental-incremental-abort-signal-rejection
    warning
    WhenexperimentalExecuteIncrementally() is called with args.abortSignal and the signal aborts before either the initial result OR all subsequent payloads have been produced. The returned Promise rejects with AbortedGraphQLExecutionError carrying the partial state — for incremental delivery, the partial state is BOTH the initial-result-so-far AND the stream of payloads emitted prior to the abort. The subsequent iterator is also signalled (its `.throw(reason)` is invoked internally per IncrementalPublisher.js:17) so an active consumer of `subsequentResults` sees a thrown error inside the for-await loop.
    ThrowsAbortedGraphQLExecutionError on the top-level Promise. Concurrently, if a consumer is already iterating `subsequentResults`, the iterator's for-await-of loop throws with the abort reason.
    Required handlingWrap BOTH the awaited call AND the iterator consumption in error handling when using abortSignal with incremental delivery: const result = await experimentalExecuteIncrementally({ schema, document, rootValue, abortSignal: req.signal, }).catch(err => { if (err instanceof AbortedGraphQLExecutionError) { return null; // client cancelled — drop the response } throw err; }); if (!result) return reply.code(499).send(); if ('initialResult' in result) { try { for await (const payload of result.subsequentResults) { await reply.sendPayload(payload); } } catch (err) { // abort fired mid-stream — terminate the multipart response await reply.endMultipart(); return; } } Abandoning the iterator without consuming-or-throwing leaks the IncrementalPublisher's pending work. For request-scoped servers (Apollo Server, Yoga), wire abortSignal to req.signal; for queue-driven workers, wire to the job-deadline AbortController.
    costlowin prodsilent failureusers seelost datavisibilitysilent
    Sources[4][16]
  • legacyExecuteIncrementally · legacy-incremental-result-not-discriminated
    error
    WhenlegacyExecuteIncrementally() resolves but the caller treats the result as a plain ExecutionResult. Same failure mode as experimentalExecuteIncrementally — incremental payloads silently dropped, client hangs. The legacy format is specifically dangerous because callers porting from the experimental function may assume the function names indicate same-shape responses with only formatting differences; in fact both produce a discriminated union with `initialResult` and `subsequentResults`.
    ReturnsLegacyExperimentalIncrementalExecutionResults: { initialResult, subsequentResults } — same top-level shape as the experimental variant, but each payload uses `path` instead of `id` for incremental location.
    Required handlingDiscriminate exactly as for experimentalExecuteIncrementally: const result = await legacyExecuteIncrementally(args); if ('initialResult' in result) { await streamLegacyIncremental(result, reply); return; } return reply.send(result); Choose between experimentalExecuteIncrementally and legacyExecuteIncrementally based on the CLIENT — if you control the client and it's modern, use the experimental (now-canonical) variant; if you ship a public GraphQL API that Relay clients consume, use the legacy variant until Relay's incremental layer migrates. DO NOT mix on the same operation — the response shape is determined per-request by which function you call.
    costmediumin prodsilent failureusers seelost datavisibilitysilent
    Sources[17][15]
  • legacyExecuteIncrementally · legacy-incremental-abort-signal-rejection
    warning
    WhenlegacyExecuteIncrementally() is called with args.abortSignal and the signal aborts during execution. Same mechanism as experimental-incremental-abort- signal-rejection: top-level Promise rejects with AbortedGraphQLExecutionError; if subsequentResults is being consumed, its for-await-of throws.
    ThrowsAbortedGraphQLExecutionError on the awaited Promise. The subsequentResults iterator throws inside the for-await consumer with the abort reason.
    Required handlingSame as experimentalExecuteIncrementally: wrap both the awaited Promise AND the iterator consumption. See experimental-incremental-abort-signal- rejection for the canonical pattern. For legacy-format servers, the abort handling shape is identical — only the payload identifiers (`path` vs. `id`) differ in successful payloads.
    costlowin prodsilent failureusers seelost datavisibilitysilent
    Sources[4][18]

Sources

Every postcondition cites at least one of these. Numbered to match the footnotes above.

  1. [1]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/v16.9.0/src/graphql.ts#L55
  2. [2]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/v16.9.0/src/graphql.ts#L60
  3. [3]graphql.org/graphql-js/executionhttps://graphql.org/graphql-js/execution/#execute
  4. [4]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/main/src/execution/AbortedGraphQLExecutionError.ts
  5. [5]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/main/src/execution/Executor.ts
  6. [6]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/v16.9.0/src/execution/subscribe.ts#L35
  7. [7]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/v16.9.0/src/execution/subscribe.ts#L97
  8. [8]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/v16.9.0/src/execution/subscribe.ts#L98
  9. [9]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/v16.9.0/src/execution/subscribe.ts#L63
  10. [10]graphql.org/graphql-js/languagehttps://graphql.org/graphql-js/language/#parse
  11. [11]graphql.org/graphql-js/validationhttps://graphql.org/graphql-js/validation/#validate
  12. [12]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/v16.9.0/src/utilities/buildASTSchema.ts#L100
  13. [13]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/v16.9.0/src/validation/validate.ts#L138
  14. [14]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/main/src/execution/execute.ts
  15. [15]github.com/graphql/graphql-spechttps://github.com/graphql/graphql-spec/pull/742
  16. [16]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/main/src/execution/incremental/IncrementalPublisher.ts
  17. [17]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/main/src/execution/legacyIncremental/legacyExecuteIncrementally.ts
  18. [18]github.com/graphql/graphql-jshttps://github.com/graphql/graphql-js/blob/main/src/execution/legacyIncremental/BranchingIncrementalPublisher.ts
Need a different package?
Request a profile