-
Notifications
You must be signed in to change notification settings - Fork 2k
/
plugins.ts
175 lines (153 loc) · 6.65 KB
/
plugins.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import type { KeyValueCache } from '@apollo/utils.keyvaluecache';
import type { Logger } from '@apollo/utils.logger';
import type { GraphQLResolveInfo, GraphQLSchema } from 'graphql';
import type { ApolloConfig } from './constructor.js';
import type { BaseContext } from './context.js';
import type { GraphQLRequestContext, GraphQLResponse } from './graphql.js';
import type {
GraphQLRequestContextDidEncounterErrors,
GraphQLRequestContextDidResolveOperation,
GraphQLRequestContextDidResolveSource,
GraphQLRequestContextExecutionDidStart,
GraphQLRequestContextParsingDidStart,
GraphQLRequestContextResponseForOperation,
GraphQLRequestContextValidationDidStart,
GraphQLRequestContextWillSendResponse,
} from './requestPipeline.js';
export interface GraphQLServerContext {
readonly logger: Logger;
readonly cache: KeyValueCache<string>;
schema: GraphQLSchema;
apollo: ApolloConfig;
// TODO(AS4): Make sure we document that we removed `persistedQueries`.
// TODO(AS4): Note change of serverlessFramework to startedInBackground.
startedInBackground: boolean;
}
export interface GraphQLSchemaContext {
apiSchema: GraphQLSchema;
coreSupergraphSdl?: string;
}
export interface ApolloServerPlugin<
in TContext extends BaseContext = BaseContext,
> {
serverWillStart?(
service: GraphQLServerContext,
): Promise<GraphQLServerListener | void>;
requestDidStart?(
requestContext: GraphQLRequestContext<TContext>,
): Promise<GraphQLRequestListener<TContext> | void>;
// TODO(AS4): Document all these.
unexpectedErrorProcessingRequest?({
requestContext,
error,
}: {
requestContext: GraphQLRequestContext<TContext>;
error: Error;
}): Promise<void>;
contextCreationDidFail?({ error }: { error: Error }): Promise<void>;
invalidRequestWasReceived?({ error }: { error: Error }): Promise<void>;
startupDidFail?({ error }: { error: Error }): Promise<void>;
}
export interface GraphQLServerListener {
schemaDidLoadOrUpdate?(schemaContext: GraphQLSchemaContext): void;
// When your server is stopped (by calling `stop()` or via the
// `SIGINT`/`SIGTERM` handlers), Apollo Server first awaits all `drainServer`
// hooks in parallel. GraphQL operations can still execute while `drainServer`
// is in progress. A typical use is to stop listening for new connections and
// wait until all current connections are idle. The built-in
// ApolloServerPluginDrainHttpServer implements this method.
drainServer?(): Promise<void>;
// When your server is stopped (by calling `stop()` or via the
// `SIGINT`/`SIGTERM` handlers) then (after the `drainServer` phase finishes)
// Apollo Server transitions into a state where no new operations will run and
// then awaits all `drainServer` hooks in parallel. A typical use is to flush
// outstanding observability data.
serverWillStop?(): Promise<void>;
// At most one plugin's serverWillStart may return a GraphQLServerListener
// with this method. If one does, it is called once on server startup and the
// page it returns is served to clients with `accept: text/html` headers. This
// is an intentionally simple API; if you want to do something fancy to serve
// a landing page, you probably should just define a handler in your web
// framework.
renderLandingPage?(): Promise<LandingPage>;
}
// The page served to clients with `accept: text/html` headers.
export interface LandingPage {
html: string;
}
export type GraphQLRequestListenerParsingDidEnd = (
err?: Error,
) => Promise<void>;
export type GraphQLRequestListenerValidationDidEnd = (
err?: ReadonlyArray<Error>,
) => Promise<void>;
export type GraphQLRequestListenerExecutionDidEnd = (
err?: Error,
) => Promise<void>;
export type GraphQLRequestListenerDidResolveField = (
error: Error | null,
result?: any,
) => void;
export interface GraphQLRequestListener<TContext extends BaseContext> {
didResolveSource?(
requestContext: GraphQLRequestContextDidResolveSource<TContext>,
): Promise<void>;
parsingDidStart?(
requestContext: GraphQLRequestContextParsingDidStart<TContext>,
): Promise<GraphQLRequestListenerParsingDidEnd | void>;
validationDidStart?(
requestContext: GraphQLRequestContextValidationDidStart<TContext>,
): Promise<GraphQLRequestListenerValidationDidEnd | void>;
didResolveOperation?(
requestContext: GraphQLRequestContextDidResolveOperation<TContext>,
): Promise<void>;
didEncounterErrors?(
requestContext: GraphQLRequestContextDidEncounterErrors<TContext>,
): Promise<void>;
// If this hook is defined, it is invoked immediately before GraphQL execution
// would take place. If its return value resolves to a non-null
// GraphQLResponse, that result is used instead of executing the query.
// Hooks from different plugins are invoked in series and the first non-null
// response is used.
responseForOperation?(
requestContext: GraphQLRequestContextResponseForOperation<TContext>,
): Promise<GraphQLResponse | null>;
executionDidStart?(
requestContext: GraphQLRequestContextExecutionDidStart<TContext>,
): Promise<GraphQLRequestExecutionListener<TContext> | void>;
willSendResponse?(
requestContext: GraphQLRequestContextWillSendResponse<TContext>,
): Promise<void>;
}
/**
* This is an object form of the parameters received by typical
* `graphql-js` resolvers. The function type is `GraphQLFieldResolver`
* and normally uses positional parameters. In order to facilitate better
* ergonomics in the Apollo Server plugin API, these have been converted to
* named properties on the object using their names from the upstream
* `GraphQLFieldResolver` type signature. Ergonomic wins, in this case,
* include not needing to have three unused variables in scope just because
* there was a need to access the `info` property in a wrapped plugin.
*/
export type GraphQLFieldResolverParams<
TSource,
TContext,
TArgs = { [argName: string]: any },
> = {
source: TSource;
args: TArgs;
contextValue: TContext;
info: GraphQLResolveInfo;
};
export interface GraphQLRequestExecutionListener<TContext extends BaseContext> {
executionDidEnd?: GraphQLRequestListenerExecutionDidEnd;
// willResolveField is not async because we've observed that it already has
// quite a performance impact on execution even without involving the Promise
// queue. If we can come up with ways to alleviate the burden (eg having an
// uninstrumented schema and an instrumented schema and only using the
// instrumented schema for a subset of operations that need detailed
// performance traces) we could be happier supporting async willResolveField.
willResolveField?(
fieldResolverParams: GraphQLFieldResolverParams<any, TContext>,
): GraphQLRequestListenerDidResolveField | void;
}