PageSourceSearch

https://graphql.org/_next/static/chunks/pages/blog/2026-06-15-introducing-graphql-js-v17-c5a5f4d071c43c09.js

js graphql.org collected 2026-09-24 07:00:33 UTC 20,453 bytes, 1 lines download raw bytes

1(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[6093],{11739:function(e,n,r){(window.__NEXT_P=window.__NEXT_P||[]).push(["/blog/2026-06-15-introducing-graphql-js-v17",function(){return r(39692)}])},39692:function(e,n,r){"use strict";r.r(n),r.d(n,{useTOC:function(){return h}});var s=r(52676),t=r(92937),a=r(45043),i=r(79428);function h(e){return[{value:"Why upgrade?",id:"why-upgrade",depth:2},{value:"Runtime integration APIs",id:"runtime-integration-apis",depth:3},{value:"Schema correctness and spec alignment",id:"schema-correctness-and-spec-alignment",depth:3},{value:"Runtime and tooling polish",id:"runtime-and-tooling-polish",depth:3},{value:"Experimental specification work",id:"experimental-specification-work",depth:2},{value:"Thank you",id:"thank-you",depth:2},{value:"What is next?",id:"what-is-next",depth:2}]}n.default=(0,t.c)(function(e){let{toc:n=h(e)}=e,r={a:"a",code:"code",h2:"h2",h3:"h3",p:"p",...(0,i.a)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(r.p,{children:"On behalf of the open-source maintainers and contributors of GraphQL.js, we are\nexcited to announce the availability of GraphQL.js v17!"}),"\n",(0,s.jsxs)(r.p,{children:["We encourage users to upgrade to v17. To make that practical,\n",(0,s.jsx)(r.a,{href:"https://www.graphql-js.org/",children:"graphql-js.org"})," now includes detailed upgrade\nguides from ",(0,s.jsx)(r.a,{href:"https://www.graphql-js.org/upgrade-guides/v14-v15/",children:"v14 to v15"}),",\n",(0,s.jsx)(r.a,{href:"https://www.graphql-js.org/upgrade-guides/v15-v16/",children:"v15 to v16"}),", and\n",(0,s.jsx)(r.a,{href:"https://www.graphql-js.org/upgrade-guides/v16-v17/",children:"v16 to v17"}),". The site also\nhas new content, including full API references for\n",(0,s.jsx)(r.a,{href:"https://www.graphql-js.org/api-v17/graphql/",children:"v17"})," and\n",(0,s.jsx)(r.a,{href:"https://www.graphql-js.org/api-v16/graphql/",children:"v16"}),"."]}),"\n",(0,s.jsxs)(r.p,{children:["If you have migration questions, please reach out in the ",(0,s.jsx)(r.code,{children:"#graphql-js"})," channel\non the ",(0,s.jsx)(r.a,{href:"https://discord.graphql.org",children:"GraphQL Discord server"}),", or\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-js/issues",children:"open an issue"})," if a migration\nproblem needs tracking. If you have corrections or suggestions for the guides,\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-js/pulls",children:"open a pull request"}),"."]}),"\n",(0,s.jsx)(r.h2,{id:n[0].id,children:n[0].value}),"\n",(0,s.jsxs)(r.p,{children:["GraphQL.js v17 delivers stronger runtime integration APIs, improved schema\ncorrectness and spec alignment, and polish to runtime and tooling APIs. It also\nincludes exciting experimental features, including fragment arguments, an\nexperimental error mode related to future ",(0,s.jsx)(r.code,{children:"onError"})," behavior, and incremental\ndelivery, for teams that want to help test proposal-backed GraphQL features in\nproduction-shaped systems."]}),"\n",(0,s.jsx)(r.h3,{id:n[1].id,children:n[1].value}),"\n",(0,s.jsxs)(r.p,{children:["For framework and server authors, v17 introduces a cleaner lower-level execution\nboundary. ",(0,s.jsx)(r.code,{children:"validateExecutionArgs()"})," validates and normalizes execution arguments\nonce, and the execute helpers run the matching root selection set:\n",(0,s.jsx)(r.code,{children:"executeRootSelectionSet()"})," for stable single-result execution and\n",(0,s.jsx)(r.code,{children:"experimentalExecuteRootSelectionSet()"})," for incremental delivery. Subscriptions\nnow have their own boundary: ",(0,s.jsx)(r.code,{children:"validateSubscriptionArgs()"})," validates the\nsubscription-specific shape, ",(0,s.jsx)(r.code,{children:"createSourceEventStream()"})," creates the source\nevent stream, ",(0,s.jsx)(r.code,{children:"mapSourceToResponseEvent()"})," maps source events to GraphQL\nresponses, and ",(0,s.jsx)(r.code,{children:"executeSubscriptionEvent()"})," executes one event. These helpers\nlet hosts customize execution without copying GraphQL.js internals."]}),"\n",(0,s.jsxs)(r.p,{children:["For teams operating GraphQL.js servers, v17 adds first-class\n",(0,s.jsx)(r.a,{href:"https://www.graphql-js.org/docs/abort-signals/",children:(0,s.jsx)(r.code,{children:"AbortSignal"})})," support and\nresolver access to cancellation through ",(0,s.jsx)(r.code,{children:"info.getAbortSignal()"}),". Request\ntimeouts, client disconnects, and downstream APIs that accept ",(0,s.jsx)(r.code,{children:"AbortSignal"})," can\nnow share one GraphQL.js-supported cancellation path. When execution aborts\nafter producing partial data, ",(0,s.jsx)(r.code,{children:"AbortedGraphQLExecutionError"})," exposes the abort\ncause and partial result."]}),"\n",(0,s.jsxs)(r.p,{children:["The companion ",(0,s.jsx)(r.code,{children:"asyncWorkFinished"})," execution hook lets hosts observe when tracked\nasync execution work has settled. That matters for cleanup, tests, tracing, and\ntransports as\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-js/issues/3792",children:"GraphQL.js sometimes stops producing a response before every async iterator or resolver cleanup task has finished"}),"."]}),"\n",(0,s.jsxs)(r.p,{children:["v17 also adds the\n",(0,s.jsx)(r.a,{href:"https://www.graphql-js.org/docs/graphql-harness/",children:(0,s.jsx)(r.code,{children:"GraphQLHarness"})})," shape for\ncustomizing the parse, validate, execute, and subscribe phases used by\n",(0,s.jsx)(r.code,{children:"graphql()"})," and ",(0,s.jsx)(r.code,{children:"graphqlSync()"}),". This intentionally follows the phase model\nproven by ",(0,s.jsx)(r.a,{href:"https://the-guild.dev/graphql/envelop",children:"Envelop"}),", bringing the\nresulting function types into the reference implementation. In particular,\n",(0,s.jsx)(r.code,{children:"GraphQLParseFn"})," and ",(0,s.jsx)(r.code,{children:"GraphQLValidateFn"})," may return promises even though the\nbuilt-in ",(0,s.jsx)(r.code,{children:"parse()"}
1)," and ",(0,s.jsx)(r.code,{children:"validate()"})," functions remain synchronous. The new\nstandard shape acknowledges that user-supplied parse and validate phases\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-js/issues/3421",children:"may sometimes be async"}),". You\ncan use a raw ",(0,s.jsx)(r.code,{children:"GraphQLHarness"})," directly, but we expect and encourage most\nservers working with this pattern to continue using Envelop or a framework\nmodeled after Envelop."]}),"\n",(0,s.jsxs)(r.p,{children:["For observability tooling, v17 publishes lifecycle events through Node.js\n",(0,s.jsx)(r.a,{href:"https://nodejs.org/api/diagnostics_channel.html",children:(0,s.jsx)(r.code,{children:"diagnostics_channel"})}),", with\nchannels for parsing, validation, execution, subscription setup, root selection\nset execution, variable coercion, and field resolution."]}),"\n",(0,s.jsx)(r.h3,{id:n[2].id,children:n[2].value}),"\n",(0,s.jsxs)(r.p,{children:["For schema and tooling authors, v17 fixes important default-value modeling bugs.\nProgrammatic defaults are now expected in uncoerced input form, either as raw\nJavaScript input values with ",(0,s.jsx)(r.code,{children:"default: { value }"})," or GraphQL literal ASTs with\n",(0,s.jsx)(r.code,{children:"default: { literal }"}),". The older default-value forms still work in v17, but are\ndeprecated. Together with the new ",(0,s.jsx)(r.code,{children:"valueToLiteral()"})," helper, this means\nGraphQL.js can make the correct default value available through introspection."]}),"\n",(0,s.jsxs)(r.p,{children:["v17 clarifies custom scalar APIs. Some methods are new and some are renamed from\nthe v16 names: ",(0,s.jsx)(r.code,{children:"coerceOutputValue"}),", ",(0,s.jsx)(r.code,{children:"coerceInputValue"}),", ",(0,s.jsx)(r.code,{children:"coerceInputLiteral"}),",\nand ",(0,s.jsx)(r.code,{children:"valueToLiteral"})," now describe the actual GraphQL value boundary. The older\n",(0,s.jsx)(r.code,{children:"serialize"}),", ",(0,s.jsx)(r.code,{children:"parseValue"}),", and ",(0,s.jsx)(r.code,{children:"parseLiteral"})," names continue to work in v17 as\ndeprecated compatibility aliases."]}),"\n",(0,s.jsxs)(r.p,{children:["Directives on directive definitions have graduated from experimental status.\nDirectives can now be applied to directive definitions without a parser flag,\ndirective extensions are part of the normal SDL grammar, and directive\ndeprecation metadata is available through ",(0,s.jsx)(r.code,{children:"GraphQLDirective"}),", introspection, and\nschema printing."]}),"\n",(0,s.jsxs)(r.p,{children:["Validation coverage is broader: schema validation reports more invalid defaults\nand duplicate operation roots, and ",(0,s.jsx)(r.code,{children:"KnownOperationTypesRule"})," validates\noperations whose root type is missing."]}),"\n",(0,s.jsx)(r.h3,{id:n[3].id,children:n[3].value}),"\n",(0,s.jsxs)(r.p,{children:["The ",(0,s.jsx)(r.code,{children:"hideSuggestions"})," option is available for hiding “Did you mean …” text\nfrom validation errors. Use it alongside ",(0,s.jsx)(r.code,{children:"NoSchemaIntrospectionCustomRule"})," to\nreduce schema-shape leakage through both suggestion text and introspection.\nResolvers may now return async iterables for list fields, which is especially\nhelpful for streamed lists."]}),"\n",(0,s.jsxs)(r.p,{children:["For SDL tooling, ",(0,s.jsx)(r.code,{children:"printDirective()"})," can now print one directive definition\nwithout printing an entire schema. Built-in scalar coercers now accept\nrepresentable JavaScript ",(0,s.jsx)(r.code,{children:"bigint"})," values. ",(0,s.jsx)(r.code,{children:"GraphQLSchema.getField()"})," can resolve\nmeta fields, schema element extensions can use symbol keys, and\n",(0,s.jsx)(r.code,{children:"findSchemaChanges()"})," reports breaking, dangerous, and safe schema changes in\none call."]}),"\n",(0,s.jsx)(r.p,{children:"Beyond those stable highlights, v17 also includes experimental specification\nwork."}),"\n",(0,s.jsx)(r.h2,{id:n[4].id,children:n[4].value}),"\n",(0,s.jsxs)(r.p,{children:["Fragment arguments let a fragment define local variables and let each fragment\nspread pass values to that fragment. The goal is better colocation: a fragment\ncan declare the inputs required by the selection it owns, and each spread can\nsupply those inputs without forcing every operation that uses the fragment to\ncarry the same variable plumbing. This work was formerly championed by\n",(0,s.jsx)(r.a,{href:"https://github.com/JoviDeCroock",children:"Jovi De Croock"}),", whose\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-spec/pull/1081",children:"spec PR"})," and GraphQL.js\nimplementation moved the proposal forward. The\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-spec/pull/1224",children:"current PR"})," needs a new\nchampion to help carry the work through the specification process."]}),"\n",(0,s.jsxs)(r.p,{children:["v17 also includes an experimental preview of more flexible GraphQL error\nhandling.\n",(0,s.jsxs)(r.a,{href:"https://github.com/graphql/graphql-wg/blob/main/rfcs/SemanticNullability.md",children:["Today’s error propagation can blur meaningful business ",(0,s.jsx)(r.code,{children:"null"})," values with resolver failures, discard useful partial data, and create normalized-cache issues when a field error bubbles into an ancestor ",(0,s.jsx)(r.code,{children:"null"}),"."]}),"\nFuture work is centered on\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-spec/pull/1163",children:"client-selected error modes"}),"\nthrough ",(0,s.jsx)(r.code,{children:"onError"}),", including ",(0,s.jsx)(r.code,{children:"PROPAGATE"}),", ",(0,s.jsx)(r.code,{children:"NULL"}),", and ",(0,s.jsx)(r.code,{children:"HALT"}),", plus\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-spec/pull/1208",children:"service discovery"})," for\nsupported modes and defaults."]}),"\n",(0,s.jsxs)(r.p,{children:["GraphQL.js v17 does not yet support the full ",(0,s.jsx)(r.code,{children:"onError"})," request shape or every\nproposed mode. It supports traditional error propagation, equivalent to\n",(0,s.jsx)(r.code,{children:"PROPAGATE"}),", and one additional experimental mode, equivalent to ",(0,s.jsx)(r.code,{children:"NULL"}),", via\n",(0,s.jsx)(r.a,{href:"https://www.graphql-js.org/docs/disabling-error-propagation/",children:(0,s.jsx)(r.code,{children:"@experimental_disableErrorPropagation"})}),".\nOperations using that directive keep field execution errors in ",(0,s.jsx)(r.code,{children:"errors"})," with\ntheir normal path and set only the errored response position to ",(0,s.jsx)(r.code,{children:"null"}),". Follow\nalong as we continue this work in v17 through the\n",(0,s.jsxs)(r.a,{href:"https://github.com/graphql/graphql-js/pull/4364",children:[(0,s.jsx)(r.code,{children:"onError"})," implementation"]})," and\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-js/pull/4523",children:"service capabilities"})," PRs."]}),"\n",(0,s.jsxs)(r.p,{children:["Last but certainly not least, v17 includes long-awaited experimental support for\nincremental delivery with ",(0,s.jsx)(r.code,{children:"@defer"})," and ",(0,s.jsx)(r.code,{children:"@stream"}),". GraphQL.js v17 keeps ordinary\n",(0,s.jsx)(r.code,{children:"execute()"})," as a single-result executor and exposes\n",(0,s.jsx)(r.code,{children:"experimentalExecuteIncrementally()"})," for the current incremental response shape.\nThe GraphQL Working Group has revised that response shape extensively, including\nthe\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/defer-stream-wg/discussions/69",children:"new response format"}),"\nwith ",(0,s.jsx)(r.code,{children:"pending"}),", ",(0,s.jsx)(r.code,{children:"incremental"}),", and ",(0,s.jsx)(r.code,{children:"completed"})," notices. Hosts that still need\nthe older incremental payload shape from earlier v17 alpha releases can use\n",(0,s.jsx)(r.code,{children:"legacyExecuteIncrementally()"}),", so existing integrations have a migration bridge\nwhile new integrations target the current format. This work is part of a\nmulti-year effort, with the tireless spec work led by\n",(0,s.jsx)(r.a,{href:"https://github.com/robrichard",children:"Rob Richard"}),", including the open\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-spec/pull/1110",children:"spec draft"}),"."]}),"\n",(0,s.jsx)(r.p,{children:"These features remain experimental. We are shipping them in GraphQL.js so\ncontinued feedback can help refine these proposals."}),"\n",(0,s.jsx)(r.h2,{id:n[5].id,children:n[5].value}),"\n",(0,s.jsxs)(r.p,{children:["This is the first major release of GraphQL.js since October 2021,\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-js/compare/16.x.x...17.x.x",children:"with contributors including"}),"\n",(0,s.jsx)(r.a,{href:"https://github.com/IvanGoncharov",children:"@IvanGoncharov"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/yaacovCR",children:"@yaacovCR"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/thomasheyenbrock",children:"@thomasheyenbrock"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/robrichard",children:"@robrichard"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/twof",children:"@twof"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/spawnia",children:"@spawnia"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/lilianammmatos",children:"@lilianammmatos"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/glasser",children:"@glasser"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/PabloSzx",children:"@PabloSzx"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/sashashura",children:"@sashashura"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/Cito",children:"@Cito"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/igrlk",children:"@igrlk"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/benjie",children:"@benjie"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/leebyron",children:"@leebyron"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/dylanowen",children:"@dylanowen"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/tomgasson",children:"@tomgasson"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/sakesun",children:"@sakesun"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/AaronMoat",children:"@AaronMoat"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/colinhacks",children:"@colinhacks"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/saihaj",children:"@saihaj"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/JoviDeCroock",children:"@JoviDeCroock"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/mjmahone",children:"@mjmahone"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/andimarek",children:"@andimarek"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/n1ru4l",children:"@n1ru4l"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/jasonkuhrt",children:"@jasonkuhrt"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/hayes",children:"@hayes"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/martinbonnin",children:"@martinbonnin"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/marklarah",children:"@marklarah"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/NeoPhi",children:"@NeoPhi"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/ardatan",children:"@ardatan"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/hkmu",children:"@hkmu"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/xonx4l",children:"@xonx4l"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/ryym",children:"@ryym"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/jerelmiller",children:"@jerelmiller"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/abishekgiri",children:"@abishekgiri"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/yuchenshi",children:"@yuchenshi"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/jbellenger",children:"@jbellenger"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/BoD",children:"@BoD"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/Nols1000",children:"@Nols1000"}),", ",(0,s.jsx)(r.a,{href:"https://github.com/Malien",children:"@Malien"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/logaretm",children:"@logaretm"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/sarahxsanders",children:"@sarahxsanders"}),",\n",(0,s.jsx)(r.a,{href:"https://github.com/fallintoplace",children:"@fallintoplace"}),", and\n",(0,s.jsx)(r.a,{href:"https://github.com/Urigo",children:"@Urigo"}),". We are grateful to them as well as everyone\nwho helped move GraphQL.js v17 forward through issues, reviews, testing, design\ndiscussion, documentation, migration feedback, and user reports."]}),"\n",(0,s.jsxs)(r.p,{children:["We particularly want to thank past maintainers\n",(0,s.jsx)(r.a,{href:"https://github.com/IvanGoncharov",children:"Ivan Goncharov"})," and\n",(0,s.jsx)(r.a,{href:"https://github.com/JoviDeCroock",children:"Jovi De Croock"})," for their contributions to\nGraphQL, GraphQL.js, and their stewardship of important portions of this\nrelease."]}),"\n",(0,s.jsxs)(r.p,{children:["As always, a huge thanks to ",(0,s.jsx)(r.a,{href:"https://github.com/leebyron",children:"Lee Byron"})," for\nbuilding such a wonderful GraphQL community, and to that community for\nsustaining this work."]}),"\n",(0,s.jsx)(r.h2,{id:n[6].id,children:n[6].value}),"\n",(0,s.jsxs)(r.p,{children:["To help shape what comes next, follow and contribute in the\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-js",children:"graphql/graphql-js repository"}),",\nespecially the\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-js/issues/4818",children:"future work discussion in graphql/graphql-js#4818"}),".\nYou can also join a\n",(0,s.jsx)(r.a,{href:"https://github.com/graphql/graphql-js-wg",children:"GraphQL.js Working Group"})," meeting or\nreach us in the ",(0,s.jsx)(r.code,{children:"#graphql-js"})," channel on the\n",(0,s.jsx)(r.a,{href:"https://discord.graphql.org",children:"GraphQL Discord server"}),". We would love your help!"]}),"\n",(0,s.jsx)(r.p,{children:"Happy coding,"}),"\n",(0,s.jsx)(r.p,{children:"Yaacov Rydzinski\n@yaacovCR"})]})},"/blog/2026-06-15-introducing-graphql-js-v17",{filePath:"src/pages/blog/2026-06-15-introducing-graphql-js-v17.mdx",timestamp:1789481543e3,pageMap:a.v,frontMatter:{title:"Introducing GraphQL.js v17",tags:["announcements"],date:"2026-06-15",byline:"Yaacov Rydzinski",featured:!0},title:"Introducing GraphQL.js v17"},"undefined"==typeof RemoteContent?h:RemoteContent.useTOC)}},function(e){e.O(0,[3556,5043,2888,9774,179],function(){return e(e.s=11739)}),_N_E=e.O()}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.