Skip to content

Support incremental delivery (@defer/@stream) in gateway executor path #8185

@Re-cool

Description

@Re-cool

Use case

Apollo Server added incremental delivery support for @defer/@stream in #8148, but this only works for the direct execution path (executeIncrementally). The gateway executor path (internals.gatewayExecutor) always wraps the result as { singleResult: result }, which means incremental delivery results from a gateway are silently ignored.

When using @apollo/gateway or any custom GatewayExecutor implementation that supports @defer, the incremental results cannot flow through to the client because:

  1. GatewayExecutionResult is typed as plain ExecutionResult — gateways can't return incremental results
  2. requestPipeline.ts doesn't check for initialResult/subsequentResults in the gateway branch

Proposed change

The runtime fix in requestPipeline.ts is small — check if the gateway result contains initialResult and route it through the existing formatErrorsInSubsequentResults handlers, matching the pattern in the direct execution branch:

// packages/server/src/requestPipeline.ts
} else if (internals.gatewayExecutor) {
  const result = await internals.gatewayExecutor(
    makeGatewayGraphQLRequestContext(requestContext, server, internals),
  );
  if ('initialResult' in result) {
    return {
      initialResult: result.initialResult,
      subsequentResults:
        'pending' in result.initialResult
          ? formatErrorsInSubsequentResultsAlpha9(
              result.subsequentResults as
AsyncIterable<GraphQLExperimentalSubsequentIncrementalExecutionResultAlpha9>,
            )
          : formatErrorsInSubsequentResultsAlpha2(
              result.subsequentResults,
            ),
    };
  } else {
    return { singleResult: result };
  }

The part I'm less sure about is how to best define the incremental delivery types in @apollo/server-gateway-interface. GatewayExecutionResult needs to become a union that includes incremental result types (both Alpha2 and Alpha9), but there are different approaches for how detailed those type definitions should be. I have a working prototype that mirrors the incrementalDeliveryPolyfill.ts structure, but I'd welcome input on the preferred approach.

I have a prototype with tests on my fork:
https://github.com/Re-cool/apollo-server/tree/feat/gateway-incremental-delivery

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions