Skip to content

Commit bce9150

Browse files
Gateway support via new @apollo/server-gateway-interface package (#6771)
Until now, the refactored AS4 did not support Apollo Gateway (or any implementation of the AS3 `gateway` option). That's because `GraphQLRequestContext` is part of the API between Apollo Gateway and Apollo Server, and that type has changed in some minor but incompatible ways in AS4. (Additionally, clashes between `declare module` declarations in AS3 and AS4 caused issue, but we removed those declarations from this branch in PRs #6764 and #6759.) This commit restores gateway support. It does this by having AS4 produce an AS3-style request context object. It uses a new `@apollo/server-gateway-interface` package to define the appropriate types for connecting to Gateway. (Note: this package will be code reviewed in this PR in this repo, but once it's approved, it will move to the apollo-utils repo and be released as non-alpha there. Once we've released AS4.0.0 we can move that package back here, but trying to do some prereleases and some non-prereleases on the same branch seems challenging.) This PR removes the top-level `executor` function, which is redundant with the `gateway` option. (Internally, the relevant field is now named `gatewayExecutor`.) Some types had been parametrized by `TContext`, because in AS3, `GraphQLExecutor` (now `GatewayExecutor`) appeared to take a `<TContext>`. However, even though the type itself took a generic argument, its main use in the return from `gateway.load` implicitly hardcoded the default `TContext`. So we are doubling down on that and only allowing `GraphQLExecutor` to use AS3's default `TContext`, the quite flexible `Record<string, any>`. Most of the way toward #6719. Co-authored-by: Trevor Scheer <[email protected]>
1 parent 10c387c commit bce9150

File tree

18 files changed

+313
-101
lines changed

18 files changed

+313
-101
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@apollo/server-integration-testsuite": patch
3+
"@apollo/server": patch
4+
---
5+
6+
Support Gateway. Remove executor constructor option.

docs/source/migration.mdx

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ If supporting older versions of TypeScript is important to you and you'd like to
359359

360360
### `@apollo/gateway`
361361

362-
<!-- TODO(AS4): Fix before release -->
362+
<!-- TODO(AS4): Fix before release. This now works at runtime; TS builds may require a new release of Gateway, and we'll update the docs when that's ready. -->
363363

364364
> ⚠️ Note: The alpha version of Apollo Server 4 does **not** work as an [Apollo Gateway](/federation/gateway). You can still use this alpha to serve [subgraphs](/federation/subgraphs), just not Gateways. We will fix this before the general release of Apollo Server 4.
365365

@@ -741,6 +741,45 @@ new ApolloServer<MyContext>({
741741
});
742742
```
743743

744+
### `executor`
745+
746+
In Apollo Server 3, there are two different ways to specify a replacement for `graphql-js`'s execution functionality. Both of them involve defining a function of the type `GraphQLExecutor`. One way is to specify that function directly as the `executor` constructor option. The other way involves using the `gateway` option.
747+
748+
In Apollo Server 4, this redundancy has been removed: there is no longer an `executor` constructor option. (Additionally, the TypeScript `GraphQLExecutor` type has been renamed `GatewayExecutor` and moved to the `@apollo/server-gateway-interface` package.)
749+
750+
If your Apollo Server 3 code defined an `executor` function and used it like this:
751+
752+
<MultiCodeBlock>
753+
754+
```ts
755+
new ApolloServer({
756+
executor,
757+
// ...
758+
});
759+
```
760+
761+
</MultiCodeBlock>
762+
763+
your Apollo Server code can use `gateway`, like so:
764+
765+
<MultiCodeBlock>
766+
767+
```ts
768+
new ApolloServer({
769+
gateway: {
770+
async load() {
771+
return { executor };
772+
},
773+
onSchemaLoadOrUpdate() {
774+
return () => {};
775+
},
776+
async stop() {},
777+
},
778+
});
779+
```
780+
781+
</MultiCodeBlock>
782+
744783

745784
## Removed features
746785

@@ -1326,14 +1365,18 @@ Apollo Server 4 more consistently handles errors thrown by multiple plugin hooks
13261365

13271366
The `gateway` option to the `ApolloServer` constructor is designed to be used with the `ApolloGateway` class from the `@apollo/gateway` package. Apollo Server 4 changes the details of how Apollo Server interacts with this object. If you use a [supported version of `@apollo/gateway`](#apollo-gateway) as your server's `gateway`, these changes won't affect you. However, if you provide something _other_ than an `ApolloGateway` instance to this option, you might need to adjust your custom code.
13281367

1329-
In Apollo Server 2, the TypeScript type used for the `gateway` constructor option is called `GraphQLService`. In Apollo Server 3, the TypeScript type changed to `GatewayInterface`, but the `apollo-server-core` package continued to export an identical `GraphQLService` type. Apollo Server 4 no longer exports the legacy `GraphQLService` type. Instead, use `GatewayInterface`.
1368+
All the TypeScript types you need to define your `gateway` are now part of the `@apollo/server-gateway-interface` package rather than being exported from an Apollo Server package directly. Additionally, many of these types have been renamed.
13301369

1331-
In Apollo Server 3, your `gateway` may define either `onSchemaChange` or the newer `onSchemaLoadOrUpdate`. In Apollo Server 4, your `gateway` must define `onSchemaLoadOrUpdate`.
1370+
In Apollo Server 2, the TypeScript type used for the `gateway` constructor option is called `GraphQLService`. In Apollo Server 3, the TypeScript type changed to `GatewayInterface`, but the `apollo-server-core` package continued to export an identical `GraphQLService` type. Apollo Server 4 no longer exports the legacy `GraphQLService` type. Instead, use `GatewayInterface`, now exported from `@apollo/server-gateway-interface`.
13321371

1333-
In Apollo Server 3, the `GatewayInterface.load` method returns `Promise<GraphQLServiceConfig>`, which contains a `schema` and an `executor`. Apollo Server 4 renames `GraphQLServiceConfig` to `GatewayLoadResult`, which now only has an `executor` field. You can use the `onSchemaLoadOrUpdate` hook if you want to receive the schema.
1372+
In Apollo Server 3, your `gateway` may define either `onSchemaChange` or the newer `onSchemaLoadOrUpdate`. In Apollo Server 4, your `gateway` must define `onSchemaLoadOrUpdate`.
13341373

1335-
In Apollo Server 3, `GatewayInterface.load` returned an object with an `executor` field with the TypeScript type `GraphQLExecutor`. The `executor` field returned the `GraphQLExecutionResult` type, a type defined by Apollo Server 3. In Apollo Server 4, the `GraphQLExecutor` type now returns the `ExecutionResult` type from `graphql-js`. These two types are essentially the same, except that in `ExecutionResult` the `data` and `extensions` fields are now `Record<string, unknown>`, rather than `Record<string, any>`.
1374+
In Apollo Server 3, the `GatewayInterface.load` method returns `Promise<GraphQLServiceConfig>`, which contains a `schema` and an `executor`. Apollo Server 4 renames `GraphQLServiceConfig` to `GatewayLoadResult` (exported from `@apollo/server-gateway-interface`), which now only has an `executor` field. You can use the `onSchemaLoadOrUpdate` hook if you want to receive the schema.
13361375

1376+
Additionally, the following types have been renamed and are now exported from `@apollo/server-gateway-interface`:
1377+
- `GraphQLExecutor` is now `GatewayExecutor`
1378+
- `SchemaLoadOrUpdateCallback` is now `GatewaySchemaLoadOrUpdateCallback`
1379+
- `Unsubscriber` is now `GatewayUnsubscriber`
13371380

13381381

13391382
## Changes to defaults

package-lock.json

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/integration-testsuite/src/apolloServerTests.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@ import {
3232
import type {
3333
ApolloServerOptions,
3434
ApolloServer,
35-
GatewayInterface,
36-
SchemaLoadOrUpdateCallback,
3735
BaseContext,
38-
GraphQLExecutor,
39-
GraphQLRequestContextExecutionDidStart,
4036
PluginDefinition,
4137
} from '@apollo/server';
4238
import fetch from 'node-fetch';
@@ -71,6 +67,12 @@ import {
7167
it,
7268
} from '@jest/globals';
7369
import type { Mock } from 'jest-mock';
70+
import type {
71+
GatewayExecutor,
72+
GatewayGraphQLRequestContext,
73+
GatewayInterface,
74+
GatewaySchemaLoadOrUpdateCallback,
75+
} from '@apollo/server-gateway-interface';
7476

7577
const quietLogger = loglevel.getLogger('quiet');
7678
function mockLogger() {
@@ -122,18 +124,18 @@ const makeGatewayMock = ({
122124
optionsSpy = (_options) => {},
123125
unsubscribeSpy = () => {},
124126
}: {
125-
executor: GraphQLExecutor<BaseContext> | null;
127+
executor: GatewayExecutor | null;
126128
schema: GraphQLSchema;
127129
optionsSpy?: (_options: any) => void;
128130
unsubscribeSpy?: () => void;
129131
}) => {
130132
const triggers = {
131133
// This gets updated later, when ApolloServer calls gateway.load().
132-
triggerSchemaChange: null as SchemaLoadOrUpdateCallback | null,
134+
triggerSchemaChange: null as GatewaySchemaLoadOrUpdateCallback | null,
133135
};
134136

135-
const listeners: SchemaLoadOrUpdateCallback[] = [];
136-
const mockedGateway: GatewayInterface<BaseContext> = {
137+
const listeners: GatewaySchemaLoadOrUpdateCallback[] = [];
138+
const mockedGateway: GatewayInterface = {
137139
load: async (options) => {
138140
optionsSpy(options);
139141
// Make sure it's async
@@ -459,7 +461,7 @@ export function defineIntegrationTestSuiteApolloServerTests(
459461
});
460462

461463
it("accepts a gateway's schema and calls its executor", async () => {
462-
const executor = jest.fn<GraphQLExecutor<{}>>();
464+
const executor = jest.fn<GatewayExecutor>();
463465
executor.mockReturnValue(
464466
Promise.resolve({ data: { testString: 'hi - but federated!' } }),
465467
);
@@ -482,7 +484,7 @@ export function defineIntegrationTestSuiteApolloServerTests(
482484
const loadError = new Error(
483485
'load error which should be be thrown by start',
484486
);
485-
const gateway: GatewayInterface<BaseContext> = {
487+
const gateway: GatewayInterface = {
486488
async load() {
487489
throw loadError;
488490
},
@@ -2247,7 +2249,7 @@ export function defineIntegrationTestSuiteApolloServerTests(
22472249
}),
22482250
});
22492251

2250-
const executor = (req: GraphQLRequestContextExecutionDidStart<any>) =>
2252+
const executor = (req: GatewayGraphQLRequestContext) =>
22512253
(req.source as string).match(/1/)
22522254
? Promise.resolve({ data: { testString1: 'hello' } })
22532255
: Promise.resolve({ data: { testString2: 'aloha' } });
@@ -2417,9 +2419,7 @@ export function defineIntegrationTestSuiteApolloServerTests(
24172419
};
24182420
});
24192421

2420-
const executor = async (
2421-
req: GraphQLRequestContextExecutionDidStart<any>,
2422-
) => {
2422+
const executor = async (req: GatewayGraphQLRequestContext) => {
24232423
const source = req.source as string;
24242424
const { startPromise, endPromise, i } = executorData[source];
24252425
startPromise.resolve();

packages/integration-testsuite/tsconfig.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@
88
"outDir": "./dist"
99
},
1010
"include": ["src/**/*"],
11-
"references": [{ "path": "../server" }]
11+
"references": [
12+
{ "path": "../server" },
13+
],
1214
}

packages/server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
},
9191
"dependencies": {
9292
"@apollo/cache-control-types": "^1.0.2",
93+
"@apollo/server-gateway-interface": "^1.0.0",
9394
"@apollo/usage-reporting-protobuf": "^4.0.0-alpha.1",
9495
"@apollo/utils.createhash": "^1.1.0",
9596
"@apollo/utils.fetcher": "^1.0.0",

0 commit comments

Comments
 (0)