Apollo Server plugin event reference
New in Apollo Server 3: All plugin lifecycle methods are
async
, except forwillResolveField
andschemaDidLoadOrUpdate
.
This reference describes the lifecycle events that your custom Apollo Server plugin can respond to.
Apollo Server fires two types of events that plugins can hook into: server lifecycle events and request lifecycle events.
Server lifecycle events are high-level events related to the lifecycle of Apollo Server itself (e.g.,
serverWillStart
).Request lifecycle events are associated with the lifecycle of a specific request.
You define responses to these events within the response to a
requestDidStart
event, as described in Responding to request lifecycle events.
With two exceptions, all plugin methods in Apollo Server 3 are
async
. The first exception iswillResolveField
, which is called much more frequently than other plugin methods. The second exception isschemaDidLoadOrUpdate
, where making the methodasync
would introduce unclear ordering semantics around method executions.
Server lifecycle events
serverWillStart
The serverWillStart
event fires when Apollo Server is preparing to start serving GraphQL requests. The server doesn't start until this asynchronous method completes. If it throws (i.e., if the Promise
it returns is rejected), startup fails and your server does not serve GraphQL operations. This helps you make sure all of your server's dependencies are available before attempting to begin serving requests.
This event is fired at different times depending on which Apollo Server middleware you're using:
In
apollo-server
, it's fired from thelisten()
method.In non-serverless middleware libraries like
apollo-server-express
, it's fired from thestart()
method.In serverless middleware libraries like
apollo-server-lambda
, it's fired in response to the first incoming request.
Example
1const server = new ApolloServer({
2 /* ... other necessary configuration ... */
3
4 plugins: [
5 {
6 async serverWillStart() {
7 console.log('Server starting!');
8 }
9 }
10 ]
11})
drainServer
The drainServer
event fires when Apollo Server is starting to shut down because ApolloServer.stop()
has been invoked (either explicitly by your code, or by one of the termination signal handlers). While drainServer
handlers run, GraphQL operations can still execute successfully. This hook is designed to allow you to stop accepting new connections and close existing connections. Apollo Server has a built-in plugin which uses this event to drain a Node http.Server
.
You define your drainServer
handler in the object returned by your serverWillStart
handler, because the two handlers usually interact with the same data. Currently, drainServer
handlers do not take arguments (this might change in the future).
Example
1const server = new ApolloServer({
2 /* ... other necessary configuration ... */
3
4 plugins: [
5 {
6 async serverWillStart() {
7 return {
8 async drainServer() {
9 await myCustomServer.drain();
10 }
11 }
12 }
13 }
14 ]
15})
serverWillStop
The serverWillStop
event fires when Apollo Server is starting to shut down because ApolloServer.stop()
has been invoked (either explicitly by your code, or by one of the termination signal handlers). If your plugin is running any background tasks, this is a good place to shut them down.
You define your serverWillStop
handler in the object returned by your serverWillStart
handler, because the two handlers usually interact with the same data. Currently, serverWillStop
handlers do not take arguments (this might change in the future).
When your serverWillStop
handler is called, Apollo Server is in a state where it will no longer start to execute new GraphQL operations, so it's a good place to flush observability data. If you are looking for a hook that runs while operations can still execute, try drainServer
.
Example
1const server = new ApolloServer({
2 /* ... other necessary configuration ... */
3
4 plugins: [
5 {
6 async serverWillStart() {
7 const interval = setInterval(doSomethingPeriodically, 1000);
8 return {
9 async serverWillStop() {
10 clearInterval(interval);
11 }
12 }
13 }
14 }
15 ]
16})
renderLandingPage
This event enables you to serve a custom landing page from Apollo Server's base URL. The event is fired once by Apollo Server after all serverWillStart
events run. At most one installed plugin can define a renderLandingPage
handler. Otherwise, Apollo Server throws an error on startup.
You define your plugin's renderLandingPage
handler in the object returned by your serverWillStart
handler, which enables it to read values passed to serverWillStart
:
1const server = new ApolloServer({
2 // ... other configuration ...
3
4 plugins: [
5 {
6 async serverWillStart() {
7 return {
8 async renderLandingPage() {
9 const html = `
10<!DOCTYPE html>
11<html>
12 <head>
13 </head>
14 <body>
15 <h1>Hello world!</h1>
16 </body>
17</html>`;
18 return { html };
19 }
20 }
21 }
22 }
23 ]
24});
The handler should return an object with a string html
field. The value of that field is served as HTML for any requests with accept: text/html
headers.
For more landing page options, see Changing the landing page.
Example
1const server = new ApolloServer({
2 /* ... other necessary configuration ... */
3
4 plugins: [
5 {
6 async serverWillStart() {
7 return {
8 async renderLandingPage() {
9 return { html: `<html><body>Welcome to your server!</body></html>` };
10 }
11 }
12 }
13 }
14 ]
15})
requestDidStart
The requestDidStart
event fires whenever Apollo Server begins fulfilling a GraphQL request.
1requestDidStart?(
2 requestContext: WithRequired<
3 GraphQLRequestContext<TContext>,
4 'request' | 'context' | 'logger'
5 >
6): Promise<GraphQLRequestListener<TContext> | void>;
This function can optionally return an object that includes functions for responding
to request lifecycle events that might follow requestDidStart
.
1const server = new ApolloServer({
2 /* ... other necessary configuration ... */
3
4 plugins: [
5 {
6 async requestDidStart(requestContext) {
7 // Within this returned object, define functions that respond
8 // to request-specific lifecycle events.
9 return {
10 // The `parsingDidStart` request lifecycle event fires
11 // when parsing begins. The event is scoped within an
12 // associated `requestDidStart` server lifecycle event.
13 async parsingDidStart(requestContext) {
14 console.log('Parsing started!')
15 },
16 }
17 }
18 }
19 ],
20})
If your plugin doesn't need to respond to any request lifecycle events, requestDidStart
should not return a value.
schemaDidLoadOrUpdate
The schemaDidLoadOrUpdate
event fires whenever Apollo Server initially loads the schema or updates the schema. A schemaDidLoadOrUpdate
handler is given the new API schema and optionally the new core schema (if using a gateway). If you provide a gateway and it is older than @apollo/gateway@0.35.0
, attempting to register a schemaDidLoadOrUpdate
handler will fail.
schemaDidLoadOrUpdate
is a synchronous plugin API (i.e., it does not return a Promise
).
Example
1const server = new ApolloServer({
2 /* ... other necessary configuration ... */
3
4 plugins: [
5 {
6 async serverWillStart() {
7 return {
8 schemaDidLoadOrUpdate({ apiSchema, coreSupergraphSdl }) {
9 console.log(`The API schema is ${printSchema(apiSchema)}`);
10 if (coreSupergraphSdl) {
11 console.log(`The core schema is ${coreSupergraphSdl}`);
12 }
13 },
14 };
15 },
16 },
17 ],
18});
Request lifecycle events
If you're using TypeScript to create your plugin, implement the
GraphQLRequestListener
interface from theapollo-server-plugin-base
module to define functions for request lifecycle events.
When Apollo Server processes a request, these events fire in the order listed (with the exception of didEncounterErrors
, which might fire in one of a few places depending on when errors occur). See the flow diagram
Note that not every event fires for every request (for example, parsingDidStart
doesn't fire for an operation that Apollo Server has cached and doesn't need to parse again).
didResolveSource
The didResolveSource
event is invoked after Apollo Server has determined the
String
-representation of the incoming operation that it will act upon. In the
event that this String
was not directly passed in from the client, this
may be retrieved from a cache store (e.g., Automated Persisted Queries).
At this stage, there is not a guarantee that the operation is not malformed.
1didResolveSource?(
2 requestContext: WithRequired<
3 GraphQLRequestContext<TContext>, 'source' | 'logger'>,
4 >,
5): Promise<void>;
parsingDidStart
The parsingDidStart
event fires whenever Apollo Server will parse a GraphQL
request to create its associated document
AST.
If Apollo Server receives a request with a query string that matches a previous
request, the associated document
might already be available in Apollo Server's cache.
In this case, parsingDidStart
is not called for the request, because parsing
does not occur.
1parsingDidStart?(
2 requestContext: WithRequired<
3 GraphQLRequestContext<TContext>,
4 'metrics' | 'source' | 'logger'
5 >,
6): Promise<void | (err?: Error) => Promise<void>>;
validationDidStart
The validationDidStart
event fires whenever Apollo Server will validate a
request's document
AST against your GraphQL schema.
Like parsingDidStart
, this event does not fire if a request's document
is
already available in Apollo Server's cache (only successfully validated document
s are cached by Apollo Server).
The document
AST is guaranteed to be
available at this stage, because parsing must succeed for validation to occur.
1validationDidStart?(
2 requestContext: WithRequired<
3 GraphQLRequestContext<TContext>,
4 'metrics' | 'source' | 'document' | 'logger'
5 >,
6): Promise<void | (err?: ReadonlyArray<Error>) => Promise<void>>;
didResolveOperation
The didResolveOperation
event fires after the graphql
library successfully
determines the operation to execute from a request's document
AST. At this stage,
both the operationName
string and operation
AST are available.
This event is not associated with your GraphQL server's resolvers. When this event fires, your resolvers have not yet executed (they execute after executionDidStart
).
If the operation is anonymous (i.e., the operation is
query { ... }
instead ofquery NamedQuery { ... }
), thenoperationName
isnull
.
1didResolveOperation?(
2 requestContext: WithRequired<
3 GraphQLRequestContext<TContext>,
4 'metrics' | 'source' | 'document' | 'operationName' | 'operation' | 'logger'
5 >,
6): Promise<void>;
responseForOperation
The responseForOperation
event is fired 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.
1responseForOperation?(
2 requestContext: WithRequired<
3 GraphQLRequestContext<TContext>,
4 'metrics' | 'source' | 'document' | 'operationName' | 'operation' | 'logger'
5 >,
6): Promise<GraphQLResponse | null>;
executionDidStart
The executionDidStart
event fires whenever Apollo Server begins executing the
GraphQL operation specified by a request's document
AST.
1executionDidStart?(
2 requestContext: WithRequired<
3 GraphQLRequestContext<TContext>,
4 'metrics' | 'source' | 'document' | 'operationName' | 'operation' | 'logger'
5 >,
6): Promise<GraphQLRequestExecutionListener | void>;
executionDidStart
may return an object with one or both of the methods executionDidEnd
and willResolveField
. executionDidEnd
is treated like an end hook: it is called after execution with any errors that occurred. willResolveField
is documented in the next section. (In Apollo Server 2, executionDidStart
could return also return an end hook directly.)
willResolveField
The willResolveField
event fires whenever Apollo Server is about to resolve a single field during the execution of an operation. The handler is passed an object with four fields (source
, args
, context
, and info
) that correspond to the four positional arguments passed to resolvers. (Note that source
corresponds to the argument often called parent
in these docs.)
You provide your willResolveField
handler in the object returned by your executionDidStart
handler.
Your willResolveField
handler can optionally return an "end hook" function that's invoked with the resolver's result (or the error that it throws). The end hook is called when your resolver has fully resolved (e.g., if the resolver returns a Promise, the hook is called with the Promise's eventual resolved result).
willResolveField
and its end hook are synchronous plugin APIs (i.e., they do not return Promise
s).
willResolveField
only fires when a field is resolved inside the Apollo Server itself; it does not fire at all if the server is a Gateway.
Example
1const server = new ApolloServer({
2 /* ... other necessary configuration ... */
3
4 plugins: [
5 {
6 async requestDidStart(initialRequestContext) {
7 return {
8 async executionDidStart(executionRequestContext) {
9 return {
10 willResolveField({source, args, context, info}) {
11 const start = process.hrtime.bigint();
12 return (error, result) => {
13 const end = process.hrtime.bigint();
14 console.log(`Field ${info.parentType.name}.${info.fieldName} took ${end - start}ns`);
15 if (error) {
16 console.log(`It failed with ${error}`);
17 } else {
18 console.log(`It returned ${result}`);
19 }
20 };
21 }
22 }
23 }
24 }
25 }
26 }
27 ]
28})
didEncounterErrors
The didEncounterErrors
event fires when Apollo Server encounters errors while
parsing, validating, or executing a GraphQL operation.
1didEncounterErrors?(
2 requestContext: WithRequired<
3 GraphQLRequestContext<TContext>,
4 'metrics' | 'source' | 'errors' | 'logger'
5 >,
6): Promise<void>;
willSendResponse
The willSendResponse
event fires whenever Apollo Server is about to send a response
for a GraphQL operation. This event fires (and Apollo Server sends a response) even
if the GraphQL operation encounters one or more errors.
1willSendResponse?(
2 requestContext: WithRequired<
3 GraphQLRequestContext<TContext>,
4 'metrics' | 'response' | 'logger'
5 >,
6): Promise<void>;