Repository: apollographql/apollo-server
Pull request #7786 (2023-11-13 16:20:51)

Add dangerouslyDisableValidation option to @apollo/server
This adds a `disableValidation` option to @apollo/server which will skip the validation step for graphql operations.

codesandbox-ci[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/apollographql/apollo-server/pr/7786/builds/438059) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 1ffcd3b1be2e9543ae39367cc1cf9c7efc1a8405:

|Sandbox| Source |
|--|--|
|[Apollo Server Typescript](https://codesandbox.io/s/apollo-server-typescript-mlqyl4)| Configuration |
|[Apollo Server](https://codesandbox.io/s/apollo-server-6xhtwn)| Configuration |


Patch:
diff --git a/.changeset/blue-laws-grab.md b/.changeset/blue-laws-grab.md
new file mode 100644
index 000000000..6e261ee15
--- /dev/null
+++ b/.changeset/blue-laws-grab.md
@@ -0,0 +1,6 @@
+---
+'@apollo/server-integration-testsuite': minor
+'@apollo/server': minor
+---
+
+Restore missing v1 `skipValidation` option as `dangerouslyDisableValidation`. Note that enabling this option exposes your server to potential security and unexpected runtime issues. Apollo will not support issues that arise as a result of using this option.
diff --git a/docs/source/api/apollo-server.mdx b/docs/source/api/apollo-server.mdx
index eed454115..e1522cacd 100644
--- a/docs/source/api/apollo-server.mdx
+++ b/docs/source/api/apollo-server.mdx
@@ -492,6 +492,21 @@ Apollo Server v5 will _always_ behave as if this option is `true` (and will igno
 </td>
 </tr>
 
+<tr>
+<td>
+
+##### `dangerouslyDisableValidation`
+
+`Boolean`
+</td>
+<td>
+
+⚠️ Caution: this option can lead to security vulnerabilities and unexpected behavior. Use of this option in production is not supported by Apollo.
+
+When set to `true`, disable validation of graphql operations entirely.
+</td>
+</tr>
+
 </tbody>
 </table>
 
diff --git a/packages/server/src/ApolloServer.ts b/packages/server/src/ApolloServer.ts
index 3ecf63b46..9455652e2 100644
--- a/packages/server/src/ApolloServer.ts
+++ b/packages/server/src/ApolloServer.ts
@@ -155,7 +155,7 @@ type ServerState =
 export interface ApolloServerInternals<TContext extends BaseContext> {
   state: ServerState;
   gatewayExecutor: GatewayExecutor | null;
-
+  dangerouslyDisableValidation?: boolean;
   formatError?: (
     formattedError: GraphQLFormattedError,
     error: unknown,
@@ -296,6 +296,8 @@ export class ApolloServer<in out TContext extends BaseContext = BaseContext> {
         ...(config.validationRules ?? []),
         ...(introspectionEnabled ? [] : [NoIntrospection]),
       ],
+      dangerouslyDisableValidation:
+        config.dangerouslyDisableValidation ?? false,
       fieldResolver: config.fieldResolver,
       includeStacktraceInErrorResponses:
         config.includeStacktraceInErrorResponses ??
diff --git a/packages/server/src/externalTypes/constructor.ts b/packages/server/src/externalTypes/constructor.ts
index ffd1cf6f7..04cf0653d 100644
--- a/packages/server/src/externalTypes/constructor.ts
+++ b/packages/server/src/externalTypes/constructor.ts
@@ -97,6 +97,7 @@ interface ApolloServerOptionsBase<TContext extends BaseContext> {
   apollo?: ApolloConfigInput;
   nodeEnv?: string;
   documentStore?: DocumentStore | null;
+  dangerouslyDisableValidation?: boolean;
   csrfPrevention?: CSRFPreventionOptions | boolean;
 
   // Used for parsing operations; unlike in AS3, this is not also used for
diff --git a/packages/server/src/requestPipeline.ts b/packages/server/src/requestPipeline.ts
index 339879740..b798cc181 100644
--- a/packages/server/src/requestPipeline.ts
+++ b/packages/server/src/requestPipeline.ts
@@ -235,27 +235,29 @@ export async function processGraphQLRequest<TContext extends BaseContext>(
     }
     await parsingDidEnd();
 
-    const validationDidEnd = await invokeDidStartHook(
-      requestListeners,
-      async (l) =>
-        l.validationDidStart?.(
-          requestContext as GraphQLRequestContextValidationDidStart<TContext>,
-        ),
-    );
-
-    const validationErrors = validate(
-      schemaDerivedData.schema,
-      requestContext.document,
-      [...specifiedRules, ...internals.validationRules],
-    );
+    if (internals.dangerouslyDisableValidation !== true) {
+      const validationDidEnd = await invokeDidStartHook(
+        requestListeners,
+        async (l) =>
+          l.validationDidStart?.(
+            requestContext as GraphQLRequestContextValidationDidStart<TContext>,
+          ),
+      );
 
-    if (validationErrors.length === 0) {
-      await validationDidEnd();
-    } else {
-      await validationDidEnd(validationErrors);
-      return await sendErrorResponse(
-        validationErrors.map((error) => new ValidationError(error)),
+      const validationErrors = validate(
+        schemaDerivedData.schema,
+        requestContext.document,
+        [...specifiedRules, ...internals.validationRules],
       );
+
+      if (validationErrors.length === 0) {
+        await validationDidEnd();
+      } else {
+        await validationDidEnd(validationErrors);
+        return await sendErrorResponse(
+          validationErrors.map((error) => new ValidationError(error)),
+        );
+      }
     }
 
     if (schemaDerivedData.documentStore) {


Test patch:
diff --git a/packages/integration-testsuite/src/apolloServerTests.ts b/packages/integration-testsuite/src/apolloServerTests.ts
index fabfb4464..102db81bb 100644
--- a/packages/integration-testsuite/src/apolloServerTests.ts
+++ b/packages/integration-testsuite/src/apolloServerTests.ts
@@ -312,6 +312,21 @@ export function defineIntegrationTestSuiteApolloServerTests(
           );
         });
 
+        it('allows disabling validation rules', async () => {
+          const uri = await createServerAndGetUrl({
+            schema,
+            stopOnTerminationSignals: false,
+            nodeEnv: 'production',
+            dangerouslyDisableValidation: true,
+          });
+
+          const apolloFetch = createApolloFetch({ uri });
+
+          const result = await apolloFetch({ query: INTROSPECTION_QUERY });
+          expect(result.data).toBeDefined();
+          expect(result.errors).toBeUndefined();
+        });
+
         it('allows introspection to be enabled explicitly', async () => {
           const uri = await createServerAndGetUrl({
             schema,