Docs
Launch GraphOS Studio
Apollo Server 3 is officially deprecated, with end-of-life scheduled for 22 October 2024. Learn more about upgrading to a supported Apollo Server version.

Apollo Server plugin event reference


New in Apollo Server 3: All plugin lifecycle methods are async, except for willResolveField and schemaDidLoadOrUpdate.

This reference describes the lifecycle events that your custom Apollo Server plugin can respond to.

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 itself (e.g., serverWillStart).
  • Request lifecycle events are associated with the lifecycle of a specific request.

With two exceptions, all plugin methods in 3 are async. The first exception is willResolveField, which is called much more frequently than other plugin methods. The second exception is schemaDidLoadOrUpdate, where making the method async would introduce unclear ordering semantics around method executions.

Server lifecycle events

serverWillStart

The serverWillStart event fires when is preparing to start serving 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 . 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 the listen() method.
  • In non- middleware libraries like apollo-server-express, it's fired from the start() method.
  • In middleware libraries like apollo-server-lambda, it's fired in response to the first incoming request.

Example

const server = new ApolloServer({
/* ... other necessary configuration ... */
plugins: [
{
async serverWillStart() {
console.log('Server starting!');
}
}
]
})

drainServer

The drainServer event fires when 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, can still execute successfully. This hook is designed to allow you to stop accepting new connections and close existing connections. 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 (this might change in the future).

Example

const server = new ApolloServer({
/* ... other necessary configuration ... */
plugins: [
{
async serverWillStart() {
return {
async drainServer() {
await myCustomServer.drain();
}
}
}
}
]
})

serverWillStop

The serverWillStop event fires when 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 (this might change in the future).

When your serverWillStop handler is called, is in a state where it will no longer start to execute new , 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

const server = new ApolloServer({
/* ... other necessary configuration ... */
plugins: [
{
async serverWillStart() {
const interval = setInterval(doSomethingPeriodically, 1000);
return {
async serverWillStop() {
clearInterval(interval);
}
}
}
}
]
})

renderLandingPage

This event enables you to serve a custom landing page from '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, 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:

index.js
const server = new ApolloServer({
// ... other configuration ...
plugins: [
{
async serverWillStart() {
return {
async renderLandingPage() {
const html = `
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>`;
return { html };
}
}
}
}
]
});

The handler should return an object with a string html . 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

const server = new ApolloServer({
/* ... other necessary configuration ... */
plugins: [
{
async serverWillStart() {
return {
async renderLandingPage() {
return { html: `<html><body>Welcome to your server!</body></html>` };
}
}
}
}
]
})

requestDidStart

The requestDidStart event fires whenever begins fulfilling a request.

requestDidStart?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'request' | 'context' | 'logger'
>
): Promise<GraphQLRequestListener<TContext> | void>;

This function can optionally return an object that includes functions for responding to request lifecycle events that might follow requestDidStart.

const server = new ApolloServer({
/* ... other necessary configuration ... */
plugins: [
{
async requestDidStart(requestContext) {
// Within this returned object, define functions that respond
// to request-specific lifecycle events.
return {
// The `parsingDidStart` request lifecycle event fires
// when parsing begins. The event is scoped within an
// associated `requestDidStart` server lifecycle event.
async parsingDidStart(requestContext) {
console.log('Parsing started!')
},
}
}
}
],
})

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 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

const server = new ApolloServer({
/* ... other necessary configuration ... */
plugins: [
{
async serverWillStart() {
return {
schemaDidLoadOrUpdate({ apiSchema, coreSupergraphSdl }) {
console.log(`The API schema is ${printSchema(apiSchema)}`);
if (coreSupergraphSdl) {
console.log(`The core schema is ${coreSupergraphSdl}`);
}
},
};
},
},
],
});

Request lifecycle events

If you're using TypeScript to create your plugin, implement the GraphQLRequestListener interface from the apollo-server-plugin-base module to define functions for request lifecycle events.

When 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 that has cached and doesn't need to parse again).

didResolveSource

The didResolveSource event is invoked after has determined the String-representation of the incoming 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 ).

At this stage, there is not a guarantee that the is not malformed.

didResolveSource?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>, 'source' | 'logger'>,
>,
): Promise<void>;

parsingDidStart

The parsingDidStart event fires whenever will parse a request to create its associated document AST.

If receives a request with a string that matches a previous request, the associated document might already be available in 's cache. In this case, parsingDidStart is not called for the request, because parsing does not occur.

parsingDidStart?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'metrics' | 'source' | 'logger'
>,
): Promise<void | (err?: Error) => Promise<void>>;

validationDidStart

The validationDidStart event fires whenever will validate a request's document AST against your .

Like parsingDidStart, this event does not fire if a request's document is already available in 's cache (only successfully validated documents are cached by ).

The document AST is guaranteed to be available at this stage, because parsing must succeed for validation to occur.

validationDidStart?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'metrics' | 'source' | 'document' | 'logger'
>,
): Promise<void | (err?: ReadonlyArray<Error>) => Promise<void>>;

didResolveOperation

The didResolveOperation event fires after the graphql library successfully determines the 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 's resolvers. When this event fires, your have not yet executed (they execute after executionDidStart).

If the is anonymous (i.e., the operation is query { ... } instead of query NamedQuery { ... }), then operationName is null.

didResolveOperation?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'metrics' | 'source' | 'document' | 'operationName' | 'operation' | 'logger'
>,
): Promise<void>;

responseForOperation

The responseForOperation event is fired immediately before execution would take place. If its return value resolves to a non-null GraphQLResponse, that result is used instead of executing the . Hooks from different plugins are invoked in series, and the first non-null response is used.

responseForOperation?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'metrics' | 'source' | 'document' | 'operationName' | 'operation' | 'logger'
>,
): Promise<GraphQLResponse | null>;

executionDidStart

The executionDidStart event fires whenever begins executing the specified by a request's document AST.

executionDidStart?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'metrics' | 'source' | 'document' | 'operationName' | 'operation' | 'logger'
>,
): 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 in the next section. (In 2, executionDidStart could return also return an end hook directly.)

willResolveField

The willResolveField event fires whenever is about to resolve a single during the execution of an . 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 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 's result (or the error that it throws). The end hook is called when your resolver has fully resolved (e.g., if the 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 Promises).

willResolveField only fires when a is resolved inside the itself; it does not fire at all if the server is a Gateway.

Example

const server = new ApolloServer({
/* ... other necessary configuration ... */
plugins: [
{
async requestDidStart(initialRequestContext) {
return {
async executionDidStart(executionRequestContext) {
return {
willResolveField({source, args, context, info}) {
const start = process.hrtime.bigint();
return (error, result) => {
const end = process.hrtime.bigint();
console.log(`Field ${info.parentType.name}.${info.fieldName} took ${end - start}ns`);
if (error) {
console.log(`It failed with ${error}`);
} else {
console.log(`It returned ${result}`);
}
};
}
}
}
}
}
}
]
})

didEncounterErrors

The didEncounterErrors event fires when encounters errors while parsing, validating, or executing a .

didEncounterErrors?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'metrics' | 'source' | 'errors' | 'logger'
>,
): Promise<void>;

willSendResponse

The willSendResponse event fires whenever is about to send a response for a . This event fires (and Apollo Server sends a response) even if the GraphQL operation encounters one or more errors.

willSendResponse?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'metrics' | 'response' | 'logger'
>,
): Promise<void>;
Previous
Creating plugins
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company