diff --git a/cspell.yml b/cspell.yml
index 49356561a7..a7e255dc1e 100644
--- a/cspell.yml
+++ b/cspell.yml
@@ -16,11 +16,13 @@ overrides:
dictionaries:
- fullstack
words:
- - clsx
- - infima
- - noopener
+ - callout
- Vite
- craco
+ - rollup
+ - Rsbuild
+ - Rspack
+ - Turbopack
- esbuild
- swcrc
- noreferrer
diff --git a/website/pages/docs/_meta.ts b/website/pages/docs/_meta.ts
index 87858e5b73..79f17fa754 100644
--- a/website/pages/docs/_meta.ts
+++ b/website/pages/docs/_meta.ts
@@ -51,6 +51,7 @@ const meta = {
type: 'separator',
title: 'Production & Scaling',
},
+ 'development-mode': '',
'going-to-production': '',
'scaling-graphql': '',
};
diff --git a/website/pages/docs/development-mode.mdx b/website/pages/docs/development-mode.mdx
new file mode 100644
index 0000000000..194b274ed1
--- /dev/null
+++ b/website/pages/docs/development-mode.mdx
@@ -0,0 +1,396 @@
+---
+title: Development Mode
+---
+
+import { Callout } from 'nextra/components';
+
+# Development Mode
+
+
+ In v16 and earlier, development mode is enabled by default. Starting in v17, it
+ is disabled by default.
+
+
+
+ In v16 and earlier, `NODE_ENV=production` disables development checks. Starting
+ in v17, development mode is controlled by exports conditions and `NODE_ENV` is
+ ignored.
+
+
+In development mode, GraphQL.js can provide an additional runtime check appropriate
+for development-time errors: the erroneous inclusion of multiple GraphQL.js modules.
+
+In v16 and earlier, development mode is enabled by default and controlled via
+`NODE_ENV`. In v17, development mode is disabled by default to best ensure that
+production builds do not incur the performance and bundle size penalties associated
+with the additional checks.
+
+In v17, development mode is enabled only when the `development` exports condition
+is active (some tool chains enable this automatically in development builds) or when
+you call `enableDevMode()` in user code. The `NODE_ENV` environment variable has no
+effect on development mode in v17.
+
+## Multiple GraphQL.js Modules
+
+Only a single GraphQL.js module can be used within a project. Different GraphQL.js
+versions cannot be used at the same time since different versions may have different
+capabilities and behavior. The data from one version used in the function from
+another could produce confusing and spurious results.
+
+Duplicate modules of GraphQL.js of the same version may also fail at runtime,
+sometimes in unexpected ways. This happens because GraphQL.js relies on
+module-scoped objects for key features. For example, GraphQL.js uses `instanceof`
+checks (v16) or unique symbols (v17) internally to distinguish between different
+schema elements, which underpin the exported predicates such as `isScalarType()`,
+`isObjectType()`, etc. Similarly, the exported constant `BREAK` allows library
+users to control visitor behavior, but will fail when passed to a duplicate module.
+
+To ensure that only a single GraphQL.js module is used, all libraries depending on
+GraphQL.js should use the appropriate peer dependency mechanism, as provided by
+their package manager, bundler, build tool, or runtime.
+
+In development mode, GraphQL.js provides validation checks that should catch most
+cases of multiple GraphQL.js modules being used within the same project.
+
+This additional validation is unnecessary in production, where the GraphQL.js
+library is expected to have been set up correctly as a single module. In v16 and
+earlier, this check is included by default and must be disabled by setting
+`NODE_ENV=production`. In v17, it is only included when the `development` exports
+condition is explicitly enabled or when `enableDevMode()` is called.
+
+## Configuring Development Mode
+
+### v16 and earlier
+
+Through v16, development mode is enabled by default and must be disabled in production
+by setting `NODE_ENV=production`.
+
+```bash
+NODE_ENV=development node server.js
+NODE_ENV=production node server.js
+```
+
+Bundlers typically replace `process.env.NODE_ENV` at build time. See
+`process.env.NODE_ENV` at build time, allowing development-only code paths to be
+removed for production builds.
+
+The following examples show how to configure common bundlers to set
+`process.env.NODE_ENV` and remove development-only code:
+
+#### Vite
+
+```js
+// vite.config.js
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+ define: {
+ 'process.env.NODE_ENV': '"production"',
+ },
+});
+```
+
+#### Next.js
+
+When you build your application with `next build` and run it using `next start`,
+Next.js sets `process.env.NODE_ENV` to `'production'` automatically. No
+additional configuration is required.
+
+```bash
+next build
+next start
+```
+
+If you run a custom server, make sure `NODE_ENV` is set manually.
+
+#### Create React App (CRA)
+
+To customize Webpack behavior in CRA, you can use a tool like
+[`craco`](https://craco.js.org/). This example uses CommonJS syntax instead of
+ESM syntax, which is required by `craco.config.js`:
+
+```js
+// craco.config.js
+const webpack = require('webpack');
+
+module.exports = {
+ webpack: {
+ plugins: [
+ new webpack.DefinePlugin({
+ 'globalThis.process': JSON.stringify(true),
+ 'process.env.NODE_ENV': JSON.stringify('production'),
+ }),
+ ],
+ },
+};
+```
+
+#### esbuild
+
+```json
+{
+ "define": {
+ "globalThis.process": true,
+ "process.env.NODE_ENV": "production"
+ }
+}
+```
+
+#### Webpack
+
+```js
+// webpack.config.js
+import { fileURLToPath } from 'url';
+import { dirname } from 'path';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+
+export default {
+ mode: 'production', // Automatically sets NODE_ENV
+ context: __dirname,
+};
+```
+
+#### Rollup
+
+```js
+// rollup.config.js
+import replace from '@rollup/plugin-replace';
+
+export default {
+ plugins: [
+ replace({
+ preventAssignment: true,
+ 'process.env.NODE_ENV': JSON.stringify('production'),
+ }),
+ ],
+};
+```
+
+#### SWC
+
+```json filename=".swcrc"
+{
+ "jsc": {
+ "transform": {
+ "optimizer": {
+ "globals": {
+ "vars": {
+ "globalThis.process": true,
+ "process.env.NODE_ENV": "production"
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+### v17
+
+Development mode can be enabled either by calling `enableDevMode()` or by using the
+`development` exports condition. If your tooling already enables the `development`
+condition in dev builds, dev mode is automatic. Otherwise, set it explicitly using
+the options below.
+
+#### A Catch-All Option: Explicit Enabling of Development Mode
+
+Development mode may be enabled explicitly by calling `enableDevMode()`:
+
+```js
+// entrypoint.js
+import { enableDevMode } from 'graphql';
+enableDevMode();
+```
+
+A bootstrapping file can be used to enable development mode conditionally:
+
+```js
+// bootstrap.js
+import process from 'node:process';
+import { enableDevMode } from 'graphql';
+if (process.env.NODE_ENV === 'development') {
+ enableDevMode();
+}
+import './path/to/my/entry.js';
+```
+
+The above is compatible with Node.js; the exact environment variable and method
+of accessing it depends on the individual platform.
+
+#### Conditional Exports and Implicit Enabling of Development Mode
+
+Depending on your platform, you may be able to use the `development` condition to
+enable development mode without the need for an explicit import.
+
+Conditional exports with custom conditions are supported by: Node.js, Deno, Bun,
+Webpack 5, Rspack, Rollup (via the `node-resolve` plugin), esbuild, Vite, and
+Rsbuild. create-react-app and Next.js support conditional exports when using
+Webpack 5 as their bundler.
+
+Conditional exports with custom conditions are not supported by Webpack 4, Rollup
+(without the `node-resolve` plugin), older versions of Deno or transpilers such as
+swc. create-react-app and Next.js do not support conditional exports with custom
+conditions when using Webpack 4 as their bundler, nor does Next.js yet support
+conditional exports with custom conditions when using Turbopack (see
+https://github.com/vercel/next.js/discussions/78912).
+
+Testing frameworks such as Mocha, Jest, and Vitest support conditional exports with
+custom conditions, but require configuration as shown below.
+
+We encourage enabling development mode in a development environment. This
+facilitates the additional check to ensure that only a single GraphQL.js module is
+used. Additional development-time checks may also be added in the future.
+
+##### Node.js
+
+In Node.js, the development condition can be enabled by passing the
+`--conditions=development` flag to the Node.js runtime.
+
+Alternatively, this can be included within the `NODE_OPTIONS` environment variable:
+
+```bash
+export NODE_OPTIONS=--conditions=development
+```
+
+##### Deno
+
+In Deno version 2.4.0 and later, you can enable the development condition by
+passing the `--conditions=development` flag to the runtime:
+
+```bash
+deno run --conditions=development main.js
+```
+
+Alternatively, the `DENO_CONDITIONS` environment variable may be used:
+
+```bash
+export DENO_CONDITIONS=development
+```
+
+##### Bun
+
+In Bun version 1.0.30 and later, you can enable the development condition by
+passing the `--conditions=development` flag to the runtime:
+
+```bash
+bun --conditions=development main.js
+```
+
+##### Webpack
+
+Webpack 5 supports the `development` condition natively and requires no additional
+configuration.
+
+##### Rollup
+
+Rollup supports the `development` condition only when using the
+`@rollup/plugin-node-resolve` plugin.
+
+```ts
+// rollup.config.js
+import resolve from '@rollup/plugin-node-resolve';
+
+export default {
+ plugins: [
+ resolve({
+ exportConditions: ['development'],
+ }),
+ ],
+};
+```
+
+##### esbuild
+
+When using esbuild, you can enable the `development` condition by setting the
+`--conditions=development` flag in your build command:
+
+```bash
+esbuild --conditions=development entrypoint.js
+```
+
+Note that setting any custom conditions will drop the default `module` condition
+(used to avoid the dual package hazard), so you may need to use:
+
+```bash
+esbuild --conditions=development,module entrypoint.js
+```
+
+See further discussion within the [esbuild documentation](https://esbuild.github.io/api/#conditions) for
+more details.
+
+##### Vite
+
+Vite supports the `development` condition natively and requires no additional
+configuration.
+
+##### Next.js
+
+When using Webpack 5 as its bundler, Next.js supports the `development` condition
+natively and requires no additional configuration. When using Webpack 4 or
+Turbopack, development mode must be enabled explicitly.
+
+##### create-react-app
+
+When using Webpack 5 as its bundler, create-react-app supports the `development`
+condition natively and requires no additional configuration. When using Webpack 4,
+development mode must be enabled explicitly.
+
+##### Mocha
+
+Mocha supports the `development` condition by passing the appropriate configuration
+to `node`:
+
+```bash
+mocha --node-option conditions=development entrypoint.js
+```
+
+Options can also be passed to `node` via the `NODE_OPTIONS` environment variable:
+
+```bash
+export NODE_OPTIONS=--conditions=development
+```
+
+##### Jest
+
+Jest supports the `development` condition by passing the appropriate configuration:
+
+```ts
+// jest.config.ts
+export const jestConfig = {
+ testEnvironmentOptions: {
+ customExportConditions: ['development'],
+ },
+};
+```
+
+You may need to also include the `node` condition within the provided list:
+
+```ts
+// jest.config.ts
+export const jestConfig = {
+ testEnvironmentOptions: {
+ customExportConditions: ['development', 'node'],
+ },
+};
+```
+
+##### Vitest
+
+Vitest supports the `development` condition by passing the appropriate
+configuration:
+
+```ts
+// vitest.config.ts
+import { defineConfig } from 'vitest/config';
+
+export const vitestConfig = defineConfig({
+ resolve: {
+ conditions: ['development'],
+ },
+ test: {
+ include: ['**/*.test.js'],
+ },
+});
+```
diff --git a/website/pages/docs/getting-started.mdx b/website/pages/docs/getting-started.mdx
index 7a0286c890..0a12557526 100644
--- a/website/pages/docs/getting-started.mdx
+++ b/website/pages/docs/getting-started.mdx
@@ -20,13 +20,24 @@ and arrow functions, so if you aren't familiar with them you might want to read
> Alternatively you can start from [this StackBlitz](https://stackblitz.com/edit/stackblitz-starters-znvgwr) - if you choose
> this route you can skip to [Basic Types](./basic-types.mdx).
-To create a new project and install GraphQL.js in your current directory:
+GraphQL.js v16 is the current stable release. v17 is available as an alpha for
+early testing and feedback. The alpha may change and should not be used in
+production.
+
+To create a new project and install the latest stable release (v16) in your
+current directory:
```sh npm2yarn
npm init
npm install graphql --save
```
+To try the v17 alpha instead:
+
+```sh npm2yarn
+npm install graphql@alpha --save
+```
+
## Writing Code
To handle GraphQL queries, we need a schema that defines the `Query` type, and we need an API root with a function called a "resolver" for each API endpoint. For an API that just returns "Hello world!", we can put this code in a file named `server.js`:
diff --git a/website/pages/docs/going-to-production.mdx b/website/pages/docs/going-to-production.mdx
index da69a36942..2af1e0b7ab 100644
--- a/website/pages/docs/going-to-production.mdx
+++ b/website/pages/docs/going-to-production.mdx
@@ -2,143 +2,27 @@
title: Going to Production
---
-# Going to Production
-
-GraphQL.JS contains a few development checks which in production will cause slower performance and
-an increase in bundle-size. Every bundler goes about these changes different, in here we'll list
-out the most popular ones.
+import { Callout } from 'nextra/components';
-GraphQL.js includes development-time checks that are useful during local testing but should
-be disabled in production to reduce overhead. Additional concerns include caching, error handling,
-schema management, and operational monitoring.
+# Going to Production
This guide covers key practices to prepare a server built with GraphQL.js for production use.
+Concerns include concerns include build optimization, caching, error handling, schema
+management, and operational monitoring.
## Optimize your build for production
-In development, GraphQL.js includes validation checks to catch common mistakes like invalid schemas
-or resolver returns. These checks are not needed in production and can increase runtime overhead.
-
-You can disable them by setting `process.env.NODE_ENV` to `'production'` during your build process.
-GraphQL.js will automatically skip over development-only code paths.
-
-Bundlers are tools that compile and optimize JavaScript for deployment. Most can be configured to
-replace environment variables such as `process.env.NODE_ENV` at build time,
-allowing for unused code (such as development only code paths) to be elided by
-minification tools.
-
-### Bundler configuration examples
-
-The following examples show how to configure common bundlers to set `process.env.NODE_ENV`
-and remove development-only code:
-
-#### Vite
-
-```js
-// vite.config.js
-import { defineConfig } from 'vite';
-
-export default defineConfig({
- define: {
- 'process.env.NODE_ENV': '"production"',
- },
-});
-```
-
-#### Next.js
-
-When you build your application with `next build` and run it using `next start`, Next.js sets
-`process.env.NODE_ENV` to `'production'` automatically. No additional configuration is required.
-
-```bash
-next build
-next start
-```
-
-If you run a custom server, make sure `NODE_ENV` is set manually.
-
-#### Create React App (CRA)
-
-To customize Webpack behavior in CRA, you can use a tool like [`craco`](https://craco.js.org/).
-This example uses CommonJS syntax instead of ESM syntax, which is required by `craco.config.js`:
-
-```js
-// craco.config.js
-const webpack = require('webpack');
-
-module.exports = {
- webpack: {
- plugins: [
- new webpack.DefinePlugin({
- 'globalThis.process': JSON.stringify(true),
- 'process.env.NODE_ENV': JSON.stringify('production'),
- }),
- ],
- },
-};
-```
-
-#### esbuild
-
-```json
-{
- "define": {
- "globalThis.process": true,
- "process.env.NODE_ENV": "production"
- }
-}
-```
-
-#### Webpack
-
-```js
-// webpack.config.js
-import { fileURLToPath } from 'url';
-import { dirname } from 'path';
-
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = dirname(__filename);
-
-export default {
- mode: 'production', // Automatically sets NODE_ENV
- context: __dirname,
-};
-```
-
-#### Rollup
-
-```js
-// rollup.config.js
-import replace from '@rollup/plugin-replace';
-
-export default {
- plugins: [
- replace({
- preventAssignment: true,
- 'process.env.NODE_ENV': JSON.stringify('production'),
- }),
- ],
-};
-```
-
-#### SWC
-
-```json filename=".swcrc"
-{
- "jsc": {
- "transform": {
- "optimizer": {
- "globals": {
- "vars": {
- "globalThis.process": true,
- "process.env.NODE_ENV": "production"
- }
- }
- }
- }
- }
-}
-```
+GraphQL.js includes development-time checks that are useful during local testing. For v16 and
+earlier, these checks are enabled by default and should be disabled in production to reduce
+overhead. See [Development Mode](./development-mode) for further details about these checks
+and how to disable them. Starting in v17, development mode is disabled by default, and must
+be explicitly enabled for development environments.
+
+
+ In v16 and earlier, development mode is enabled by default and must be explicitly disabled
+ in production. Starting in v17, development mode is disabled by default and may require
+ explicit enabling. See [Development Mode](./development-mode) for details and instructions.
+
## Secure your schema
diff --git a/website/pages/upgrade-guides/v16-v17.mdx b/website/pages/upgrade-guides/v16-v17.mdx
index 00b8a27343..f853504436 100644
--- a/website/pages/upgrade-guides/v16-v17.mdx
+++ b/website/pages/upgrade-guides/v16-v17.mdx
@@ -12,6 +12,40 @@ import { Callout } from 'nextra/components'
# Breaking changes
+## Required Node.js versions
+
+The v17 release drops support for end-of-life versions of Node.JS, retaining support for versions 20, 22, and 24 or above.
+
+## ESM and conditional exports
+
+Earlier versions of GraphQL.js shipped dual builds for CommonJS and ESM, with ESM ".mjs" files sitting alongside CommonJS ".js" files.
+The ESM build was accessible via tooling recognizing the `module` field in `package.json`, while the CommonJS build was accessible via
+the `main` field. Unless configured carefully, this could sometimes lead to multiple copies of GraphQL.js being loaded in the same
+process, i.e. the dual-package hazard.
+
+v17 enables access to the ESM build via the `exports` field in `package.json` in a scheme designed to avoid the dual-package hazard as
+best as possible, relying on the ability of bun and newer versions of Node.js to consistently load ESM modules via `require` by
+indicating support for specific conditions. __ESM will now be served by default__, unless the requesting environment tooling __both__
+(A) supports the `node` or `require` conditions __and__ (B) does NOT support `bun`, `module`, `module-sync`. In that scenario, the
+CommonJS build will be served instead.
+
+Note: ESM is not served to deno even though it supports require(esm) because deno not yet support the `module-sync` condition, nor
+does it seem to provide the `deno` condition when calling `require`, see https://github.com/denoland/deno/issues/29970.
+
+Deno users can access a Typescript build for deno via git://github.com/graphql/graphql-js.git#deno as well as by specifically loading
+the index.mjs file, i.e. `import { graphql } from 'graphql/index.mjs'`, although this does not protect against the dual-package hazard.
+
+## Development mode no longer enabled by default and no longer dependent on NODE_ENV value
+
+GraphQl.js development mode in v17 is disabled by default and can be enabled by the `development` condition on supporting platforms or
+by explicitly enabling it within user code by calling `enableDevMode()`. Development mode may trigger permanent de-optimizations and
+therefore cannot be disabled once enabled. The new `isDevModeEnabled()` function can be used to check whether development mode has
+been enabled.
+
+GraphQL.js development mode no longer depends on the `NODE_ENV` environment variable; build tools other than Node.js no longer need
+to replace this Node.js specific code. See [Development Mode](./development-mode) for further details regarding how to enable these
+checks in v17.
+
## Default values
GraphQL schemas allow default values for input fields and arguments. Historically, GraphQL.js did not rigorously validate or coerce these
@@ -178,7 +212,7 @@ Use the `validateInputValue` helper to retrieve the actual errors.
- Added `hideSuggestions` option to `execute`/`validate`/`subscribe`/... to hide schema-suggestions in error messages
- Added `abortSignal` option to `graphql()`, `execute()`, and `subscribe()` allows cancellation of these methods;
- the `abortSignal` can also be passed to field resolvers to cancel asynchronous work that they initiate.
+ `info.abortSignal` can also be used in field resolvers to cancel asynchronous work that they initiate.
- `extensions` support `symbol` keys, in addition to the normal string keys.
- Added ability for resolver functions to return async iterables.
- Added `perEventExecutor` execution option to allows specifying a custom executor for subscription source stream events, which can be useful for preparing a per event execution context argument.