diff --git a/website/css/globals.css b/website/css/globals.css
index 58e95138a6..131f0239d8 100644
--- a/website/css/globals.css
+++ b/website/css/globals.css
@@ -126,6 +126,73 @@ div[id^='headlessui-menu-items'] {
margin: 0 0.35rem;
}
+.upgrade-guide-toc {
+ margin: 1rem 0 2rem;
+ padding: 1rem;
+ border: 1px solid rgb(229 231 235);
+ border-radius: 0.5rem;
+ background: rgb(249 250 251);
+}
+
+.dark .upgrade-guide-toc {
+ border-color: rgb(55 65 81);
+ background: rgb(17 24 39 / 0.35);
+}
+
+.upgrade-guide-toc-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(13.5rem, 1fr));
+ gap: 0.75rem 1rem;
+}
+
+.upgrade-guide-toc-group {
+ display: grid;
+ align-content: start;
+ gap: 0.35rem;
+ min-width: 0;
+ padding: 0.6rem 0.75rem;
+ border-left: 2px solid rgb(209 213 219);
+ border-radius: 0.375rem;
+ background: rgb(255 255 255 / 0.7);
+}
+
+.dark .upgrade-guide-toc-group {
+ border-left-color: rgb(75 85 99);
+ background: rgb(31 41 55 / 0.38);
+}
+
+.upgrade-guide-toc-title {
+ font-size: 0.875rem;
+ font-weight: 650;
+ line-height: 1.35;
+}
+
+.upgrade-guide-toc-links {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.25rem 0.65rem;
+ font-size: 0.8125rem;
+ line-height: 1.35;
+}
+
+.upgrade-guide-toc a {
+ overflow-wrap: anywhere;
+ text-decoration: none;
+}
+
+.upgrade-guide-toc a:hover {
+ text-decoration: underline;
+ text-underline-offset: 0.15em;
+}
+
+.upgrade-guide-toc-links a {
+ color: rgb(75 85 99);
+}
+
+.dark .upgrade-guide-toc-links a {
+ color: rgb(209 213 219);
+}
+
.api-item-divider {
margin: 2rem 0;
border: 0;
diff --git a/website/generate-api.js b/website/generate-api.js
index f65bd9ae4f..4b5442baa3 100644
--- a/website/generate-api.js
+++ b/website/generate-api.js
@@ -3094,7 +3094,7 @@ function addCategory(comment, category) {
const trailing = comment.match(/\s*$/)?.[0] ?? '';
const body = comment.slice(0, comment.length - trailing.length);
- const oneLine = /^(\s*)\/\*\*\s*(.*?)\s*\*\/$/.exec(body);
+ const oneLine = /^(\s*)\/\*\*[^\S\r\n]*(.*?)[^\S\r\n]*\*\/$/.exec(body);
if (oneLine != null) {
const [, indent, text] = oneLine;
return `${indent}/**\n${indent} * ${text}\n${indent} *\n${indent} * @category ${category}\n${indent} */${trailing}`;
diff --git a/website/pages/_meta.ts b/website/pages/_meta.ts
index 4feffb9b7b..9ee0f3022b 100644
--- a/website/pages/_meta.ts
+++ b/website/pages/_meta.ts
@@ -11,6 +11,14 @@ const meta = {
title: 'v16 to v17',
href: '/upgrade-guides/v16-v17',
},
+ 'v15-v16': {
+ title: 'v15 to v16',
+ href: '/upgrade-guides/v15-v16',
+ },
+ 'v14-v15': {
+ title: 'v14 to v15',
+ href: '/upgrade-guides/v14-v15',
+ },
},
},
api: {
diff --git a/website/pages/api-v17/execution.mdx b/website/pages/api-v17/execution.mdx
index 969fe5b48f..35ab089cd6 100644
--- a/website/pages/api-v17/execution.mdx
+++ b/website/pages/api-v17/execution.mdx
@@ -161,8 +161,9 @@ Returns either a synchronous ExecutionResult (if all encountered resolvers
are synchronous), or a Promise of an ExecutionResult that will eventually be
resolved and never rejected.
-If the arguments to this function do not result in a legal execution context,
-a GraphQLError will be thrown immediately explaining the invalid input.
+If the schema is invalid, an error will be thrown immediately. GraphQL
+request errors, including missing operations and variable coercion errors,
+are returned in an errors-only ExecutionResult.
Field errors are collected into the response instead of rejecting the
returned promise. Only the field that produced the error and its descendants
@@ -251,17 +252,18 @@ result; // => { data: { greeting: 'Hello, Ada!' } }
Implements the "Executing operations" section of the spec.
-Returns a Promise that will eventually resolve to the data described by
-The "Response" section of the GraphQL specification.
+Returns either a synchronous ExecutionResult, or a Promise for an
+ExecutionResult, described by the "Response" section of the GraphQL
+specification.
-If errors are encountered while executing a GraphQL field, only that
-field and its descendants will be omitted, and sibling fields will still
-be executed. An execution which encounters errors will still result in a
-resolved Promise.
+If errors are encountered while executing a GraphQL field, only that field
+and its descendants will be omitted, and sibling fields will still be
+executed. These field errors are collected into the returned result instead
+of being thrown or rejecting the returned promise.
-Errors from sub-fields of a NonNull type may propagate to the top level,
-at which point we still log the error and null the parent field, which
-in this case is the entire response.
+Errors from sub-fields of a NonNull type may propagate to the top level, at
+which point we still collect the error and null the parent field, which in
+this case is the entire response.
**Signature:**
@@ -408,6 +410,9 @@ result; // => { data: { greeting: 'Hello' } }
Executes a subscription operation once for a single source event.
+Field errors are collected into the returned result instead of being thrown
+or rejecting the returned promise.
+
**Signature:**
@@ -489,21 +494,22 @@ result; // => { data: { greeting: 'Hello' } }
Implements the "Subscribe" algorithm described in the GraphQL specification.
-Returns a Promise that resolves to either an AsyncIterator (if successful)
-or an ExecutionResult (error). The promise will be rejected if the schema or
-other arguments to this function are invalid, or if the resolved event stream
-is not an async iterable.
+Returns either an AsyncGenerator (if successful), an ExecutionResult (error),
+or a Promise for one of those results. The call will throw immediately if
+the schema is invalid or the selected operation is not a subscription.
-If the client-provided arguments to this function do not result in a
-compliant subscription, a GraphQL Response (ExecutionResult) with descriptive
-errors and no data will be returned.
+GraphQL request errors, including missing operations and variable coercion
+errors, return or resolve to a GraphQL Response (ExecutionResult) with
+descriptive errors and no data.
If the source stream could not be created due to faulty subscription resolver
-logic or underlying systems, the promise will resolve to a single
-ExecutionResult containing `errors` and no `data`.
+logic, a non-async-iterable resolver result, or a system error, the
+function will return or resolve to a single ExecutionResult containing
+`errors` and no `data`.
-If the operation succeeded, the promise resolves to an AsyncIterator, which
-yields a stream of ExecutionResults representing the response stream.
+If the operation succeeded, the function returns or resolves to an
+AsyncGenerator, which yields a stream of ExecutionResults representing the
+response stream.
This function does not support incremental delivery (`@defer` and `@stream`).
If an operation which would defer or stream data is executed with this
@@ -657,21 +663,20 @@ Implements the "CreateSourceEventStream" algorithm described in the
GraphQL specification, resolving the subscription source event stream for a
previously validated subscription request.
-Returns a Promise that resolves to either an AsyncIterable (if successful)
-or an ExecutionResult (error). The promise will be rejected if the validated
-execution arguments are invalid, or if the resolved event stream is not an
-async iterable.
+Returns either an AsyncIterable (if successful), an ExecutionResult (error),
+or a Promise for one of those results. The call will throw immediately if
+it is not passed validated execution arguments.
-If the client-provided arguments to this function do not result in a
-compliant subscription, a GraphQL Response (ExecutionResult) with
-descriptive errors and no data will be returned.
+If the validated arguments do not result in a compliant subscription, a
+GraphQL Response (ExecutionResult) with descriptive errors and no data will
+be returned.
If the source stream could not be created due to faulty subscription
-resolver logic or underlying systems, the promise will resolve to a single
-ExecutionResult containing `errors` and no `data`.
+resolver logic or a system error, the function will return or
+resolve to a single ExecutionResult containing `errors` and no `data`.
-If the operation succeeded, the promise resolves to the AsyncIterable for the
-event stream returned by the resolver.
+If the operation succeeded, the function returns or resolves to the
+AsyncIterable for the event stream returned by the resolver.
A Source Event Stream represents a sequence of events, each of which triggers
a GraphQL execution for that event.
@@ -764,12 +769,11 @@ Symbol.asyncIterator in stream; // => true
#### validateExecutionArgs()
-Constructs a ExecutionContext object from the arguments passed to
-execute, which we will pass throughout the other execution methods.
-
-Throws a GraphQLError if a valid execution context cannot be created.
+Validates the arguments passed to execute, subscribe, and their lower-level
+helpers.
-TODO: consider no longer exporting this function
+Throws if the schema is invalid. GraphQL request errors, including variable
+coercion errors, are returned as a GraphQLError array.
**Signature:**
@@ -873,6 +877,10 @@ validatedArgs.hideSuggestions; // => true
Validates execution arguments for a subscription operation.
+Throws if the schema is invalid or the selected operation is not a
+subscription. GraphQL request errors, including variable coercion errors, are
+returned as a GraphQLError array.
+
**Signature:**
@@ -1556,8 +1564,9 @@ This function returns either a single ExecutionResult, or an
ExperimentalIncrementalExecutionResults object containing an `initialResult`
and a stream of `subsequentResults`.
-If the arguments to this function do not result in a legal execution context,
-a GraphQLError will be thrown immediately explaining the invalid input.
+If the schema is invalid, an error will be thrown immediately. GraphQL
+request errors, including missing operations and variable coercion errors,
+are returned in an errors-only ExecutionResult.
**Signature:**
diff --git a/website/pages/api-v17/graphql.mdx b/website/pages/api-v17/graphql.mdx
index 6a5f5a6d27..f0fa336f4c 100644
--- a/website/pages/api-v17/graphql.mdx
+++ b/website/pages/api-v17/graphql.mdx
@@ -135,7 +135,7 @@ isDevModeEnabled(); // => true
#### GraphQLParseContext
-**Interface.** * Context published on `graphql:parse`.
+**Interface.** Context published on the sync-only `graphql:parse` channel.
@@ -172,7 +172,7 @@ isDevModeEnabled(); // => true
#### GraphQLValidateContext
-**Interface.** * Context published on `graphql:validate`.
+**Interface.** Context published on the sync-only `graphql:validate` channel.
@@ -214,7 +214,9 @@ isDevModeEnabled(); // => true
#### GraphQLExecuteContext
-**Interface.** * Context published on `graphql:execute`.
+**Interface.** Context published on `graphql:execute`.
+
+Returned results may contain GraphQL errors collected during execution.
@@ -257,12 +259,12 @@ isDevModeEnabled(); // => true
error?
-
Error thrown while executing, when execution fails abruptly.
+
Error thrown or rejected while executing, when execution fails abruptly.
result?
-
Execution result returned by execution.
+
Execution result returned by execution, including GraphQL errors.
@@ -271,7 +273,9 @@ isDevModeEnabled(); // => true
#### GraphQLExecuteRootSelectionSetContext
-**Interface.** * Context published on `graphql:execute:rootSelectionSet`.
+**Interface.** Context published on `graphql:execute:rootSelectionSet`.
+
+Returned results may contain GraphQL errors collected during execution.
@@ -319,12 +323,13 @@ isDevModeEnabled(); // => true
error?
-
Error thrown while executing the root selection set.
+
Error thrown or rejected while executing the root selection set.
result?
-
Execution result returned from the root selection set.
+
Execution result returned from the root selection set, including GraphQL
+errors.
@@ -335,10 +340,12 @@ isDevModeEnabled(); // => true
**Interface.** Context published on `graphql:execute:variableCoercion`.
-Coercion runs synchronously inside argument validation, so only the
-`start`/`end` (and, on a thrown error, [`error`](/api-v17/error)) lifecycle fires. When
-coercion produces variable errors it does not throw; instead `result`
-carries the `errors` array, mirroring `graphql:validate`.
+Coercion runs synchronously while execution arguments are validated, so only
+the `start`/`end` (and, on an abrupt throw, [`error`](/api-v17/error)) lifecycle fires.
+Ordinary variable coercion failures are returned on `result.errors`; when
+execution is invoked through APIs such as `execute()` or `subscribe()`, they
+surface as GraphQL result errors rather than as the tracing [`error`](/api-v17/error)
+lifecycle event.
@@ -400,7 +407,12 @@ carries the `errors` array, mirroring `graphql:validate`.
#### GraphQLSubscribeContext
-**Interface.** * Context published on `graphql:subscribe`.
+**Interface.** Context published on `graphql:subscribe`.
+
+Subscription source resolver errors and invalid source stream results are
+returned on `result` as ExecutionResult errors; they do not publish the
+[`error`](/api-v17/error) lifecycle event unless subscription setup fails abruptly before
+GraphQL can form a result.
@@ -443,12 +455,13 @@ carries the `errors` array, mirroring `graphql:validate`.
error?
-
Error thrown while subscribing, when subscription setup fails abruptly.
+
Error thrown or rejected while subscribing, when setup fails abruptly.
result?
-
Subscription response stream or execution result returned by subscribe.
+
Subscription response stream, or an ExecutionResult containing GraphQL
+errors.
@@ -457,7 +470,11 @@ carries the `errors` array, mirroring `graphql:validate`.
#### GraphQLResolveContext
-**Interface.** * Context published on `graphql:resolve`.
+**Interface.** Context published on `graphql:resolve`.
+
+Resolver throws and rejections publish the [`error`](/api-v17/error) lifecycle event here.
+The same failure may also be formatted into the enclosing execution or
+subscription result.
@@ -510,12 +527,12 @@ carries the `errors` array, mirroring `graphql:validate`.
error?
-
Error thrown by the resolver, when resolution fails.
+
Error thrown or rejected by the resolver, when resolution fails.
result?
-
Value returned by the resolver.
+
Value returned by the resolver, when resolution succeeds.
@@ -524,7 +541,7 @@ carries the `errors` array, mirroring `graphql:validate`.
#### GraphQLChannelContextByName
-**Interface.** * Mapping from tracing channel name to the context type published on it.
+**Interface.** Mapping from tracing channel name to the context type published on it.
@@ -581,7 +598,7 @@ carries the `errors` array, mirroring `graphql:validate`.
#### GraphQLChannels
-**Interface.** The collection of tracing channels graphql-js emits on. Application
+**Interface.** The collection of tracing channels GraphQL.js emits on. Application
performance monitoring (APM) tools subscribe to these by name on their own
`node:diagnostics_channel` import; both paths land on the same channel
instance because `tracingChannel(name)` is cached by name.
diff --git a/website/pages/docs/experimental-specification-features.mdx b/website/pages/docs/experimental-specification-features.mdx
index 0da607e2e4..e93895db6f 100644
--- a/website/pages/docs/experimental-specification-features.mdx
+++ b/website/pages/docs/experimental-specification-features.mdx
@@ -33,8 +33,8 @@ See [Defer and Stream](/docs/defer-stream).
## Fragment arguments
-Fragment arguments add fragment-local variable definitions and fragment-spread
-arguments. GraphQL.js exposes the syntax through the
+Fragment arguments let named fragments declare local variables and let named
+fragment spreads pass values for them. GraphQL.js exposes the syntax through the
`experimentalFragmentArguments` parser option. The AST surface includes
`FragmentArgumentNode`, and execution supports the resulting values.
diff --git a/website/pages/docs/index.mdx b/website/pages/docs/index.mdx
index fde75e5de6..e92178a8d2 100644
--- a/website/pages/docs/index.mdx
+++ b/website/pages/docs/index.mdx
@@ -10,21 +10,29 @@ specification. It provides the parser, validator, executor, type system, and
utilities used to build GraphQL servers, clients, tools, and schema workflows in
JavaScript and TypeScript.
-## Version reference
+## Start here
-| Version area | Start here |
-| --- | --- |
-| Stable v16 API | [v16 API reference](/api-v16/graphql) |
-| v17 release candidate API | [v17 API reference](/api-v17/graphql) |
-| v16 to v17 changes | [What changed in GraphQL.js v17](/upgrade-guides/v16-v17) |
-| v17 specification experiments | [Experimental Specification Features](/docs/experimental-specification-features) |
-| v17 runtime features | [GraphQL Harness](/docs/graphql-harness), [Abort Signals](/docs/abort-signals), [Execution Hooks](/docs/execution-hooks), [Node.js tracing channels](/api-v17/graphql#category-diagnostics) |
+GraphQL.js v16 is the current stable release line, and a v17 release candidate
+is available for final testing and feedback. If you are upgrading from an older
+major version, follow each upgrade guide in sequence until you reach your target
+version.
-The guides in this section describe GraphQL concepts and GraphQL.js behavior.
-The API sections document the public exports by package module.
+- **Upgrade guides:** [v14 to v15](/upgrade-guides/v14-v15),
+ [v15 to v16](/upgrade-guides/v15-v16),
+ [v16 to v17](/upgrade-guides/v16-v17)
+- **API reference:** [v16 stable](/api-v16/graphql),
+ [v17 release candidate](/api-v17/graphql)
+- **Specification experiments:**
+ [Experimental Specification Features](/docs/experimental-specification-features)
+- **GraphQL.js runtime APIs:** [GraphQL Harness](/docs/graphql-harness),
+ [Abort Signals](/docs/abort-signals),
+ [Execution Hooks](/docs/execution-hooks),
+ [Node.js tracing channels](/api-v17/graphql#category-diagnostics)
-GraphQL.js v17 also publishes Node.js `node:diagnostics_channel` tracing
-events, mainly for application performance monitoring (APM) integrations that
-observe parse, validate, execute, subscribe, and resolver lifecycle boundaries.
-See the [Diagnostics API reference](/api-v17/graphql#category-diagnostics) for
-the channel names and payload shapes.
+## More Guides
+
+The documentation sidebar includes longer guides for building schemas,
+executing operations, handling errors, testing GraphQL servers, and preparing a
+GraphQL.js service for production. Start with
+[Getting Started](/docs/getting-started) if you are new to GraphQL.js, or use
+the Core Concepts and Advanced Guides sections for focused topics.
diff --git a/website/pages/docs/schema-coordinates.mdx b/website/pages/docs/schema-coordinates.mdx
index 7a9042b94c..56f01352e1 100644
--- a/website/pages/docs/schema-coordinates.mdx
+++ b/website/pages/docs/schema-coordinates.mdx
@@ -8,7 +8,7 @@ import { Callout } from 'nextra/components';
# Schema Coordinates
- Schema coordinate helpers are available in GraphQL.js v17 and newer. They
+ Schema coordinate helpers are available in GraphQL.js v16.12.0 and newer. They
implement the GraphQL schema-coordinate grammar and resolution semantics,
which have now been merged into the specification work.
diff --git a/website/pages/upgrade-guides/v14-v15.mdx b/website/pages/upgrade-guides/v14-v15.mdx
new file mode 100644
index 0000000000..94611397be
--- /dev/null
+++ b/website/pages/upgrade-guides/v14-v15.mdx
@@ -0,0 +1,598 @@
+---
+title: What changed in GraphQL.js v15
+sidebarTitle: v14 to v15
+---
+
+import { Callout } from 'nextra/components';
+
+{/* cspell:ignore buildschema */}
+
+# What Changed in GraphQL.js v15
+
+
+ GraphQL.js v15 is an older release line. GraphQL.js v16 is the current stable
+ release line, and a v17 release candidate is available for final testing and
+ feedback. If you are upgrading from v14, use this guide first, then continue
+ through [v15 to v16](/upgrade-guides/v15-v16) and
+ [v16 to v17](/upgrade-guides/v16-v17).
+
+
+GraphQL.js v15 is mainly a compatibility cleanup and SDL modernization release.
+It keeps the core v14 request flow: parse, validate, execute, and use the
+schema types from `graphql/type`. Most migration work is around removed
+deprecated utilities, newer schema-language features, and stricter enum and
+scalar coercion behavior. Complete this step before applying later
+major-version guides so failures stay tied to one version boundary.
+
+## Contents
+
+
+
+## Using this Guide
+
+This guide is for projects that depend on GraphQL.js, including applications,
+servers, libraries, and tools. It compares the latest available v14 and v15
+release lines, and focuses on differences that still exist between those lines.
+If a change first appeared during the v15 line but also shipped in the latest
+v14 line, it is not listed here as v14-to-v15 migration work.
+
+Changes described below use these labels:
+
+- **Breaking change:** v14 code may need to change before it runs on v15.
+- **Behavioral tightening:** v15 validates, coerces, or reports a case more
+ precisely.
+- **Deprecation:** the v14 API still works in v15, but should be migrated
+ before v16.
+- **New stable API:** a new public API that can be adopted independently.
+- **Experimental or opt-in:** available in v15, but proposal-backed or outside
+ the default request path.
+
+## Platform and Package Shape
+
+### Node.js and package dependencies
+
+**Breaking change.** GraphQL.js v15 requires Node.js 10 and later:
+
+```json
+{
+ "engines": {
+ "node": ">= 10.x"
+ }
+}
+```
+
+Upgrade Node.js before upgrading GraphQL.js. This separates runtime and
+package-manager errors from GraphQL.js migration errors.
+
+**Breaking change.** GraphQL.js v15 no longer depends on its old iterator
+helper package or `babel-polyfill`. Subscription and async-iterator code uses
+native iterator protocols. If a browser or non-Node runtime does not provide
+the iterator features your application needs, provide those polyfills in the
+host application.
+
+### TypeScript definitions
+
+**Breaking change.** The published TypeScript definitions were reorganized, but
+public imports from `graphql`, `graphql/language`, `graphql/type`,
+`graphql/execution`, `graphql/subscription`, `graphql/utilities`, and
+`graphql/validation` continue to work.
+
+GraphQL.js v15 also removes several TypeScript-only generic parameters that
+did not exist in the JavaScript runtime surface, including the extra data,
+source, and args type parameters on common execution and field types. Let those
+types infer from the remaining public generics instead of passing removed
+parameters.
+
+## Removed Utilities and Renamed Entry Points
+
+### Input coercion helpers
+
+**Breaking change.** The deprecated `coerceValue()` helper was removed. Use
+`coerceInputValue()` for JavaScript input values.
+
+```diff
+- import { coerceValue } from 'graphql/utilities';
++ import { coerceInputValue } from 'graphql/utilities';
+
+- const result = coerceValue(value, inputType);
++ const result = coerceInputValue(value, inputType);
+```
+
+`coerceInputValue()` reports errors through an optional callback. If the
+callback is omitted, it throws on the first invalid value.
+
+**Breaking change.** `isValidJSValue()` and `isValidLiteralValue()` were
+removed. Use `coerceInputValue()` when you need to validate a JavaScript input
+value, and use `validate()` plus the specified validation rules when you need
+to validate a document.
+
+### Introspection query helper
+
+**Breaking change.** The deprecated `introspectionQuery` string constant was
+removed. Use `getIntrospectionQuery()` so callers can request optional
+introspection fields supported by the target server.
+
+```diff
+- import { introspectionQuery } from 'graphql';
++ import { getIntrospectionQuery } from 'graphql';
+
+- const source = introspectionQuery;
++ const source = getIntrospectionQuery();
+```
+
+### Lexer construction
+
+**Breaking change.** `createLexer()` was removed. Use the `Lexer` class with a
+`Source` instance.
+
+```diff
+- import { createLexer, Source } from 'graphql/language';
++ import { Lexer, Source } from 'graphql/language';
+
+ const source = new Source('{ field }');
+- const lexer = createLexer(source);
++ const lexer = new Lexer(source);
+```
+
+`Lexer`, `Location`, and `Token` are public classes in v15. Code that only uses
+`parse()` does not need to construct them directly.
+
+### File-level imports
+
+**Breaking change for file-level imports.** GraphQL.js only treats the root
+package and package-module entry points as semver-stable API boundaries.
+Prefer imports from `graphql`, `graphql/language`, `graphql/type`,
+`graphql/execution`, `graphql/subscription`, `graphql/utilities`, and
+`graphql/validation`.
+
+If you import individual files, v15 changes a few file names and export names.
+The most common cases are summarized in
+[Deep Import Moves](#deep-import-moves).
+
+## Schema Construction and SDL
+
+### Schema extensions in `buildSchema()`
+
+**New stable API.** `buildSchema()` accepts documents that contain type,
+interface, union, enum, input object, scalar, directive, and schema extensions.
+In v14, `buildSchema()` ignored extension definitions and only the original
+definition contributed fields.
+
+```graphql
+type Query {
+ a: String
+}
+
+extend type Query {
+ b: String
+}
+```
+
+In v15, the resulting `Query` type has both `a` and `b`. If you had custom SDL
+preprocessing that separated definitions from extensions before calling
+`buildSchema()`, simplify it and let GraphQL.js apply the extensions.
+
+### Interfaces implementing interfaces
+
+**New stable API.** SDL and code-first schemas can model interfaces that
+implement other interfaces.
+
+```graphql
+interface Node {
+ id: ID
+}
+
+interface Resource implements Node {
+ id: ID
+}
+```
+
+Run `validateSchema()` after migration. Object types implementing an interface
+inherit the same field subtype obligations that apply to directly implemented
+interfaces.
+
+### Schema descriptions
+
+**New stable API.** Schema definitions can have descriptions.
+
+```graphql
+"Public API schema"
+schema {
+ query: Query
+}
+```
+
+`GraphQLSchema` also exposes `description`. Code that serializes schema
+metadata should decide whether to preserve or display the new description
+field.
+
+### Custom scalar specification URLs
+
+**New stable API.** v15 supports the `@specifiedBy` directive and the
+programmatic `specifiedByUrl` scalar configuration field.
+
+```js
+const URLScalar = new GraphQLScalarType({
+ name: 'URL',
+ specifiedByUrl: 'https://example.com/url-spec',
+});
+```
+
+`printSchema()` emits `@specifiedBy(url: ...)`, `buildSchema()` reads it, and
+introspection can include `specifiedByUrl` when requested with
+`getIntrospectionQuery({ specifiedByUrl: true })`.
+
+### Deprecating input values
+
+**New stable API.** v15 supports `@deprecated` on field arguments, directive
+arguments, and input object fields.
+
+```graphql
+input SearchInput {
+ oldField: String @deprecated(reason: "Use newField")
+ newField: String
+}
+
+type Query {
+ search(oldArg: String @deprecated(reason: "Use input")): String
+}
+```
+
+Introspection fields such as `args` and `inputFields` accept
+`includeDeprecated`. Clients and tooling that assume those lists contain only
+currently recommended values should keep the default
+`includeDeprecated: false`.
+
+### Empty deprecation reasons
+
+**Behavioral tightening.** Empty deprecation reasons count as deprecations. In
+v14, a field with `deprecationReason: ''` could appear as not deprecated. In
+v15, the field is deprecated and the empty reason is preserved.
+
+If code checks `field.isDeprecated`, prefer checking
+`field.deprecationReason != null` so the same logic works with the v16 field
+shape too.
+
+## Coercion and Serialization
+
+### Enum values
+
+**Breaking change.** `GraphQLEnumType` no longer preserves `undefined` as a
+custom internal enum value. In v14, `{ value: undefined }` made
+`parseValue()` return `undefined` for that enum name and let
+`serialize(undefined)` return the enum name. In v15, `value: undefined` is
+treated like an omitted `value`, so the internal value becomes the enum name
+and `serialize(undefined)` is invalid.
+
+```diff
+ const Episode = new GraphQLEnumType({
+ name: 'Episode',
+ values: {
+- NEW_HOPE: { value: undefined },
++ NEW_HOPE: {},
+ },
+ });
+```
+
+Omit `value` when the runtime value should be the enum name. If you used
+`undefined` as a real sentinel value, replace it with an explicit runtime value
+before upgrading.
+
+**Behavioral tightening.** `GraphQLEnumType.serialize()`,
+`GraphQLEnumType.parseValue()`, and `GraphQLEnumType.parseLiteral()` throw
+when the provided value does not match a known enum value. v14 returned
+`undefined` for several invalid values.
+
+```js
+const Episode = new GraphQLEnumType({
+ name: 'Episode',
+ values: { NEW_HOPE: { value: 4 } },
+});
+
+Episode.serialize(5); // throws in v15
+```
+
+### Built-in scalars
+
+**Behavioral tightening.** `Float` execution now reports `NaN` serialization
+as a field error. `GraphQLFloat.serialize(NaN)` already threw in the latest v14
+line, but returning `NaN` from a resolver for a `Float` field could still
+produce a null JSON value without a field error. In v15, the field reports the
+serialization error.
+
+`astFromValue(NaN, GraphQLFloat)` also throws in v15.
+
+**New stable behavior.** `GraphQLInt`, `GraphQLFloat`, and `GraphQLBoolean`
+serialize object wrappers through their primitive conversion hooks.
+
+```js
+const value = { valueOf: () => 7 };
+
+GraphQLInt.serialize(value); // 7 in v15
+```
+
+If your resolvers accidentally return boxed or object-wrapped scalar values,
+v15 may now accept them where v14 reported errors. Prefer returning primitive
+JavaScript values from resolvers for predictable behavior.
+
+## Execution and Validation
+
+### Request API object arguments
+
+**New stable API.** v15 accepts object arguments for `graphql()`,
+`graphqlSync()`, `execute()`, `executeSync()`, and `subscribe()`. The v14
+positional forms still work in v15, but the object forms are the
+v16-compatible request API shape.
+
+```diff
+- const result = await graphql(schema, source, rootValue, contextValue);
++ const result = await graphql({
++ schema,
++ source,
++ rootValue,
++ contextValue,
++ });
+```
+
+If you plan to continue to v16, convert these call sites while still on v15 so
+the mechanical API change is separate from the major-version bump.
+
+### Synchronous execution helper
+
+**New stable API.** `executeSync()` is available from the root package and
+`graphql/execution`. Use it when every resolver is expected to complete
+synchronously and an asynchronous resolver should be treated as a programming
+error.
+
+```js
+import { executeSync, parse } from 'graphql';
+
+const result = executeSync({ schema, document: parse('{ viewer { id } }') });
+```
+
+### Execution result and error formatting
+
+**New stable API.** The TypeScript `ExecutionResult` type includes optional
+`extensions`. This matches GraphQL responses that carry out-of-band metadata
+such as tracing identifiers or cache hints.
+
+**New stable API.** `GraphQLError` has `toJSON()`, and the package exports
+`GraphQLFormattedError` types for formatter code. Code that stringifies errors
+through JSON can rely on `toJSON()` rather than duplicating GraphQL error
+formatting logic.
+
+### Deprecated-usage validation
+
+**New stable API.** `NoDeprecatedCustomRule` is available as a validation rule
+for operations that should reject deprecated fields, arguments, input fields,
+or enum values.
+
+```js
+import { NoDeprecatedCustomRule, validate } from 'graphql';
+
+const errors = validate(schema, document, [NoDeprecatedCustomRule]);
+```
+
+`findDeprecatedUsages()` still exists in v15, but a validation rule is easier
+to compose with the rest of the request pipeline.
+
+**New stable API.** `NoSchemaIntrospectionCustomRule` rejects introspection
+fields in executable documents. Use it only for hosts that intentionally block
+introspection.
+
+### Recommended validation rules
+
+**New stable API.** The v15 line includes `recommendedRules` and
+`MaxIntrospectionDepthRule`. `specifiedRules` remains the GraphQL
+specification validation set. `recommendedRules` contains GraphQL.js-specific
+rules that many public servers should consider but that are not required by
+the specification.
+
+```js
+import { recommendedRules, specifiedRules, validate } from 'graphql';
+
+const errors = validate(schema, document, [
+ ...specifiedRules,
+ ...recommendedRules,
+]);
+```
+
+## v15 Compatibility APIs to Clear Before v16
+
+These APIs still work in v15, but v16 removes them or replaces them with
+different names or call shapes. Migrate them during the v15 step when
+possible:
+
+- Convert positional `graphql()`, `graphqlSync()`, `execute()`,
+ `executeSync()`, and `subscribe()` calls to object arguments.
+- Replace `findDeprecatedUsages()` with `validate()` plus
+ `NoDeprecatedCustomRule`.
+- Replace comment descriptions and `commentDescriptions` usage with string or
+ block-string descriptions. Read `description` fields directly instead of
+ calling `getDescription()`.
+- Remove `allowLegacySDLEmptyFields` and
+ `allowLegacySDLImplementsInterfaces`; use valid SDL instead.
+- Replace `GraphQLSchema.isPossibleType()` with `schema.isSubType()`.
+- Replace `field.isDeprecated` and `enumValue.isDeprecated` reads with
+ `deprecationReason != null`.
+- Plan to rename scalar specification URL config and introspection reads from
+ `specifiedByUrl` to `specifiedByURL` when moving to v16.
+
+## Practical Migration Order
+
+Use this order to keep the upgrade reviewable. The checklists below expand
+each category into concrete items.
+
+1. Update Node.js and remove runtime assumptions provided by old package
+ dependencies.
+2. Replace removed utilities and lexer construction helpers.
+3. Update schema construction and SDL features, then run `validateSchema()`.
+4. Update enum, scalar, and input coercion expectations.
+5. Clear v15 compatibility APIs that v16 removes.
+6. Adopt new validation rules and optional v15 schema features where they
+ match your server design.
+
+Run schema validation, operation validation, execution tests, and TypeScript
+checks after each group. Mechanical changes are easiest to review when they are
+separated from behavior changes.
+
+## Detailed Migration Checklists
+
+### Platform and package shape
+
+- Run v15 on Node.js 10 or newer.
+- Provide host-level iterator or browser polyfills if you support runtimes that
+ do not have the native iterator features your application uses.
+- Update TypeScript code that references removed v14-only generic parameters.
+- Prefer public package entry points instead of files under the old top-level
+ declaration tree.
+- If you import from individual files, review
+ [Deep Import Moves](#deep-import-moves) for moved or renamed file paths.
+
+### Removed APIs and required replacements
+
+- Replace `coerceValue()` with `coerceInputValue()`.
+- Replace `isValidJSValue()` and `isValidLiteralValue()` with input coercion or
+ document validation, depending on the source of the value.
+- Replace `introspectionQuery` with `getIntrospectionQuery()`.
+- Replace `createLexer(source, options)` with `new Lexer(source)`.
+- Remove `allowedLegacyNames` usage from `GraphQLSchema` construction.
+
+### Schema and SDL
+
+- Let `buildSchema()` consume extension definitions directly, or remove custom
+ preprocessing that used to compensate for ignored extensions.
+- Validate schemas that use interface inheritance and make sure implementing
+ object types satisfy inherited fields.
+- Preserve `GraphQLSchema.description` if your tooling serializes schema
+ metadata.
+- Use `specifiedByUrl` or `@specifiedBy` for custom scalar specification URLs.
+- Decide how clients and tooling should handle deprecated arguments and input
+ fields.
+- Treat `deprecationReason: ''` as a real deprecation.
+
+### v16 prep
+
+- Convert `graphql()`, `graphqlSync()`, `execute()`, `executeSync()`, and
+ `subscribe()` positional calls to object arguments.
+- Replace `findDeprecatedUsages()` with `validate()` plus
+ `NoDeprecatedCustomRule`.
+- Replace comment descriptions and `commentDescriptions` usage with string or
+ block-string descriptions.
+- Remove `allowLegacySDLEmptyFields` and
+ `allowLegacySDLImplementsInterfaces` usage.
+- Replace `GraphQLSchema.isPossibleType()` with `schema.isSubType()`.
+- Replace `field.isDeprecated` and `enumValue.isDeprecated` reads with
+ `deprecationReason != null`.
+- Rename scalar specification URL config and introspection reads from
+ `specifiedByUrl` to `specifiedByURL` when moving to v16.
+
+### Coercion and execution
+
+- Remove `value: undefined` from enum value configs.
+- Update tests that expected enum `serialize()`, `parseValue()`, or
+ `parseLiteral()` to return `undefined` for invalid values.
+- Update Float execution or `astFromValue()` tests that expected `NaN` to
+ become `null` without an error.
+- Update scalar wrapper tests if you depended on v14 rejecting boxed or
+ object-wrapped primitive values.
+- Use `executeSync()` for intentionally synchronous execution paths.
+- Preserve `ExecutionResult.extensions` in TypeScript helpers that wrap
+ execution results.
+
+### Optional v15 features
+
+- Use `GraphQLError.toJSON()` and `GraphQLFormattedError` types for formatted
+ error output.
+- Use `NoDeprecatedCustomRule` when deprecated schema elements should fail
+ validation.
+- Use `NoSchemaIntrospectionCustomRule` only for hosts that intentionally block
+ introspection.
+- Consider `recommendedRules` and `MaxIntrospectionDepthRule` for public
+ servers.
+- Request `specifiedByUrl` in introspection only when the target server
+ supports it.
+
+## Deep Import Moves
+
+These notes apply only to file-level imports. Prefer package-module imports
+where possible because file-level paths are not semver-stable API boundaries.
+
+- `graphql/utilities/schemaPrinter` moved to
+ `graphql/utilities/printSchema`. The public function names did not change:
+ `printSchema()`, `printType()`, and `printIntrospectionSchema()` keep their
+ names.
+- The `getIntrospectionQuery()` file-level import moved from
+ `graphql/utilities/introspectionQuery` to
+ `graphql/utilities/getIntrospectionQuery`. The function name did not change;
+ the old `introspectionQuery` string constant did not move and should be
+ replaced as described above.
+- Validation rule files were renamed to match the public `*Rule` export
+ convention. For example,
+ `graphql/validation/rules/FieldsOnCorrectType` became
+ `graphql/validation/rules/FieldsOnCorrectTypeRule`, and the exported rule is
+ `FieldsOnCorrectTypeRule`. The same convention applies to the v14 specified
+ validation rules such as `ValuesOfCorrectType`, `NoUnusedVariables`, and
+ `ProvidedRequiredArguments`.
+- The type-only helper `graphql/tsutils/Maybe` moved to
+ `graphql/jsutils/Maybe`, with a named `Maybe` type export instead of the old
+ default type export.
diff --git a/website/pages/upgrade-guides/v15-v16.mdx b/website/pages/upgrade-guides/v15-v16.mdx
new file mode 100644
index 0000000000..5be9ae3a46
--- /dev/null
+++ b/website/pages/upgrade-guides/v15-v16.mdx
@@ -0,0 +1,743 @@
+---
+title: What changed in GraphQL.js v16
+sidebarTitle: v15 to v16
+---
+
+import { Callout } from 'nextra/components';
+
+{/* cspell:ignore executesync graphqlsync */}
+
+# What Changed in GraphQL.js v16
+
+
+ GraphQL.js v16 is the current stable release line on npm, and a v17 release
+ candidate is available for final testing and feedback. If you are upgrading
+ from v15, use this guide first, then continue with
+ [v16 to v17](/upgrade-guides/v16-v17) when you are ready to test the release
+ candidate.
+
+
+GraphQL.js v16 has been the stable major release line since 2021. Upgrading
+from v15 means moving to the established API shape GraphQL.js users have
+relied on throughout the v16 line: request APIs use object arguments,
+long-deprecated SDL and schema helpers are removed, and the published type
+surface changes. The latest v16 line also includes practical tooling and
+specification support such as OneOf input objects, schema coordinates, token
+limits, and directives on directive definitions. Treat this guide as the
+required cleanup before moving into the v17 execution and runtime changes.
+
+## Contents
+
+
+
+## Using this Guide
+
+This guide is for projects that depend on GraphQL.js, including applications,
+servers, libraries, and tools. It compares the latest available v15 and v16
+release lines, and focuses on differences that still exist between those lines.
+If a change first appeared during the v16 line but also shipped in the latest
+v15 line, it is not listed here as v15-to-v16 migration work.
+
+Changes described below use these labels:
+
+- **Breaking change:** v15 code may need to change before it runs on v16.
+- **Behavioral tightening:** v16 validates, coerces, or reports a case more
+ precisely.
+- **Deprecation:** the v15 API still works in v16, but should be migrated
+ before v17.
+- **New stable API:** a new public API that can be adopted independently.
+- **Experimental or opt-in:** available in v16, but proposal-backed or outside
+ the default request path.
+
+## Platform and Package Shape
+
+### Node.js, TypeScript, and Flow
+
+**Breaking change.** GraphQL.js v16 requires Node.js 12.22, 14.16, 16, or
+newer:
+
+```json
+{
+ "engines": {
+ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
+ }
+}
+```
+
+Upgrade Node.js before upgrading GraphQL.js. This separates runtime and
+package-manager errors from GraphQL.js migration errors.
+
+**Breaking change.** The published type definitions target TypeScript 4.1 and
+newer. v16 is implemented in TypeScript, so the package types are generated
+from the source rather than maintained as a parallel declaration tree.
+
+**Breaking change for Flow consumers.** v15 packages shipped `.js.flow` files.
+v16 packages do not. Runtime JavaScript imports are unaffected, but projects
+that consumed GraphQL.js Flow definitions need local definitions, generated
+stubs, or another Flow integration strategy.
+
+**Historical note.** v16 is also where the GraphQL.js source moved from
+Flow-typed JavaScript to TypeScript. The repository Flow configuration,
+checked-in `flow-typed` definitions, and Flow integration test were removed as
+part of that internal migration. The user-facing package change is the removal
+of published `.js.flow` files and the TypeScript declaration baseline above.
+
+### Runtime polyfills
+
+**Breaking change.** v16 drops old runtime and browser polyfills for APIs such
+as `Array.from`, `Array.prototype.find`, `Array.prototype.flatMap`,
+`Object.values`, `Object.entries`, `Symbol`, `Number.isFinite`, and
+`Number.isInteger`.
+
+If you support older browsers or embedded runtimes, provide polyfills in the
+host application. GraphQL.js assumes the JavaScript runtime already has the
+language features required by the supported Node.js range.
+
+### File-level imports
+
+**Breaking change for file-level imports.** GraphQL.js only treats the root
+package and package-module entry points as semver-stable API boundaries.
+Prefer imports from `graphql`, `graphql/language`, `graphql/type`,
+`graphql/execution`, `graphql/utilities`, and `graphql/validation`.
+
+If you import individual files, v16 moves a few file paths and removes some
+remaining validation-rule aliases without the `Rule` suffix. The most common
+cases are summarized in [Deep Import Moves](#deep-import-moves).
+
+## Request APIs
+
+### `graphql()` and `graphqlSync()`
+
+**Breaking change.** `graphql()` and `graphqlSync()` no longer accept
+positional arguments. Pass a single object argument. v15 accepted both forms;
+the object form is the compatibility path to use before upgrading.
+
+```diff
+- const result = await graphql(schema, source, rootValue, contextValue);
++ const result = await graphql({
++ schema,
++ source,
++ rootValue,
++ contextValue,
++ });
+```
+
+In v16, calling `graphql(schema, source)` treats the schema as the whole
+argument object and fails schema validation. Convert call sites mechanically
+before changing resolver or schema behavior.
+
+### `execute()` and `executeSync()`
+
+**Breaking change.** `execute()` and `executeSync()` also accept only the
+object-argument form. This removes the positional compatibility path that was
+still available in v15.
+
+```diff
+- const result = execute(schema, document, rootValue, contextValue);
++ const result = execute({
++ schema,
++ document,
++ rootValue,
++ contextValue,
++ });
+```
+
+Convert on v15 first when you want to separate mechanical API changes from the
+version bump.
+
+### `subscribe()`
+
+**Breaking change.** `subscribe()` accepts only the object-argument form. v15
+accepted both forms; v16 rejects positional subscription calls.
+
+```diff
+- const result = await subscribe(schema, document, rootValue, contextValue);
++ const result = await subscribe({
++ schema,
++ document,
++ rootValue,
++ contextValue,
++ });
+```
+
+**Deprecation.** Subscription APIs are exported from `graphql/execution` in
+v16. The `graphql/subscription` subpath still resolves for compatibility, but
+application code should import from `graphql` or `graphql/execution`.
+
+```diff
+- import { subscribe } from 'graphql/subscription';
++ import { subscribe } from 'graphql/execution';
+```
+
+**Deprecation.** `createSourceEventStream()` still accepts positional
+arguments in v16, but the named-argument form is the migration target before
+moving to v17.
+
+## Removed APIs and Replacements
+
+### Deprecated usage finder
+
+**Breaking change.** `findDeprecatedUsages()` was removed. Use
+`NoDeprecatedCustomRule` with `validate()`.
+
+```diff
+- import { findDeprecatedUsages } from 'graphql/utilities';
++ import { NoDeprecatedCustomRule, validate } from 'graphql/validation';
+
+- const errors = findDeprecatedUsages(schema, document);
++ const errors = validate(schema, document, [NoDeprecatedCustomRule]);
+```
+
+`NoDeprecatedCustomRule` reports deprecated fields, arguments, input fields,
+and enum values through the same validation pipeline as the rest of operation
+validation.
+
+### Comments as descriptions
+
+**Breaking change.** Long-deprecated comments-as-descriptions support was
+removed. Use GraphQL string descriptions.
+
+```diff
+- # User-facing type.
+- type User {
++ "User-facing type."
++ type User {
+ id: ID
+ }
+```
+
+The `commentDescriptions` option on schema utilities no longer has an effect,
+and the old `getDescription()` helper was removed. Read the `description`
+property from AST nodes and schema objects instead.
+
+### Legacy SDL syntax
+
+**Breaking change.** Empty field sets are no longer accepted, even with the
+old `allowLegacySDLEmptyFields` parser option.
+
+```diff
+- type Empty {}
++ type Empty {
++ _empty: Boolean
++ }
+```
+
+Prefer removing empty placeholder types entirely. If a type must remain visible
+in schema tooling, give it an intentional field.
+
+The v15 parser already rejected legacy interface lists such as
+`implements A B` by default, but could accept them with the
+`allowLegacySDLImplementsInterfaces` parser option. v16 removes that option.
+Update any remaining SDL that relied on it to use `implements A & B`.
+
+### Schema and type helpers
+
+**Breaking change.** `GraphQLSchema.isPossibleType()` was removed.
+Use `schema.isSubType(abstractType, maybeSubType)`.
+
+```diff
+- schema.isPossibleType(SomeInterface, SomeObject);
++ schema.isSubType(SomeInterface, SomeObject);
+```
+
+**Breaking change.** `GraphQLField.isDeprecated` and
+`GraphQLEnumValue.isDeprecated` were removed. Check
+`deprecationReason != null`.
+
+```diff
+- if (field.isDeprecated) {
++ if (field.deprecationReason != null) {
+ report(field.deprecationReason);
+ }
+```
+
+This also handles empty deprecation reasons correctly.
+
+### Scalar specification URL casing
+
+**Breaking change.** Programmatic scalar configuration and introspection now
+use `specifiedByURL` instead of `specifiedByUrl`.
+
+```diff
+ const URLScalar = new GraphQLScalarType({
+ name: 'URL',
+- specifiedByUrl: 'https://example.com/url-spec',
++ specifiedByURL: 'https://example.com/url-spec',
+ });
+```
+
+The SDL directive is unchanged:
+
+```graphql
+scalar URL @specifiedBy(url: "https://example.com/url-spec")
+```
+
+The `getIntrospectionQuery({ specifiedByUrl: true })` option name remains
+lowercase `Url` for compatibility, but the introspection field it requests is
+`specifiedByURL`.
+
+## Validation and Execution Behavior
+
+### Validation error limits
+
+**Behavioral tightening.** `validate()` stops after 100 validation errors by
+default and appends an error that says validation was aborted. This prevents a
+single invalid operation from producing unbounded diagnostic work.
+
+If a tool intentionally needs more errors, pass `maxErrors` in the validation
+options.
+
+```js
+const errors = validate(schema, document, undefined, {
+ maxErrors: 500,
+});
+```
+
+### Subscription root fields
+
+**Behavioral tightening.** Subscription operations may not select an
+introspection field as the top-level subscription field.
+
+```graphql
+subscription {
+ __typename
+}
+```
+
+v15 accepted this. v16 reports a validation error. Subscription roots should
+select an actual subscription field from the schema.
+
+### Input values
+
+**Behavioral tightening.** Input coercion no longer treats non-iterable
+array-like objects as lists. Pass real arrays or iterable objects for list
+inputs.
+
+```diff
+- const value = { 0: 'A', 1: 'B', length: 2 };
++ const value = ['A', 'B'];
+```
+
+**Behavioral tightening.** Input object coercion rejects arrays. v15 could
+coerce `[]` as an empty input object. In v16, input object values must be
+ordinary object values with fields keyed by input-field name.
+
+**New stable API.** `execute()` accepts `options.maxCoercionErrors` to cap
+variable coercion errors. The default is 50.
+
+```js
+const result = execute({
+ schema,
+ document,
+ variableValues,
+ options: { maxCoercionErrors: 10 },
+});
+```
+
+### Scalar result coercion
+
+**Behavioral tightening.** A custom scalar `serialize()` function may not
+return `null`. v15 treated that as a null field value. v16 reports a field
+error because scalar serialization is expected to produce a concrete serialized
+value or throw.
+
+```js
+const BadScalar = new GraphQLScalarType({
+ name: 'Bad',
+ serialize() {
+ return null; // field error in v16
+ },
+});
+```
+
+Return a valid serialized value or throw a descriptive error from the scalar.
+
+### GraphQL errors
+
+**New stable API.** `GraphQLError` accepts an options object.
+
+```diff
+- throw new GraphQLError(message, nodes, source, positions, path, originalError);
++ throw new GraphQLError(message, {
++ nodes,
++ source,
++ positions,
++ path,
++ originalError,
++ });
+```
+
+**Behavioral tightening.** `GraphQLError` has the string tag
+`[object GraphQLError]` and its JSON representation includes the specification
+fields. Avoid asserting on `Object.prototype.toString.call(error)` or
+enumerated implementation details. Use `error.toJSON()` for formatted errors.
+
+**Deprecation.** `printError()` and `formatError()` still work in v16, but are
+deprecated. Use `error.toString()` and `error.toJSON()`.
+
+## Language and Tooling
+
+### Visitor shape
+
+**Breaking change.** The fourth visitor shape was removed. Visitors shaped as
+`{ enter: { Field() {} } }` no longer work. Use kind-keyed visitors instead.
+
+```diff
+ visit(document, {
+- enter: {
+- Field(node) {
+- // ...
+- },
++ Field(node) {
++ // ...
+ },
+ });
+```
+
+The `{ Field: { enter() {}, leave() {} } }` shape also remains supported.
+
+**New stable API.** `getEnterLeaveForKind()` is available for code that needs
+to normalize visitor functions by AST kind.
+
+### Const values and operation types
+
+**New stable API.** v16 exposes helpers and value exports that are useful for
+tooling:
+
+- `parseConstValue()`.
+- `isConstValueNode()`.
+- `OperationTypeNode`.
+- `GRAPHQL_MIN_INT` and `GRAPHQL_MAX_INT`.
+
+These are additive; adopt them where they replace local copies or string-based
+constants.
+
+### Parser token limits
+
+**New stable API.** `parse()` accepts `maxTokens` and parsed `DocumentNode`
+objects expose `tokenCount`.
+
+```js
+const document = parse(source, { maxTokens: 10_000 });
+
+console.log(document.tokenCount);
+```
+
+Use `maxTokens` at trust boundaries to reject pathologically large documents
+before validation.
+
+### Executable descriptions
+
+**New stable API.** v16 parses and prints descriptions on operation
+definitions, variable definitions, and named fragment definitions.
+
+```graphql
+"Fetch the viewer profile"
+query Viewer {
+ viewer {
+ id
+ }
+}
+```
+
+Tooling that preserves executable documents should carry the new `description`
+AST fields through visitors, transforms, and printers. Shorthand anonymous
+queries still cannot have descriptions.
+
+### Introspection query depth
+
+**New stable API.** `getIntrospectionQuery()` accepts `typeDepth` to control
+how deeply generated introspection queries recurse through nested `ofType`
+fields.
+
+```js
+const source = getIntrospectionQuery({ typeDepth: 4 });
+```
+
+Lower `typeDepth` can help when a server or gateway applies strict query-depth
+or complexity limits to introspection.
+
+## Schema Features Added in the v16 Line
+
+### Enum value thunks
+
+**New stable API.** `GraphQLEnumType` accepts `values` as a thunk. This matches
+the lazy field configuration pattern used by object and input object types and
+can help code-first schemas avoid construction-order cycles.
+
+```js
+const Episode = new GraphQLEnumType({
+ name: 'Episode',
+ values: () => ({
+ NEW_HOPE: { value: 4 },
+ }),
+});
+```
+
+### OneOf input objects
+
+**Experimental or opt-in.** v16 supports OneOf input objects through the
+`@oneOf` directive in SDL and `isOneOf: true` in code-first schemas.
+
+```graphql
+input ProductSpecifier @oneOf {
+ id: ID
+ name: String
+}
+```
+
+OneOf input objects require exactly one key at runtime. Their fields must be
+nullable and must not define defaults. Introspection exposes `isOneOf`, and
+`getIntrospectionQuery({ oneOf: true })` requests that field.
+
+### Schema coordinates
+
+**New stable API.** v16 exposes schema-coordinate parsing and resolution
+helpers for tooling that needs stable references to schema elements.
+
+```js
+import { resolveSchemaCoordinate } from 'graphql/utilities';
+
+const resolved = resolveSchemaCoordinate(
+ schema,
+ 'Query.search(criteria:)',
+);
+```
+
+Coordinates can resolve named types, fields, input fields, enum values,
+directive definitions, and arguments.
+
+### Directives on directive definitions
+
+**Experimental or opt-in.** The v16 line supports directives on directive
+definitions behind `experimentalDirectivesOnDirectiveDefinitions`.
+
+```js
+const schema = buildSchema(source, {
+ experimentalDirectivesOnDirectiveDefinitions: true,
+});
+```
+
+The directive location is `DIRECTIVE_DEFINITION`. Applied directives are stored
+on directive AST nodes and directive extension AST nodes.
+
+## Additional Deprecations in v16
+
+These APIs still work in v16, but are deprecated for removal in v17:
+
+- Positional `GraphQLError` constructor arguments; use the options object.
+- `printError()` and `formatError()`; use `error.toString()` and
+ `error.toJSON()`.
+- `getOperationRootType()`; use `schema.getRootType(operation)`.
+- `assertValidName()` and `isValidNameError()`; use `assertName()`.
+- The custom `TypeInfo` fifth argument to `validate()`; use
+ `visitWithTypeInfo()` for custom traversals.
+- `TypeInfo` `getFieldDefFn` customization.
+- `assertValidExecutionArguments()`; use `assertValidSchema()` or migrate to
+ the v17 execution-argument validation helpers when you move beyond v16.
+- `graphql/subscription`; import subscription APIs from `graphql` or
+ `graphql/execution`.
+- Positional `createSourceEventStream()` arguments; use the named-argument
+ form in v16, then call `validateSubscriptionArgs()` before
+ `createSourceEventStream()` when moving to v17.
+
+## Practical Migration Order
+
+Use this order to keep the upgrade reviewable. The checklists below expand
+each category into concrete items.
+
+1. Update Node.js, TypeScript, and runtime polyfill assumptions.
+2. Convert `graphql()`, `execute()`, and `subscribe()` call sites to
+ object-style arguments while still on v15 if possible.
+3. Replace removed APIs and legacy SDL/comment-description syntax.
+4. Update scalar, input coercion, subscription, and validation behavior tests.
+5. Migrate deprecated compatibility APIs that still work in v16 but should be
+ gone before v17.
+6. Adopt optional v16 features such as OneOf, schema coordinates, parser token
+ limits, and directive-definition directives only where they match your
+ server design.
+
+Run schema validation, operation validation, execution tests, and TypeScript
+checks after each group. Mechanical changes are easiest to review when they are
+separated from behavior changes.
+
+## Detailed Migration Checklists
+
+### Platform and package shape
+
+- Run v16 on Node.js 12.22, 14.16, 16, or newer.
+- Keep TypeScript at 4.1 or newer.
+- Replace any dependency on GraphQL.js `.js.flow` package files with local Flow
+ definitions, generated stubs, or another Flow integration strategy.
+- Provide host-level polyfills if you support runtimes older than the v16
+ JavaScript baseline.
+- Audit file-level imports against the appendix below and prefer public
+ package entry points such as `graphql`, `graphql/execution`,
+ `graphql/language`, `graphql/type`, `graphql/utilities`, and
+ `graphql/validation`.
+
+### Required API replacements
+
+- Convert `graphql()` and `graphqlSync()` positional calls to object arguments.
+- Convert `execute()` and `executeSync()` positional calls to object arguments.
+- Convert `subscribe()` positional calls to object arguments.
+- Replace `findDeprecatedUsages()` with `validate()` plus
+ `NoDeprecatedCustomRule`.
+- Replace comment descriptions with string or block-string descriptions.
+- Remove `getDescription()` usage and read `description` fields directly.
+- Replace `GraphQLSchema.isPossibleType()` with `schema.isSubType()`.
+- Replace `field.isDeprecated` and `enumValue.isDeprecated` checks with
+ `deprecationReason != null`.
+- Rename scalar config and introspection reads from `specifiedByUrl` to
+ `specifiedByURL`.
+
+### SDL and schema validation
+
+- Replace empty object, interface, and input object definitions with real
+ fields or remove the placeholder types.
+- Remove `allowLegacySDLImplementsInterfaces` and replace any legacy
+ `implements A B` syntax with `implements A & B`.
+- Run `validateSchema()` after updating OneOf input objects, interface
+ inheritance, and scalar specification URLs.
+- Check subscription operations for top-level introspection selections.
+
+### Coercion, execution, and errors
+
+- Replace array-like list input values with arrays or iterable objects.
+- Replace array values passed for input object types with object values keyed by
+ input-field name.
+- Update custom scalar tests so `serialize()` returns a valid value or throws,
+ never `null`.
+- Decide whether `execute({ options: { maxCoercionErrors } })` should cap
+ variable coercion diagnostics for your host.
+- Update tests that asserted unlimited validation errors.
+- Use `GraphQLError` options objects in new code.
+- Use `error.toJSON()` and `error.toString()` instead of `formatError()` and
+ `printError()`.
+
+### Deprecated compatibility APIs
+
+- Migrate `getOperationRootType()` to `schema.getRootType(operation)`.
+- Migrate `assertValidName()` and `isValidNameError()` to `assertName()`.
+- Remove the custom `TypeInfo` argument to `validate()`.
+- Remove `TypeInfo` `getFieldDefFn` customizations.
+- Replace `graphql/subscription` imports with `graphql` or
+ `graphql/execution`.
+- Keep `createSourceEventStream()` call sites on named arguments.
+
+### Optional v16 features
+
+- Use `parse(source, { maxTokens })` and inspect `document.tokenCount` at trust
+ boundaries.
+- Preserve executable `description` fields when transforming or printing parsed
+ operations and fragments.
+- Use `getIntrospectionQuery({ typeDepth })` when generated introspection
+ queries need to fit server depth or complexity limits.
+- Use `GraphQLEnumType` `values` thunks only where lazy enum construction helps
+ code-first schema setup.
+- Use `GraphQLOneOfDirective`, SDL `@oneOf`, or `isOneOf: true` only when
+ exactly-one input semantics are intended.
+- Use `resolveSchemaCoordinate()` for tooling that stores schema element
+ references.
+- Enable `experimentalDirectivesOnDirectiveDefinitions` only for hosts that
+ intentionally support directives applied to directive definitions or
+ directive extensions.
+- Use additive tooling helpers such as `parseConstValue()`,
+ `isConstValueNode()`, `getEnterLeaveForKind()`, `OperationTypeNode`,
+ `GRAPHQL_MIN_INT`, and `GRAPHQL_MAX_INT` where they replace local code.
+
+## Deep Import Moves
+
+These notes apply only to file-level imports. Prefer package-module imports
+where possible because file-level paths are not semver-stable API boundaries.
+
+- Subscription implementation files moved under execution. Replace
+ `graphql/subscription/subscribe` with `graphql/execution/subscribe`, or
+ preferably import `subscribe` and `createSourceEventStream` from
+ `graphql/execution`. The public function names did not change.
+- The internal async-iterator helper moved from
+ `graphql/subscription/mapAsyncIterator` to
+ `graphql/execution/mapAsyncIterator`. The export changed from a default
+ export to the named `mapAsyncIterator` export.
+- `graphql/error/formatError` was folded into
+ `graphql/error/GraphQLError` and the `graphql/error` package module.
+ `formatError()` still exists in v16, but it is deprecated; prefer
+ `error.toJSON()`.
+- Remaining validation-rule file aliases without the `Rule` suffix were
+ removed. Use the suffixed `*Rule` file and export names, for example
+ `graphql/validation/rules/ExecutableDefinitionsRule` and
+ `ExecutableDefinitionsRule`, or
+ `graphql/validation/rules/UniqueTypeNamesRule` and `UniqueTypeNamesRule`.
diff --git a/website/pages/upgrade-guides/v16-v17.mdx b/website/pages/upgrade-guides/v16-v17.mdx
index 92395a4e60..713440d3ab 100644
--- a/website/pages/upgrade-guides/v16-v17.mdx
+++ b/website/pages/upgrade-guides/v16-v17.mdx
@@ -5,11 +5,13 @@ sidebarTitle: v16 to v17
import { Callout } from 'nextra/components';
+{/* cspell:ignore graphqlerror graphqlsync */}
+
# What Changed in GraphQL.js v17
- GraphQL.js v17 is currently available as `17.0.0-rc.0`. This guide
- describes migration-impacting changes from the v16 stable line to the v17
+ GraphQL.js v17 has a release candidate available for final testing and
+ feedback. Use this guide to prepare v16 projects for testing the
release-candidate line.
@@ -21,11 +23,121 @@ development checks are opt-in. Host integration features such as harnesses,
abort signals, execution hooks, and Node.js tracing channels are explicit
GraphQL.js runtime APIs.
-## Reading the Labels
-
-Migration items below use these labels:
+## Contents
+
+
+
+## Using this Guide
+
+This guide is for projects that depend on GraphQL.js, including applications,
+servers, libraries, and tools. It compares the latest v16 release line with the
+current v17 release candidate, and focuses on differences you will see when
+testing that RC from an up-to-date v16 project. If a change is already present
+in both lines, including v17 work that also shipped in v16, it is not listed
+here as v16-to-v17 migration work.
+
+Changes described below use these labels:
- **Breaking change:** v16 code may need to change before it runs on v17.
+- **Breaking change for earlier v17 alpha users:** code that adopted an
+ experimental v17 alpha feature may need to change; it is not stable
+ v16-to-v17 migration work.
- **Behavioral tightening:** v17 validates or reports a case more precisely.
- **Deprecation:** the v16 API still works in v17, but should be migrated
before v18.
@@ -67,6 +179,18 @@ than semver-stable API boundaries: GraphQL.js only promises semver
compatibility for the root package and package-module entry points. Prefer the
public entry points above for application and library code.
+If you import individual files, v17 has fewer true moves
+than older release lines. The moved or renamed cases are summarized
+in [Deep Import Moves](#deep-import-moves). Removed APIs and compatibility
+paths are covered in the sections where they affect public imports.
+
+The v17 package also contains a matching `__dev__/` build tree. When your
+runtime or bundler honors package conditions, import the normal `graphql/...`
+specifier and select the `development` condition so package resolution chooses
+that build. If you are loading files in an environment that does not use
+package conditions, such as direct CDN file URLs, import from the matching
+`__dev__/` file explicitly when you need the development build.
+
**Breaking change.** The deprecated `graphql/subscription` compatibility
subpath is gone. Import subscription APIs from `graphql` or
`graphql/execution`.
@@ -122,8 +246,9 @@ simple hosts use common v17 options without rebuilding the whole pipeline.
### GraphQL Harness
-**New stable API.** `GraphQLHarness` lets hosts replace the parse, validate,
-execute, and subscribe phases used by `graphql()` and `graphqlSync()`.
+**New stable API.** The `GraphQLHarness` TypeScript interface and
+`defaultHarness` runtime object let hosts replace the parse, validate, execute,
+and subscribe phases used by `graphql()` and `graphqlSync()`.
The harness is modeled after Envelop-style plugin pipelines. It brings the
broader phase types used by that ecosystem closer to the reference
@@ -143,10 +268,10 @@ Use [GraphQL Harness](/docs/graphql-harness) for examples, and call
### Single-result execution
-**Breaking change.** `execute()` is now the stable single-result executor. It
-does not support incremental delivery. If a schema or operation opts into
-`@defer` or `@stream`, execute it with `experimentalExecuteIncrementally()`
-instead.
+**Breaking change for earlier v17 alpha users.** `execute()` is now the stable
+single-result executor. It does not support incremental delivery. If a schema
+or operation opts into `@defer` or `@stream`, execute it with
+`experimentalExecuteIncrementally()` instead.
```diff
- import { execute } from 'graphql';
@@ -156,11 +281,13 @@ instead.
This keeps the `execute()` contract simple: callers receive one
`ExecutionResult`, or a promise for one.
-**Breaking change.** Incremental execution no longer uses the old
-`singleResult` discriminator. Remove branches that check for `singleResult`.
-
### Incremental delivery
+The incremental-delivery notes in this section compare the v17 release
+candidate with earlier v17 alpha releases of this experimental feature. The
+current payload model follows the GraphQL WG
+[defer/stream RFC payload format](https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#payload-format).
+
**Experimental or opt-in.** `experimentalExecuteIncrementally()` returns either
a normal `ExecutionResult` or an object with `initialResult` and an async
iterator of `subsequentResults`.
@@ -180,11 +307,16 @@ if ('initialResult' in result) {
```
**Experimental or opt-in.** `legacyExecuteIncrementally()` remains available
-for hosts that still need the older incremental delivery payload shape. The
-legacy shape identifies deferred and streamed payloads with fields such as
-`path` and optional `label`, and can duplicate field data across payloads. The
-current experimental shape registers pending work by `id` and reports
-completion with `completed` entries.
+for hosts that still need the older incremental delivery payload shape from
+earlier v17 alpha releases. The legacy shape identifies deferred and streamed
+payloads with fields such as `path` and optional `label`, and can duplicate
+field data across payloads. The current experimental shape registers pending
+work by `id` and reports completion with `completed` entries.
+
+**Breaking change for earlier v17 alpha users.** The legacy executor preserves
+that older incremental payload format, but the even older return type shape
+with `singleResult` as a discriminator is gone. Remove branches that check for
+`singleResult`.
Schema setup, directive validation, result shapes, and transport guidance are
covered in [Defer and Stream](/docs/defer-stream).
@@ -197,6 +329,12 @@ only to mutation and subscription root selections, including through fragments
whose type condition is an interface implemented by the root type. Query root
selections may use incremental delivery.
+When a fragment is shared between query and subscription root fields, disable
+incremental behavior on the subscription path with a variable-backed `if`
+argument on `@defer` or `@stream`, or with applicable `@skip` and `@include`
+directives. Root-selection restrictions are separate: do not place `@defer` or
+`@stream` on mutation or subscription root selections themselves.
+
### Resolver return values
**New stable API.** List fields can resolve to async iterables. This is useful
@@ -238,10 +376,11 @@ of always returning a promise. Existing `await subscribe(args)` code continues
to work, but TypeScript code that assumed a promise return type must be updated
to handle the synchronous path.
-**Breaking change.** `subscribe()` does not support incremental delivery. If a
-fragment is shared between queries and subscriptions, use a variable-backed
-`if` argument on `@defer` or `@stream`, or applicable `@skip` and `@include`
-directives, to disable incremental behavior in subscription operations.
+**Breaking change for earlier v17 alpha users.** `subscribe()` does not support
+incremental delivery. If a fragment is shared between query and subscription
+root fields, use a variable-backed `if` argument on `@defer` or `@stream`, or
+applicable `@skip` and `@include` directives, to disable incremental behavior
+in subscription operations.
### Lower-level subscription helpers
@@ -258,7 +397,9 @@ asserts that the selected operation is a subscription.
stream to execution results. Its third argument is a `RootSelectionSetExecutor`,
which customizes how each source event is executed after the source stream has
already been created. If you omit it, GraphQL.js uses the default
-subscription-event executor.
+subscription-event executor, `executeSubscriptionEvent()`. Call
+`executeSubscriptionEvent()` directly only when you already have
+`ValidatedSubscriptionArgs` and want to execute one subscription source event.
See [Advanced Execution Pipelines](/docs/advanced-execution-pipelines) for
complete helper examples.
@@ -283,6 +424,11 @@ GraphQL.js also accepts an external `abortSignal` on `graphql()`, `execute()`,
resolver-scoped signal with `info.getAbortSignal()` and pass it to downstream
APIs that support cancellation.
+When an external abort stops execution after a partial result exists,
+GraphQL.js rejects with `AbortedGraphQLExecutionError`. The error exposes the
+abort cause and the partial execution result so hosts can decide whether to log,
+discard, or surface partial data according to their transport policy.
+
At this time, GraphQL.js does not expose fine-grained per-field cancellation.
Resolvers in an operation share one signal. For internally cancelled portions
of an operation, GraphQL.js aborts that shared resolver signal when the result
@@ -318,10 +464,10 @@ such as `graphql:parse`, `graphql:validate`, `graphql:execute`,
execution code.
The channels are resolved at module load and no-op on runtimes that do not
-provide `node:diagnostics_channel`. GraphQL.js also exports TypeScript context
-types for strongly typed subscribers. See the
-[Diagnostics API reference](/api-v17/graphql#category-diagnostics) for the
-channel names and payload shapes.
+provide `node:diagnostics_channel`. Import `graphql/diagnostics` for the
+channel objects and TypeScript context types for strongly typed subscribers.
+See the [Diagnostics API reference](/api-v17/graphql#category-diagnostics) for
+the channel names and payload shapes.
## Input Coercion, Defaults, and Custom Scalars
@@ -359,6 +505,27 @@ incorrectly from an already-coerced internal value.
See [Passing Arguments](/docs/passing-arguments) and
[Mutations and Input Types](/docs/mutations-and-input-types).
+### `undefined` and omitted input values
+
+**Behavioral tightening.** v17 continues the cleanup of JavaScript-specific
+`undefined` handling by treating `undefined` as absence at user-visible input
+boundaries. Known input-object fields with value `undefined` were already
+generally treated as omitted in the latest v16 line; v17 closes inconsistent
+cases around variables and unknown fields.
+
+The v16-to-v17 differences to audit are:
+
+- `variableValues: { x: undefined }` is treated as if `x` was not provided.
+ Operation and fragment variable defaults can apply, and nullable variables
+ with no default are omitted from the coerced variable map instead of being
+ coerced to `null`.
+- In input object literals such as `{ a: $x }`, an omitted or explicitly
+ `undefined` variable omits the field, so an input-field default can apply.
+ An explicit `null` variable still overrides the default for nullable fields.
+- Unknown fields with value `undefined` in JavaScript input objects are ignored
+ by `coerceInputValue()`, `validateInputValue()`, and `valueToLiteral()`.
+ Unknown fields with any other value are still invalid.
+
### Coercion and validation helpers
**Behavioral change.** `coerceInputValue()` and `coerceInputLiteral()` now
@@ -374,6 +541,24 @@ scalar literals. GraphQL.js calls it automatically during execution. If you use
literal coercion helpers directly outside execution, call `replaceVariables()`
yourself before coercing literals that may contain variables.
+### Built-in scalar `bigint` values
+
+**New stable behavior.** The built-in scalar coercers accept JavaScript
+`bigint` values in the cases where the scalar can represent them.
+
+- `GraphQLInt` accepts `bigint` input and output values within the GraphQL
+ 32-bit integer range.
+- `GraphQLFloat` accepts `bigint` input and output values that can be converted
+ to a JavaScript number without losing precision.
+- `GraphQLID` accepts `bigint` input and output values and serializes them as
+ strings.
+- `GraphQLString` and `GraphQLBoolean` accept `bigint` values during result
+ coercion only.
+
+GraphQL variable JSON and GraphQL literals still do not have a separate BigInt
+literal format. This is a JavaScript runtime coercion change for hosts that pass
+`bigint` values directly.
+
### Custom scalar method names
**Deprecation.** v17 introduces scalar method names that match the GraphQL
@@ -407,6 +592,15 @@ about where values came from.
**Breaking change.** `info.variableValues` follows the same model. Use
`info.variableValues.coerced` for runtime values inside resolvers.
+**Behavioral tightening.** v16 already used null-prototype object maps
+internally while preparing execution and coercion data. v17 keeps that shape
+through the user-visible boundary for performance, so resolver argument
+objects, coerced input object values, and the `coerced` maps inside
+`VariableValues` are null-prototype objects when user code receives them. Use
+`Object.keys()`, `Object.entries()`, `Object.hasOwn()`, or other APIs that do
+not rely on inherited `Object.prototype` methods. Avoid calling methods such as
+`args.hasOwnProperty(...)` directly.
+
**New stable API.** `GraphQLResolveInfo` adds `getAbortSignal()` and
`getAsyncHelpers()` for the abort-signal and execution-hook APIs.
@@ -429,12 +623,19 @@ non-null. In v16 the argument type was nullable `String`, so a directive use
could explicitly pass `reason: null`. In v17, omit `reason` to use the default
deprecation reason; explicit `null` is no longer valid.
+**Behavioral tightening.** The `includeDeprecated` arguments on
+`__Type.fields`, `__Type.enumValues`, `__Type.inputFields`, and `__Field.args`
+are now `Boolean! = false`. The `includeDeprecated` argument on
+`__Schema.directives` already had that shape in the latest v16 line. Clients
+should omit the argument or pass a Boolean value; queries that pass
+`includeDeprecated: null` to those fields are invalid in v17.
+
### Programmatic schema APIs
-**New stable API.** v17 exposes public schema element types and assertions for
-programmatic schema work, including `GraphQLField`, `GraphQLArgument`,
-`GraphQLInputField`, `GraphQLEnumValue`, `assertField()`, `assertArgument()`,
-`assertInputField()`, and `assertEnumValue()`.
+**New stable API.** v17 exposes TypeScript schema element types and runtime
+assertions for programmatic schema work, including `GraphQLField`,
+`GraphQLArgument`, `GraphQLInputField`, `GraphQLEnumValue`, `assertField()`,
+`assertArgument()`, `assertInputField()`, and `assertEnumValue()`.
**New stable API.** `GraphQLSchema.getField(parentType, fieldName)` resolves
ordinary fields and GraphQL meta fields such as `__typename`, `__schema`, and
@@ -470,10 +671,10 @@ from a public entry point, replace that import with
const { enter, leave } = getEnterLeaveForKind(visitor, Kind.FIELD);
```
-**Behavioral change.** Empty AST collections may be omitted as `undefined`.
-Code that reads properties such as `arguments`, `directives`,
-`variableDefinitions`, `interfaces`, `fields`, `types`, or `operationTypes`
-should treat them as optional.
+**Behavioral change.** The parser may omit optional AST collection properties
+as `undefined` instead of storing empty arrays. Code that reads properties such
+as `arguments`, `directives`, `variableDefinitions`, `interfaces`, `fields`,
+`types`, or `operationTypes` should treat them as optional.
**New stable API.** `isSubscriptionOperationDefinitionNode()` narrows
subscription operation nodes.
@@ -491,9 +692,10 @@ spreads can provide values. See
## Validation
-**Breaking change.** Passing a custom `TypeInfo` instance as a fifth argument
-to `validate()` was removed. If you need a custom traversal with custom type
-tracking, compose your visitor with `visitWithTypeInfo()`.
+**Breaking change.** The public `validate()` signature no longer accepts a
+custom `TypeInfo` instance as a fifth argument. JavaScript callers can still
+pass an extra argument, but v17 ignores it. If you need a custom traversal with
+custom type tracking, compose your visitor with `visitWithTypeInfo()`.
```diff
- const errors = validate(schema, document, rules, options, customTypeInfo);
@@ -590,9 +792,9 @@ separated from behavior changes.
- Keep TypeScript at 4.4 or newer.
- Prefer package-module entry points such as `graphql`, `graphql/execution`,
`graphql/language`, `graphql/type`, `graphql/utilities`, and
- `graphql/validation` for semver-stable imports. Audit deep imports below
- those modules, since they are exposed but are not semver-stable API
- boundaries.
+ `graphql/validation` for semver-stable imports. Audit file-level imports
+ against the appendix below, since they are exposed but are not
+ semver-stable API boundaries.
- Replace `graphql/subscription` imports with `graphql` or
`graphql/execution`.
- Enable development mode explicitly in development environments, either with
@@ -604,7 +806,7 @@ separated from behavior changes.
- Replace `KindEnum`, `TokenKindEnum`, and `DirectiveLocationEnum` with `Kind`,
`TokenKind`, and `DirectiveLocation`.
- Replace `getVisitFn()` with `getEnterLeaveForKind()`.
-- Remove the custom `TypeInfo` fifth argument to `validate()`; use
+- Remove the custom `TypeInfo` fifth argument from `validate()` call sites; use
`visitWithTypeInfo()` for custom traversals.
- Replace `assertValidName()` and `isValidNameError()` with `assertName()`.
- Replace `assertValidExecutionArguments()` with `assertValidSchema()` or
@@ -625,6 +827,8 @@ separated from behavior changes.
directive-argument defaults.
- Prefer `default: { value }` for raw JavaScript input values and
`default: { literal }` for GraphQL literals.
+- Audit tests and integrations that pass `undefined` in `variableValues` or
+ JavaScript input objects; v17 treats those values as omitted in more places.
- Update direct `coerceInputValue()` and `coerceInputLiteral()` callers that
expect diagnostic errors; use `validateInputValue()` or
`validateInputLiteral()` when you need errors.
@@ -636,25 +840,34 @@ separated from behavior changes.
instead of `result.coerced`.
- Update resolvers that read `info.variableValues` to use
`info.variableValues.coerced`.
+- Treat user-visible resolver args, coerced input object values, and
+ `VariableValues.coerced` as null-prototype maps.
+- Update clients or tests that passed `includeDeprecated: null` in
+ introspection queries.
+- Update built-in scalar tests for JavaScript `bigint` values if your host
+ passes `bigint` values directly.
### Execution and subscriptions
- Use `execute()` only for stable single-result execution.
- Use `experimentalExecuteIncrementally()` for operations that may use active
`@defer` or `@stream`.
-- Remove code that checks the old incremental `singleResult` discriminator.
+- If you tested earlier v17 alpha releases, remove code that checks the old
+ incremental `singleResult` discriminator.
- Keep `legacyExecuteIncrementally()` only for hosts that still need the older
- incremental payload shape.
+ incremental payload shape from earlier v17 alpha releases.
- Use `validateExecutionArgs()` before lower-level execution helpers such as
`executeRootSelectionSet()`, `experimentalExecuteRootSelectionSet()`, and
`legacyExecuteRootSelectionSet()`.
- Handle `subscribe()` returning either a value or a promise.
- Call `validateSubscriptionArgs()` before `createSourceEventStream()`.
+- Use `executeSubscriptionEvent()` only when executing one validated
+ subscription event directly.
- Replace subscription `perEventExecutor` usage with
`mapSourceToResponseEvent()` and its root-selection-set executor argument.
-- Disable incremental behavior in shared subscription fragments with a
- variable-backed `if` argument on `@defer` or `@stream`, or applicable `@skip`
- and `@include` directives.
+- Disable incremental behavior in fragments shared between query and
+ subscription root fields with a variable-backed `if` argument on `@defer` or
+ `@stream`, or applicable `@skip` and `@include` directives.
### Deprecated compatibility APIs
@@ -683,11 +896,33 @@ These v16 APIs still work in v17, but are deprecated for removal in v18:
- Use `hideSuggestions` when public diagnostics should omit schema suggestions.
- Use `abortSignal` and `info.getAbortSignal()` when a host can propagate
cancellation to downstream work.
+- Handle `AbortedGraphQLExecutionError` if your host needs access to partial
+ execution results after an external abort.
- Use `info.getAsyncHelpers()` and `asyncWorkFinished` hooks when a host needs
a cleanup or telemetry boundary after tracked async work settles.
- Use Node.js tracing channels primarily for application performance
monitoring (APM) integrations.
-- Consider `GraphQLHarness` when a host needs to customize `graphql()` parse,
- validate, execute, or subscribe phases.
+- Consider a custom `GraphQLHarness` object when a host needs to customize
+ `graphql()` parse, validate, execute, or subscribe phases.
- Enable `experimentalFragmentArguments` only for hosts that intentionally
support arguments on named fragment spreads.
+
+## Deep Import Moves
+
+These notes apply only to file-level imports. Prefer package-module imports
+where possible because file-level paths are not semver-stable API boundaries.
+
+- `graphql/execution/subscribe` was folded into `graphql/execution/execute`.
+ The public function names did not change; prefer importing `subscribe` and
+ `createSourceEventStream` from the `graphql/execution` package module.
+- The internal helper `graphql/execution/mapAsyncIterator` was renamed to
+ `graphql/execution/mapAsyncIterable`. It remains internal, and the v17
+ helper no longer preserves a custom async-generator return value.
+- `graphql/utilities/findBreakingChanges` moved to
+ `graphql/utilities/findSchemaChanges`. The deprecated
+ `findBreakingChanges()` and `findDangerousChanges()` exports still exist
+ there as migration bridges; prefer `findSchemaChanges()`.
+- `graphql/utilities/assertValidName` has no same-name file replacement. Use
+ `assertName` from `graphql` or `graphql/type`, or from the file
+ `graphql/type/assertName` if you must keep a file-level import. There is no
+ direct replacement for `isValidNameError()`.