Skip to content

Commit

Permalink
New package @apollo/server-gateway-interface
Browse files Browse the repository at this point in the history
This package was reviewed in
apollographql/apollo-server#6771
  • Loading branch information
glasser committed Aug 8, 2022
1 parent 8ac8812 commit 6798bd5
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 0 deletions.
7 changes: 7 additions & 0 deletions packages/server-gateway-interface/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
*
!src/**/*
!dist/**/*
dist/**/*.test.*
!package.json
!README.md
!LICENSE
11 changes: 11 additions & 0 deletions packages/server-gateway-interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Apollo Server/Gateway interface

This package defines TypeScript types for the interface between Apollo Server and Apollo Gateway. It contains no runtime code.

The types in this package describe the API as of Gateway 0.35.0 (ie, with onSchemaLoadOrUpdate). It is extracted from the Apollo Server 3 `apollo-server-types` package. Most of the type names have been changed to start with `Gateway`, so that they coexist better with similarly-named types. (Because TypeScript is generally structurally typed, this is OK.)

Note that the cache scope field (eg, on `requestContext.overallCachePolicy.scope`) is defined as `any` in this package. That's because in AS3 this type is an enum, and enums in TypeScript are *not* structurally typed. So we can't actually create an object of this type without depending on AS3 (or updating AS3 to get its definition from somewhere shared).

We have updated `@apollo/gateway` (v0 and v2) to define its types from this package rather than `apollo-server-types`. This allows Gateway to be compatible with both AS3 and AS4 (and even AS2, for Gateway 0.x) without needing to pull either package's code into the TypeScript build.

Apollo Server 4 directly depends on this package and uses its types to construct its calls to the Gateway.
31 changes: 31 additions & 0 deletions packages/server-gateway-interface/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@apollo/server-gateway-interface",
"version": "0.0.0",
"description": "Interface used to connect Apollo Gateway to Apollo Server",
"type": "module",
"types": "dist/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/apollographql/apollo-utils",
"directory": "packages/server-gateway-interface"
},
"keywords": [],
"author": "Apollo <packages@apollographql.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/apollographql/apollo-utils/issues"
},
"homepage": "https://github.com/apollographql/apollo-utils#readme",
"engines": {
"node": ">=14.0"
},
"dependencies": {
"@apollo/utils.fetcher": "^1.0.0",
"@apollo/utils.logger": "^1.0.0",
"@apollo/utils.keyvaluecache": "^1.0.1",
"@apollo/usage-reporting-protobuf": "^4.0.0-alpha.1"
},
"peerDependencies": {
"graphql": "^16.5.0"
}
}
128 changes: 128 additions & 0 deletions packages/server-gateway-interface/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// NOTE: Once Apollo Server 4 is released, move this package into the
// apollo-server repo. We're placing it in the apollo-utils repo for now to
// enable us to make non-alpha releases that can be used on the apollo-server
// version-4 branch.

import type { KeyValueCache } from "@apollo/utils.keyvaluecache";
import type {
DocumentNode,
ExecutionResult,
GraphQLError,
GraphQLFormattedError,
GraphQLSchema,
OperationDefinitionNode,
} from "graphql";
import type { Logger } from "@apollo/utils.logger";
import type { Trace } from "@apollo/usage-reporting-protobuf";
import type { FetcherHeaders } from "@apollo/utils.fetcher";

export interface GatewayInterface {
onSchemaLoadOrUpdate(
callback: GatewaySchemaLoadOrUpdateCallback,
): GatewayUnsubscriber;
load(options: { apollo: GatewayApolloConfig }): Promise<GatewayLoadResult>;
stop(): Promise<void>;
}

export type GatewaySchemaLoadOrUpdateCallback = (schemaContext: {
apiSchema: GraphQLSchema;
coreSupergraphSdl: string;
}) => void;

export type GatewayUnsubscriber = () => void;

export interface GatewayApolloConfig {
key?: string;
keyHash?: string;
graphRef?: string;
}

export interface GatewayLoadResult {
executor: GatewayExecutor | null;
}

export type GatewayExecutor = (
requestContext: GatewayGraphQLRequestContext,
) => Promise<GatewayExecutionResult>;

export type GatewayExecutionResult = ExecutionResult<
Record<string, any>,
Record<string, any>
>;

// Note that the default value for TContext is the same as in AS3, not
// BaseContext.
export interface GatewayGraphQLRequestContext<TContext = Record<string, any>> {
readonly request: GatewayGraphQLRequest;
readonly response?: GatewayGraphQLResponse;
logger: Logger;
readonly schema: GraphQLSchema;
readonly schemaHash: GatewaySchemaHash;
readonly context: TContext;
readonly cache: KeyValueCache;
readonly queryHash: string;
readonly document: DocumentNode;
readonly source: string;
readonly operationName: string | null;
readonly operation: OperationDefinitionNode;
readonly errors?: ReadonlyArray<GraphQLError>;
readonly metrics: GatewayGraphQLRequestMetrics;
debug?: boolean;
readonly overallCachePolicy: GatewayCachePolicy;
}

export interface GatewayGraphQLRequest {
query?: string;
operationName?: string;
variables?: Record<string, any>;
extensions?: Record<string, any>;
http?: GatewayHTTPRequest;
}

export interface GatewayHTTPRequest {
readonly method: string;
readonly url: string;
readonly headers: FetcherHeaders;
}

export interface GatewayGraphQLResponse {
data?: Record<string, any> | null;
errors?: ReadonlyArray<GraphQLFormattedError>;
extensions?: Record<string, any>;
http?: GatewayHTTPResponse;
}

export interface GatewayHTTPResponse {
readonly headers: FetcherHeaders;
status?: number;
}

export type GatewaySchemaHash = string & { __fauxpaque: "SchemaHash" };

export interface GatewayGraphQLRequestMetrics {
captureTraces?: boolean;
persistedQueryHit?: boolean;
persistedQueryRegister?: boolean;
responseCacheHit?: boolean;
forbiddenOperation?: boolean;
registeredOperation?: boolean;
startHrTime?: [number, number];
queryPlanTrace?: Trace.QueryPlanNode;
}

export interface GatewayCachePolicy extends GatewayCacheHint {
replace(hint: GatewayCacheHint): void;
restrict(hint: GatewayCacheHint): void;
policyIfCacheable(): Required<GatewayCacheHint> | null;
}

export interface GatewayCacheHint {
maxAge?: number;
// In AS3, this field is an enum. In TypeScript, enums are not structurally
// typed: you need to actually refer directly to the enum's definition to
// produce a value of its type in a typesafe way. Doing that would prevent us
// from severing the dependency on apollo-server-types (AS3), so instead we
// just use 'any'. The legal runtime values of this fields are the strings
// `PUBLIC` and `PRIVATE`.
scope?: any;
}
9 changes: 9 additions & 0 deletions packages/server-gateway-interface/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["**/__tests__"],
}
1 change: 1 addition & 0 deletions tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
{ "path": "packages/operationRegistrySignature" },
{ "path": "packages/printWithReducedWhitespace" },
{ "path": "packages/removeAliases" },
{ "path": "packages/server-gateway-interface" },
{ "path": "packages/sortAST" },
{ "path": "packages/stripSensitiveLiterals" },
{ "path": "packages/usageReporting" },
Expand Down

0 comments on commit 6798bd5

Please sign in to comment.