Plugins
Extend Apollo Server with custom functionality
Plugins are available in Apollo Server 2.2.x and later.
Plugins enable you to extend Apollo Server's core functionality by performing custom operations in response to certain events. Currently, these events correspond to individual phases of the GraphQL request lifecycle, and to the startup of Apollo Server itself.
For example, a basic logging plugin might log the GraphQL query string associated with each request that's sent to Apollo Server.
Creating a plugin
Plugins are JavaScript objects that implement one or more functions that respond to
events. Here's a basic plugin that responds to the serverWillStart
event:
1const myPlugin = {
2 serverWillStart() {
3 console.log('Server starting up!');
4 },
5};
If you're using TypeScript to create a plugin, the
apollo-server-plugin-base
module exports theApolloServerPlugin
interface for plugins to implement.
You can define a plugin in the same file where you initialize Apollo Server, or you can export it as a separate module:
1module.exports = {
2 serverWillStart() {
3 console.log('Server starting up!');
4 },
5};
To create a plugin that accepts options, create a function that accepts an
options
object and returns a properly structured plugin object, like so:
1module.exports = (options) => {
2 return {
3 serverWillStart() {
4 console.log(options.logMessage);
5 },
6 };
7};
Responding to events
A plugin specifies exactly which events
it responds to by implementing functions that correspond to those events.
The plugin in the examples above responds to the serverWillStart
event, which
fires when Apollo Server is preparing to start up.
A plugin can respond to any combination of supported events.
Responding to request lifecycle events
Plugins can respond to the following events associated with the GraphQL request lifecycle:
However, the way you define these functions is slightly different from the
serverWillStart
example above. First, your plugin must define the requestDidStart
function:
1const myPlugin = {
2 requestDidStart() {
3 console.log('Request started!');
4 },
5};
The requestDidStart
event fires whenever Apollo Server receives a GraphQL request,
before any of the lifecycle events listed above. You can respond to this event
just like you respond to serverWillStart
, but you also use this function
to define responses for a request's lifecycle events, like so:
1const myPlugin = {
2 requestDidStart(requestContext) {
3 console.log('Request started!');
4
5 return {
6
7 parsingDidStart(requestContext) {
8 console.log('Parsing started!');
9 },
10
11 validationDidStart(requestContext) {
12 console.log('Validation started!');
13 }
14
15 }
16 },
17};
As shown, the requestDidStart
function can optionally return an object that
defines functions that respond to request lifecycle events. This structure
organizes and encapsulates all of your plugin's request lifecycle logic, making it
easier to reason about.
Request lifecycle event flow
The following diagram illustrates the sequence of events that fire for each request. Each of these events is documented below.
Any event below that can result in "Success" can also result in an error. Whenever an error occurs, the
didEncounterErrors
event fires and the remainder of the "Success" path does not.
End hooks
Event handlers for the following events can optionally return a function that is invoked after the corresponding lifecycle phase ends:
executionDidStart
(this handler can alternatively return an object containing anexecutionDidEnd
function)
These end hooks are passed any errors that occurred during the execution of that lifecycle phase. For example, the following plugin logs any errors that occur during any of the above lifecycle events:
1const myPlugin = {
2 requestDidStart() {
3 return {
4 parsingDidStart() {
5 return (err) => {
6 if (err) {
7 console.error(err);
8 }
9 }
10 },
11 validationDidStart() {
12 // This end hook is unique in that it can receive an array of errors,
13 // which will contain every validation error that occurred.
14 return (errs) => {
15 if (errs) {
16 errs.forEach(err => console.error(err));
17 }
18 }
19 },
20 executionDidStart() {
21 return (err) => {
22 if (err) {
23 console.error(err);
24 }
25 }
26 }
27 }
28 }
29}
Note that the validationDidStart
end hook receives an array of errors that
contains every validation error that occurred (if any). The willResolveField
end hook receives the error thrown by the resolver as the first argument and the result of the resolver as the second argument. The arguments to each
end hook are documented in the type definitions in Request lifecycle events.
Inspecting request and response details
As the example above shows, requestDidStart
and request lifecycle functions accept a requestContext
parameter. This parameter is of type GraphQLRequestContext
, which includes a
request
(of type GraphQLRequest
), along with a response
field (of type GraphQLResponse
) if it's available.
These types and their related subtypes are all defined in apollo-server-types/src/index.ts
.
Installing a plugin
Add your plugin to Apollo Server by providing a plugins
configuration
option to the ApolloServer
constructor, like so:
1const { ApolloServer } = require('apollo-server');
2const ApolloServerOperationRegistry =
3 require('apollo-server-plugin-operation-registry');
4
5/* This example doesn't provide `typeDefs` or `resolvers`,
6 both of which are required to start the server. */
7const { typeDefs, resolvers } = require('./separatelyDefined');
8
9const server = new ApolloServer({
10 typeDefs,
11 resolvers,
12
13 // You can import plugins or define them in-line, as shown:
14 plugins: [
15
16 /* This plugin is from a package that's imported above. */
17 ApolloServerOperationRegistry({ /* options */ }),
18
19 /* This plugin is imported in-place. */
20 require('./localPluginModule'),
21
22 /* This plugin is defined in-line. */
23 {
24 serverWillStart() {
25 console.log('Server starting up!');
26 },
27 }
28 ],
29})
Apollo Server event reference
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.
Currently, two server lifecycle events are supported: serverWillStart
and requestDidStart
.
Request lifecycle events are associated with a specific request. You define responses to these events within the response to a requestDidStart
event, as described in Responding to request lifecycle events.
Server lifecycle events
serverWillStart
The serverWillStart
event fires when Apollo Server is preparing to start serving GraphQL requests. If you respond to this event with an async
function (or if the function returns a Promise
), the server doesn't start until the asynchronous operation completes. If the Promise
is rejected, startup fails (unless you're using Express middleware). This helps you make sure all
of your server's dependencies are available before attempting to begin serving requests.
Example
1const server = new ApolloServer({
2 /* ... other necessary configuration ... */
3
4 plugins: [
5 {
6 serverWillStart() {
7 console.log('Server starting!');
8 }
9 }
10 ]
11})
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).
Example
1const server = new ApolloServer({
2 /* ... other necessary configuration ... */
3
4 plugins: [
5 {
6 serverWillStart() {
7 const interval = setInterval(doSomethingPeriodically, 1000);
8 return {
9 serverWillStop() {
10 clearInterval(interval);
11 }
12 }
13 }
14 }
15 ]
16})
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): 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 requestDidStart(requestContext) {
7
8 /* Within this returned object, define functions that respond
9 to request-specific lifecycle events. */
10 return {
11
12 /* The `parsingDidStart` request lifecycle event fires
13 when parsing begins. The event is scoped within an
14 associated `requestDidStart` server lifecycle event. */
15 parsingDidStart(requestContext) {
16 console.log('Parsing started!')
17 },
18 }
19 }
20 }
21 ],
22})
If your plugin doesn't need to respond to any request lifecycle events, requestDidStart
should not return a value.
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.
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): ValueOrPromise<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): (err?: Error) => void | 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): (err?: ReadonlyArray<Error>) => void | 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): ValueOrPromise<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): ValueOrPromise<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): (err?: Error) => void | void;
executionDidStart
may return an "end hook" function. Alternatively, it may return an object with one or both of the methods executionDidEnd
and willResolveField
. executionDidEnd
is treated identically to an end hook: it is called after execution with any errors that occurred. willResolveField
is documented in the next section.
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).
Example
1const server = new ApolloServer({
2 /* ... other necessary configuration ... */
3
4 plugins: [
5 {
6 requestDidStart(initialRequestContext) {
7 return {
8 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): ValueOrPromise<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): ValueOrPromise<void>;