diff --git a/packages/server-gateway-interface/.npmignore b/packages/server-gateway-interface/.npmignore new file mode 100644 index 00000000000..65ccfbfae7f --- /dev/null +++ b/packages/server-gateway-interface/.npmignore @@ -0,0 +1,7 @@ +* +!src/**/* +!dist/**/* +dist/**/*.test.* +!package.json +!README.md +!LICENSE \ No newline at end of file diff --git a/packages/server-gateway-interface/README.md b/packages/server-gateway-interface/README.md new file mode 100644 index 00000000000..a43720b36da --- /dev/null +++ b/packages/server-gateway-interface/README.md @@ -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. diff --git a/packages/server-gateway-interface/package.json b/packages/server-gateway-interface/package.json new file mode 100644 index 00000000000..5a6403a7980 --- /dev/null +++ b/packages/server-gateway-interface/package.json @@ -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 ", + "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": "14.x || 15.x || 16.x" + } +} diff --git a/packages/server-gateway-interface/src/index.ts b/packages/server-gateway-interface/src/index.ts new file mode 100644 index 00000000000..f6b22f7608d --- /dev/null +++ b/packages/server-gateway-interface/src/index.ts @@ -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; + stop(): Promise; +} + +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; + +export type GatewayExecutionResult = ExecutionResult< + Record, + Record +>; + +// Note that the default value for TContext is the same as in AS3, not +// BaseContext. +export interface GatewayGraphQLRequestContext> { + 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; + readonly metrics: GatewayGraphQLRequestMetrics; + debug?: boolean; + readonly overallCachePolicy: GatewayCachePolicy; +} + +export interface GatewayGraphQLRequest { + query?: string; + operationName?: string; + variables?: Record; + extensions?: Record; + http?: GatewayHTTPRequest; +} + +export interface GatewayHTTPRequest { + readonly method: string; + readonly url: string; + readonly headers: FetcherHeaders; +} + +export interface GatewayGraphQLResponse { + data?: Record | null; + errors?: ReadonlyArray; + extensions?: Record; + 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 | 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; +} diff --git a/packages/server-gateway-interface/tsconfig.json b/packages/server-gateway-interface/tsconfig.json new file mode 100644 index 00000000000..d653fae6503 --- /dev/null +++ b/packages/server-gateway-interface/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist" + }, + "include": ["src/**/*"], + "exclude": ["**/__tests__"] +}