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()`.