Skip to content

Commit

Permalink
docs: document ApolloServer.stop, serverWillStop, willResolveField (#…
Browse files Browse the repository at this point in the history
…4913)

v2.20 has two changes related to `stop`, so this is a good time to actually
document it!

Fixes #4104.

Includes #4914.

Co-authored-by: Stephen Barlow <[email protected]>
  • Loading branch information
glasser and Stephen Barlow authored Feb 9, 2021
1 parent 17caf69 commit dbf22c2
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 12 deletions.
44 changes: 44 additions & 0 deletions docs/source/api/apollo-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,25 @@ You can also manually call `stop()` in other contexts. Note that `stop()` is asy
</td>
</tr>

<tr>
<td>

###### `stopGracePeriodMillis`

`number`
</td>
<td>

The amount of time to wait after [`ApolloServer.stop()`](#stop) is called (including via a [termination signal](#stoponterminationsignals)) before forcefully closing all active connections. If you pass `Infinity`, Apollo Server waits indefinitely for all active connections to go idle.

**This option is used only by the `apollo-server` package.** If you're integrating with [Node.js middleware](../integrations/middleware/) via a different package, it's your responsibility to stop your HTTP server in whatever way is appropriate.

The default value is `10_000` (10 seconds).

</td>
</tr>


<tr>
<td colspan="2">

Expand Down Expand Up @@ -759,6 +778,27 @@ Unlike [`applyMiddleware`](#applymiddleware), `getMiddleware` does _not_ automat

The `getMiddleware` method takes the same options as [`applyMiddleware`](#applymiddleware), **except** the `app` option.

#### `stop`

`ApolloServer.stop()` is an async method that tells all of Apollo Server's background tasks to complete. It calls and awaits all [`serverWillStop` plugin handlers](../integrations/plugins/#serverwillstop) (including the [usage reporting plugin](./plugin/usage-reporting/)'s handler, which sends a final usage report to Apollo Studio). This method takes no arguments.

If your server is a [federated gateway](https://www.apollographql.com/docs/federation/gateway/), `stop` also stops gateway-specific background activities, such as polling for updated service configuration.

In some circumstances, Apollo Server calls `stop` automatically when the process receives a `SIGINT` or `SIGTERM` signal. See the [`stopOnTerminationSignals` constructor option](#stoponterminationsignals) for details.

If you're using the `apollo-server` package (which handles setting up an HTTP server for you), this method first stops the HTTP server. Specifically, it:

* Stops listening for new connections
* Closes idle connections (i.e., connections with no current HTTP request)
* Closes active connections whenever they become idle
* Waits for all connections to be closed

If any connections remain active after a grace period (10 seconds by default), Apollo Server forcefully closes those connections. You can configure this grace period with the [`stopGracePeriodMillis` constructor option](#stopgraceperiodmillis).

If you're using a [middleware package](../integrations/middleware/) instead of `apollo-server`, you should stop your HTTP server before calling `ApolloServer.stop()`.

---

## `gql`

A [template literal tag](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates) for wrapping GraphQL strings, such as schema definitions:
Expand All @@ -775,6 +815,7 @@ const typeDefs = gql`

This converts GraphQL strings into the format that Apollo libraries expect when working with operations and schemas. It also helps tools identify when a string contains GraphQL language (such as to enable syntax highlighting).

---

## `makeExecutableSchema`

Expand All @@ -784,6 +825,8 @@ The [`ApolloServer` constructor](#constructor) automatically calls this method u

This method is defined in the `graphql-tools` library and is re-exported from `apollo-server` as a convenience. [See its documentation here.](./graphql-tools/#makeexecutableschemaoptions)

---

## `addMockFunctionsToSchema`

The `addMockFunctionsToSchema` method is re-exported from `apollo-server` as a convenience.
Expand Down Expand Up @@ -837,6 +880,7 @@ addMockFunctionsToSchema({
preserveResolvers: false,
});
```
---

## `graphql-tools` exports

Expand Down
4 changes: 3 additions & 1 deletion docs/source/data/resolvers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,9 @@ When a chain "diverges" like this, each subchain executes in parallel.

## Resolver arguments

Resolver functions take the following positional arguments, in order:
Resolver functions are passed four arguments: `parent`, `args`, `context`, and `info` (in that order).

> You can use any name for each argument in your code, but the Apollo docs use these names as a convention. Instead of `parent`, it's also common to use the parent type's name or `source`.
| Argument | Description |
|---|---|
Expand Down
95 changes: 86 additions & 9 deletions docs/source/integrations/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,12 @@ 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
### Request lifecycle event flow

The following diagram illustrates the sequence of events that fire for each request. Each of these events is documented [below](#request-lifecycle-events).

> 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_.
```mermaid
graph TB;
request(requestDidStart) --> resolveSource(didResolveSource);
Expand All @@ -127,26 +129,29 @@ graph TB;
validation --"Success"--> resolveOperation(didResolveOperation);
resolveOperation --"Success"--> response(responseForOperation);
execution(executionDidStart*);
resolveField(willResolveField*);
errors(didEncounterErrors);
errors --> send;
response --"Response provided"--> send;
response --"No response provided"--> execution;
execution --"Success"--> send(willSendResponse);
execution ---->|"Success"| send(willSendResponse);
execution --"(Fires once per resolver)"-->resolveField;
execution & resolveSource & resolveOperation & parsing & validation --"Failure"--> errors;
errors --> send;
class server,request secondary;
class errors secondary;
```

<sup>*The indicated events also support <a href="#end-hooks">end hooks</a> that fire when their associated step <em>completes</em>.</sup>
<sup>*The indicated events also support <a href="#end-hooks">end hooks</a> that are called when their associated step <em>completes</em>.</sup>

#### End hooks
### End hooks

Event handlers for the following events can optionally return a function
that is invoked after the corresponding lifecycle phase _ends_:

* [`parsingDidStart`](#parsingdidstart)
* [`validationDidStart`](#validationdidstart)
* [`executionDidStart`](#executiondidstart)
* [`executionDidStart`](#executiondidstart) (this handler can alternatively return an _object_ containing an `executionDidEnd` function)
* [`willResolveField`](#willresolvefield)

These **end hooks** are passed any errors that occurred during the
execution of that lifecycle phase. For example, the following plugin logs
Expand Down Expand Up @@ -185,7 +190,7 @@ const myPlugin = {
```

Note that the `validationDidStart` end hook receives an _array_ of errors that
contains every validation error that occurred (if any). The arguments to each
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](#request-lifecycle-events).

### Inspecting request and response details
Expand Down Expand Up @@ -266,6 +271,33 @@ const server = new ApolloServer({
})
```

### `serverWillStop`

The `serverWillStop` event fires when Apollo Server is starting to shut down because [`ApolloServer.stop()`](../api/apollo-server/#stop) has been invoked (either explicitly by your code, or by one of the [termination signal handlers](../api/apollo-server/#stoponterminationsignals)). 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`](#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

```js
const server = new ApolloServer({
/* ... other necessary configuration ... */

plugins: [
{
serverWillStart() {
const interval = setInterval(doSomethingPeriodically, 1000);
return {
serverWillStop() {
clearInterval(interval);
}
}
}
}
]
})
```

### `requestDidStart`

The `requestDidStart` event fires whenever Apollo Server begins fulfilling a GraphQL request.
Expand Down Expand Up @@ -376,6 +408,8 @@ 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`](#executiondidstart)).

> If the operation is anonymous (i.e., the operation is `query { ... }` instead of `query NamedQuery { ... }`), then `operationName` is `null`.
```typescript
Expand Down Expand Up @@ -417,6 +451,49 @@ executionDidStart?(
): (err?: Error) => void | void;
```

`executionDidStart` may return an ["end hook"](#end-hooks) 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](../data/resolvers/#resolver-arguments). (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`](#executiondidstart) handler.

Your `willResolveField` handler can optionally return an ["end hook"](#end-hooks) 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

```js
const server = new ApolloServer({
/* ... other necessary configuration ... */

plugins: [
{
requestDidStart(initialRequestContext) {
return {
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 Apollo Server encounters errors while
Expand Down
4 changes: 2 additions & 2 deletions packages/apollo-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class ApolloServer extends ApolloServerBase {
super(config);
this.cors = config && config.cors;
this.onHealthCheck = config && config.onHealthCheck;
this.stopGracePeriodMillis = config?.stopGracePeriodMillis ?? 10000;
this.stopGracePeriodMillis = config?.stopGracePeriodMillis ?? 10_000;
}

private createServerInfo(
Expand Down Expand Up @@ -123,7 +123,7 @@ export class ApolloServer extends ApolloServerBase {
// - closes all connections with no active requests
// - continues to close connections when their active request count drops to
// zero
// - in 3 seconds (configurable), closes all remaining active connections
// - in 10 seconds (configurable), closes all remaining active connections
// - calls its callback once there are no remaining active connections
//
// If you don't like this behavior, use apollo-server-express instead of
Expand Down

0 comments on commit dbf22c2

Please sign in to comment.