Introducing GraphQL.js v17
On behalf of the open-source maintainers and contributors of GraphQL.js, we are excited to announce the availability of GraphQL.js v17!
We encourage users to upgrade to v17. To make that practical, graphql-js.org now includes detailed upgrade guides from v14 to v15, v15 to v16, and v16 to v17. The site also has new content, including full API references for v17 and v16.
If you have migration questions, please reach out in the #graphql-js channel
on the GraphQL Discord server, or
open an issue if a migration
problem needs tracking. If you have corrections or suggestions for the guides,
open a pull request.
Why upgrade?
GraphQL.js v17 delivers stronger runtime integration APIs, improved schema
correctness and spec alignment, and polish to runtime and tooling APIs. It also
includes exciting experimental features, including fragment arguments, an
experimental error mode related to future onError behavior, and incremental
delivery, for teams that want to help test proposal-backed GraphQL features in
production-shaped systems.
Runtime integration APIs
For framework and server authors, v17 introduces a cleaner lower-level execution
boundary. validateExecutionArgs() validates and normalizes execution arguments
once, and the execute helpers run the matching root selection set:
executeRootSelectionSet() for stable single-result execution and
experimentalExecuteRootSelectionSet() for incremental delivery. Subscriptions
now have their own boundary: validateSubscriptionArgs() validates the
subscription-specific shape, createSourceEventStream() creates the source
event stream, mapSourceToResponseEvent() maps source events to GraphQL
responses, and executeSubscriptionEvent() executes one event. These helpers
let hosts customize execution without copying GraphQL.js internals.
For teams operating GraphQL.js servers, v17 adds first-class
AbortSignal support and
resolver access to cancellation through info.getAbortSignal(). Request
timeouts, client disconnects, and downstream APIs that accept AbortSignal can
now share one GraphQL.js-supported cancellation path. When execution aborts
after producing partial data, AbortedGraphQLExecutionError exposes the abort
cause and partial result.
The companion asyncWorkFinished execution hook lets hosts observe when tracked
async execution work has settled. That matters for cleanup, tests, tracing, and
transports as
GraphQL.js sometimes stops producing a response before every async iterator or resolver cleanup task has finished.
v17 also adds the
GraphQLHarness shape for
customizing the parse, validate, execute, and subscribe phases used by
graphql() and graphqlSync(). This intentionally follows the phase model
proven by Envelop, bringing the
resulting function types into the reference implementation. In particular,
GraphQLParseFn and GraphQLValidateFn may return promises even though the
built-in parse() and validate() functions remain synchronous. The new
standard shape acknowledges that user-supplied parse and validate phases
may sometimes be async. You
can use a raw GraphQLHarness directly, but we expect and encourage most
servers working with this pattern to continue using Envelop or a framework
modeled after Envelop.
For observability tooling, v17 publishes lifecycle events through Node.js
diagnostics_channel, with
channels for parsing, validation, execution, subscription setup, root selection
set execution, variable coercion, and field resolution.
Schema correctness and spec alignment
For schema and tooling authors, v17 fixes important default-value modeling bugs.
Programmatic defaults are now expected in uncoerced input form, either as raw
JavaScript input values with default: { value } or GraphQL literal ASTs with
default: { literal }. The older default-value forms still work in v17, but are
deprecated. Together with the new valueToLiteral() helper, this means
GraphQL.js can make the correct default value available through introspection.
v17 clarifies custom scalar APIs. Some methods are new and some are renamed from
the v16 names: coerceOutputValue, coerceInputValue, coerceInputLiteral,
and valueToLiteral now describe the actual GraphQL value boundary. The older
serialize, parseValue, and parseLiteral names continue to work in v17 as
deprecated compatibility aliases.
Directives on directive definitions have graduated from experimental status.
Directives can now be applied to directive definitions without a parser flag,
directive extensions are part of the normal SDL grammar, and directive
deprecation metadata is available through GraphQLDirective, introspection, and
schema printing.
Validation coverage is broader: schema validation reports more invalid defaults
and duplicate operation roots, and KnownOperationTypesRule validates
operations whose root type is missing.
Runtime and tooling polish
The hideSuggestions option is available for hiding “Did you mean …” text
from validation errors. Use it alongside NoSchemaIntrospectionCustomRule to
reduce schema-shape leakage through both suggestion text and introspection.
Resolvers may now return async iterables for list fields, which is especially
helpful for streamed lists.
For SDL tooling, printDirective() can now print one directive definition
without printing an entire schema. Built-in scalar coercers now accept
representable JavaScript bigint values. GraphQLSchema.getField() can resolve
meta fields, schema element extensions can use symbol keys, and
findSchemaChanges() reports breaking, dangerous, and safe schema changes in
one call.
Beyond those stable highlights, v17 also includes experimental specification work.
Experimental specification work
Fragment arguments let a fragment define local variables and let each fragment spread pass values to that fragment. The goal is better colocation: a fragment can declare the inputs required by the selection it owns, and each spread can supply those inputs without forcing every operation that uses the fragment to carry the same variable plumbing. This work was formerly championed by Jovi De Croock, whose spec PR and GraphQL.js implementation moved the proposal forward. The current PR needs a new champion to help carry the work through the specification process.
v17 also includes an experimental preview of more flexible GraphQL error
handling.
Today’s error propagation can blur meaningful business null values with resolver failures, discard useful partial data, and create normalized-cache issues when a field error bubbles into an ancestor null.
Future work is centered on
client-selected error modes
through onError, including PROPAGATE, NULL, and HALT, plus
service discovery for
supported modes and defaults.
GraphQL.js v17 does not yet support the full onError request shape or every
proposed mode. It supports traditional error propagation, equivalent to
PROPAGATE, and one additional experimental mode, equivalent to NULL, via
@experimental_disableErrorPropagation.
Operations using that directive keep field execution errors in errors with
their normal path and set only the errored response position to null. Follow
along as we continue this work in v17 through the
onError implementation and
service capabilities PRs.
Last but certainly not least, v17 includes long-awaited experimental support for
incremental delivery with @defer and @stream. GraphQL.js v17 keeps ordinary
execute() as a single-result executor and exposes
experimentalExecuteIncrementally() for the current incremental response shape.
The GraphQL Working Group has revised that response shape extensively, including
the
new response format
with pending, incremental, and completed notices. Hosts that still need
the older incremental payload shape from earlier v17 alpha releases can use
legacyExecuteIncrementally(), so existing integrations have a migration bridge
while new integrations target the current format. This work is part of a
multi-year effort, with the tireless spec work led by
Rob Richard, including the open
spec draft.
These features remain experimental. We are shipping them in GraphQL.js so continued feedback can help refine these proposals.
Thank you
This is the first major release of GraphQL.js since October 2021, with contributors including @IvanGoncharov, @yaacovCR, @thomasheyenbrock, @robrichard, @twof, @spawnia, @lilianammmatos, @glasser, @PabloSzx, @sashashura, @Cito, @igrlk, @benjie, @leebyron, @dylanowen, @tomgasson, @sakesun, @AaronMoat, @colinhacks, @saihaj, @JoviDeCroock, @mjmahone, @andimarek, @n1ru4l, @jasonkuhrt, @hayes, @martinbonnin, @marklarah, @NeoPhi, @ardatan, @hkmu, @xonx4l, @ryym, @jerelmiller, @abishekgiri, @yuchenshi, @jbellenger, @BoD, @Nols1000, @Malien, @logaretm, @sarahxsanders, @fallintoplace, and @Urigo. We are grateful to them as well as everyone who helped move GraphQL.js v17 forward through issues, reviews, testing, design discussion, documentation, migration feedback, and user reports.
We particularly want to thank past maintainers Ivan Goncharov and Jovi De Croock for their contributions to GraphQL, GraphQL.js, and their stewardship of important portions of this release.
As always, a huge thanks to Lee Byron for building such a wonderful GraphQL community, and to that community for sustaining this work.
What is next?
To help shape what comes next, follow and contribute in the
graphql/graphql-js repository,
especially the
future work discussion in graphql/graphql-js#4818.
You can also join a
GraphQL.js Working Group meeting or
reach us in the #graphql-js channel on the
GraphQL Discord server. We would love your help!
Happy coding,
Yaacov Rydzinski @yaacovCR