diff --git a/docs/source/config.json b/docs/source/config.json
index f394daff390..85cb1d26f93 100644
--- a/docs/source/config.json
+++ b/docs/source/config.json
@@ -1,22 +1,27 @@
{
"title": "Apollo Server",
"version": "v4 (Alpha)",
- "algoliaFilters": [
- "docset:server",
- [
- "docset:react",
- "docset:federation"
- ]
- ],
+ "algoliaFilters": ["docset:server", ["docset:react", "docset:federation"]],
"sidebar": {
"Introduction": "/",
"Get started": "/getting-started",
- "New in v4": {
- "Migrating to Apollo Server 4": "/migration",
- "Changelog": "https://github.com/apollographql/apollo-server/blob/main/CHANGELOG.md"
- },
- "Server frameworks": {
- "Building integrations": "/integrations/building-integrations"
- }
+ "New in v4": {
+ "Migrating to Apollo Server 4": "/migration",
+ "Changelog": "https://github.com/apollographql/apollo-server/blob/main/CHANGELOG.md"
+ },
+ "Defining a Schema": {
+ "Schema basics": "/schema/schema",
+ "Unions and interfaces": "/schema/unions-interfaces",
+ "Custom scalars": "/schema/custom-scalars",
+ "Directives": "/schema/directives"
+ },
+ "Fetching Data": {
+ "Resolvers": "/data/resolvers",
+ "Error handling": "/data/errors",
+ "Subscriptions": "/data/subscriptions"
+ },
+ "Server frameworks": {
+ "Building integrations": "/integrations/building-integrations"
+ }
}
}
diff --git a/docs/source/data/data-sources.mdx b/docs/source/data/data-sources.mdx
index ca0ef2edc32..09b2ba3b2f2 100644
--- a/docs/source/data/data-sources.mdx
+++ b/docs/source/data/data-sources.mdx
@@ -3,6 +3,8 @@ title: Data sources
description: Manage connections to databases and REST APIs
---
+
+
**Data sources** are classes that Apollo Server can use to encapsulate fetching data from a particular source, such as a database or a REST API. These classes help handle caching, deduplication, and errors while resolving operations.
Your server can use any number of different data sources. You don't _have_ to use data sources to fetch data, but they're strongly recommended.
@@ -22,8 +24,6 @@ flowchart LR;
class restAPI,sql secondary;
```
-
-
## Open-source implementations
All data source implementations extend the generic [`DataSource` abstract class](https://github.com/apollographql/apollo-server/blob/main/packages/apollo-datasource/src/index.ts), which is included in the `apollo-datasource` package. Subclasses of a `DataSource` should define whatever logic is required to communicate with a particular store or API.
@@ -45,7 +45,6 @@ If none of these implementations applies to your use case, you can create your o
> Apollo does not provide official support for community-maintained libraries. We cannot guarantee that community-maintained libraries adhere to best practices, or that they will continue to be maintained.
-
## Adding data sources to Apollo Server
You provide your `DataSource` subclasses to the `ApolloServer` constructor, like so:
@@ -55,10 +54,8 @@ const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
- cache: "bounded",
- plugins: [
- ApolloServerPluginLandingPageLocalDefault({ embed: true }),
- ],
+ cache: 'bounded',
+ plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
dataSources: () => {
return {
moviesAPI: new MoviesAPI(),
@@ -68,9 +65,9 @@ const server = new ApolloServer({
});
```
-* As shown, the `dataSources` option is a _function_. This function returns an _object_ containing instances of your `DataSource` subclasses (in this case, `MoviesAPI` and `PersonalizationAPI`).
-* Apollo Server calls this function for _every incoming operation_. It automatically assigns the returned object to the `dataSources` field of [the `context` object](./resolvers/#the-context-argument) that's passed between your server's resolvers.
-* Also as shown, **the function should create a new instance of each data source for each operation.** If multiple operations share a single data source instance, you might accidentally combine results from multiple operations.
+- As shown, the `dataSources` option is a _function_. This function returns an _object_ containing instances of your `DataSource` subclasses (in this case, `MoviesAPI` and `PersonalizationAPI`).
+- Apollo Server calls this function for _every incoming operation_. It automatically assigns the returned object to the `dataSources` field of [the `context` object](./resolvers/#the-context-argument) that's passed between your server's resolvers.
+- Also as shown, **the function should create a new instance of each data source for each operation.** If multiple operations share a single data source instance, you might accidentally combine results from multiple operations.
Your resolvers can now access your data sources from the shared `context` object and use them to fetch data:
@@ -98,7 +95,7 @@ When you initialize Apollo Server, you can provide its constructor a _different_
### Using an external cache backend
- When running multiple instances of your server, you should use a shared cache backend. This enables one server instance to use the cached result from _another_ instance.
+When running multiple instances of your server, you should use a shared cache backend. This enables one server instance to use the cached result from _another_ instance.
Apollo Server supports using [Memcached](https://memcached.org/), [Redis](https://redis.io/), or other cache backends via the [`keyv`](https://www.npmjs.com/package/keyv) package. For examples, see [Configuring external caching](../performance/cache-backends#configuring-external-caching).
@@ -165,7 +162,7 @@ class MoviesAPI extends RESTDataSource {
// GET
async getMovie(id) {
return this.get(
- `movies/${encodeURIComponent(id)}` // path
+ `movies/${encodeURIComponent(id)}`, // path
);
}
@@ -207,8 +204,9 @@ class MoviesAPI extends RESTDataSource {
> Note the use of [`encodeURIComponent`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent). This is a standard JavaScript function that encodes special characters in a URI, preventing a possible injection attack vector.
For a simple example, suppose our REST endpoint responded to the following URLs:
-* DELETE `/movies/:id`
-* DELETE `/movies/:id/characters`
+
+- DELETE `/movies/:id`
+- DELETE `/movies/:id/characters`
A "malicious" client could provide an `:id` of `1/characters` to target the delete `characters` endpoint when it was the singular `movie` endpoint that we were trying to delete. URI encoding prevents this kind of injection by transforming the `/` into `%2F`. This can then be correctly decoded and interpreted by the server and won't be treated as a path segment.
@@ -218,8 +216,8 @@ For all HTTP convenience methods, the **first parameter** is the relative path o
The **second parameter** depends on the HTTP method:
-* For HTTP methods with a request body (`post`, `put`, `patch`), the second parameter _is_ the request body.
-* For HTTP methods _without_ a request body, the second parameter is an object with keys and values corresponding to the request's query parameters.
+- For HTTP methods with a request body (`post`, `put`, `patch`), the second parameter _is_ the request body.
+- For HTTP methods _without_ a request body, the second parameter is an object with keys and values corresponding to the request's query parameters.
For all methods, the **third parameter** is an `init` object that enables you to provide additional options (such as headers and referrers) to the `fetch` API that's used to send the request. For details, [see MDN's fetch docs](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters).
@@ -229,7 +227,6 @@ For all methods, the **third parameter** is an `init` object that enables you to
Data sources also have access to the GraphQL operation context, which is useful for storing a user token or other relevant information.
-
#### Setting a header
```js
@@ -288,9 +285,9 @@ DataLoader is great for its intended use case, but itโs less helpful when load
When layering GraphQL over REST APIs, it's most helpful to have a resource cache that:
-* Saves data across multiple GraphQL requests
-* Can be shared across multiple GraphQL servers
-* Provides cache management features like expiry and invalidation that use standard HTTP cache control headers
+- Saves data across multiple GraphQL requests
+- Can be shared across multiple GraphQL servers
+- Provides cache management features like expiry and invalidation that use standard HTTP cache control headers
#### Batching with REST APIs
diff --git a/docs/source/data/errors.mdx b/docs/source/data/errors.mdx
index f9b525d2412..4e3f02ae5cb 100644
--- a/docs/source/data/errors.mdx
+++ b/docs/source/data/errors.mdx
@@ -5,33 +5,30 @@ description: Making errors actionable on the client and server
-Whenever Apollo Server encounters errors while processing a GraphQL operation, its response to the client includes an `errors` array that contains each error that occurred. Each error in the array has an `extensions` field that provides additional useful information, including an error `code` and (while in development mode) an `exception.stacktrace`.
+Whenever Apollo Server encounters errors while processing a GraphQL operation, its response to the client includes an `errors` array containing each error that occurred. Each error in the array has an `extensions` field that provides additional useful information, including an error `code` and (while in development mode) a `stacktrace`.
Here's an example error response caused by misspelling the `__typename` field in a query:
-
Code / Subclass |
+ Code | Description | The GraphQL operation string contains a syntax error. + | @@ -74,11 +83,11 @@ The GraphQL operation string contains a syntax error. ###### `GRAPHQL_VALIDATION_FAILED` -`ValidationError`The GraphQL operation is not valid against the server's schema. + | @@ -87,63 +96,65 @@ The GraphQL operation is not valid against the server's schema. ###### `BAD_USER_INPUT` -`UserInputError`The GraphQL operation includes an invalid value for a field argument. + |
---|---|---|
-###### `UNAUTHENTICATED` +###### `PERSISTED_QUERY_NOT_FOUND` -`AuthenticationError` | -The server failed to authenticate with a required data source, such as a REST API. +A client sent the hash of a query string to execute via [automatic persisted queries](/apollo-server/performance/apq/), but the query was not in the APQ cache. + | |
-###### `FORBIDDEN` +###### `PERSISTED_QUERY_NOT_SUPPORTED` -`ForbiddenError` | -The server was unauthorized to access a required data source, such as a REST API. +A client sent the hash of a query string to execute via [automatic persisted queries](/apollo-server/performance/apq/), but the server has disabled APQ. + | |
-###### `PERSISTED_QUERY_NOT_FOUND` +###### `OPERATION_RESOLUTION_FAILURE` -`PersistedQueryNotFoundError` | -A client sent the hash of a query string to execute via [automatic persisted queries](../performance/apq/), but the query was not in the APQ cache. +The request was parsed successfully and is valid against the server's schema, but the server couldn't resolve which operation to run. + +This occurs when a request containing multiple named operations doesn't specify which operation to run (i.e.,`operationName`), or if the named operation isn't included in the request. + | |
-###### `PERSISTED_QUERY_NOT_SUPPORTED` +###### `BAD_REQUEST` -`PersistedQueryNotSupportedError` | -A client sent the hash of a query string to execute via [automatic persisted queries](../performance/apq/), but the server has disabled APQ. +An error occurred before your server could attempt to parse the given GraphQL operation. + | An unspecified error occurred. -This is the default error code returned by any `ApolloError` instance that doesn't specify a different code. +When Apollo Server formats an error in a response, it sets the code extension to this value if no other code is set. + |
A resolver can return a single value or an object, as shown in [Defining a resolver](#defining-a-resolver). This return value is passed down to any nested resolvers via the `parent` argument.
| | `Array` |Return an array if and only if your schema indicates that the resolver's associated field contains a list.
After you return an array, Apollo Server executes nested resolvers for each item in the array.
| | `null` / `undefined` |Indicates that the value for the field could not be found.
If your schema indicates that this resolver's field is nullable, then the operation result has a `null` value at the field's position.
If this resolver's field is _not_ nullable, Apollo Server sets the field's _parent_ to `null`. If necessary, this process continues up the resolver chain until it reaches a field that _is_ nullable. This ensures that a response never includes a `null` value for a non-nullable field. When this happens, the response's `errors` property will be populated with relevant errors concerning the nullability of that field.
| -| [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) |Resolvers often perform asynchronous actions, such as fetching from a database or back-end API. To support this, a resolver can return a promise that resolves to any other supported return type.
| - +| [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) |Resolvers can be asynchronous and perform async actions, such as fetching from a database or back-end API. To support this, a resolver can return a promise that resolves to any other supported return type.
| ## Default resolvers diff --git a/docs/source/data/subscriptions.mdx b/docs/source/data/subscriptions.mdx index 79e40294912..8477c78795c 100644 --- a/docs/source/data/subscriptions.mdx +++ b/docs/source/data/subscriptions.mdx @@ -3,15 +3,15 @@ title: Subscriptions in Apollo Server description: Persistent GraphQL read operations --- -> **Apollo Server 3 removes built-in support for subscriptions.** You can reenable support as [described below](#enabling-subscriptions). +>**Apollo Server does not provide built-in support for subscriptions.** You can enable support for subscriptions as [described below](#enabling-subscriptions). > > **Subscriptions are not currently supported in [Apollo Federation](/federation/).** > -> This article has been updated to use the `graphql-ws` library to add support for subscriptions to Apollo Server. We no longer recommend using the previously documented `subscriptions-transport-ws`, because this library is not actively maintained. For more information about the differences between the two libraries, see [Switching from `subscriptions-transport-ws`](#switching-from-subscriptions-transport-ws). +> This article uses the `graphql-ws` library to add support for subscriptions to Apollo Server 4. We no longer recommend using the previously documented `subscriptions-transport-ws`, because this library is not actively maintained. For more information about the differences between the two libraries, see [Switching from `subscriptions-transport-ws`](#switching-from-subscriptions-transport-ws). **Subscriptions** are long-lasting GraphQL read operations that can update their result whenever a particular server-side event occurs. Most commonly, updated results are _pushed_ from the server to subscribing clients. For example, a chat application's server might use a subscription to push newly received messages to all clients in a particular chat room. -Because subscription updates are usually pushed by the server (instead of polled by the client), they usually use [the WebSocket protocol](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) instead of HTTP. +Because subscription updates are usually pushed by the server (instead of polled by the client), they generally use [the WebSocket protocol](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) instead of HTTP. > **Important:** Compared to queries and mutations, subscriptions are significantly more complex to implement. Before you begin, [confirm that your use case requires subscriptions](/react/data/subscriptions/#when-to-use-subscriptions). @@ -42,102 +42,94 @@ subscription PostFeed { ## Enabling subscriptions -> Beginning in Apollo Server 3, subscriptions are **not** supported by the "batteries-included" `apollo-server` package. To enable subscriptions, you must first [swap to the `apollo-server-express` package](../integrations/middleware/#swapping-out-apollo-server) (or any other Apollo Server integration package that supports subscriptions). + + +> Subscriptions are **not** supported by Apollo Server 4's `startStandaloneServer` function. To enable subscriptions, you must first [swap to using the `expressMiddleware` function](./migration#migrate-from-apollo-server-express) (or any other Apollo Server integration package that supports subscriptions). > -> The following steps assume you've already swapped to `apollo-server-express`. +> The following steps assume you've already swapped to `expressMiddleware`. To run both an Express app _and_ a separate WebSocket server for subscriptions, we'll create an `http.Server` instance that effectively wraps the two and becomes our new `listen`er. -1. Install `graphql-ws`, `ws`, `@graphql-tools/schema`, and `apollo-server-core`: - ```bash - npm install graphql-ws ws @graphql-tools/schema apollo-server-core - ``` +1. Install `graphql-ws`, `ws`, and `@graphql-tools/schema`: + + ```bash + npm install graphql-ws ws @graphql-tools/schema + ``` 2. Add the following imports to the file where you initialize your `ApolloServer` instance (we'll use these in later steps): - ```ts title="index.ts" - import { createServer } from 'http'; - import { - ApolloServerPluginDrainHttpServer, - ApolloServerPluginLandingPageLocalDefault, - } from "apollo-server-core"; - import { makeExecutableSchema } from '@graphql-tools/schema'; - import { WebSocketServer } from 'ws'; - import { useServer } from 'graphql-ws/lib/use/ws'; - ``` + ```ts title="index.ts" + import { createServer } from 'http'; + import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer'; + import { makeExecutableSchema } from '@graphql-tools/schema'; + import { WebSocketServer } from 'ws'; + import { useServer } from 'graphql-ws/lib/use/ws'; + ``` 3. Next, in order to set up both the HTTP and subscription servers, we need to first create an `http.Server`. Do this by passing your Express `app` to the `createServer` function, which we imported from the `http` module: - ```ts title="index.ts" - // This `app` is the returned value from `express()`. - const httpServer = createServer(app); - ``` + ```ts title="index.ts" + // This `app` is the returned value from `express()`. + const httpServer = createServer(app); + ``` 4. Create an instance of `GraphQLSchema` (if you haven't already). - > If you already pass the `schema` option to the `ApolloServer` constructor (instead of `typeDefs` and `resolvers`), you can skip this step. + > If you already pass the `schema` option to the `ApolloServer` constructor (instead of `typeDefs` and `resolvers`), you can skip this step. - The subscription server (which we'll instantiate next) doesn't take `typeDefs` and `resolvers` options. Instead, it takes an executable `GraphQLSchema`. We can pass this `schema` object to both the subscription server and `ApolloServer`. This way, we make sure that the same schema is being used in both places. + The subscription server (which we'll instantiate next) doesn't take `typeDefs` and `resolvers` options. Instead, it takes an executable `GraphQLSchema`. We can pass this `schema` object to both the subscription server and `ApolloServer`. This way, we make sure that the same schema is being used in both places. - ```ts title="index.ts" - const schema = makeExecutableSchema({ typeDefs, resolvers }); - // ... - const server = new ApolloServer({ - schema, - csrfPrevention: true, - cache: "bounded", - plugins: [ - ApolloServerPluginLandingPageLocalDefault({ embed: true }), - ], - }); - ``` + ```ts title="index.ts" + const schema = makeExecutableSchema({ typeDefs, resolvers }); + // ... + const server = new ApolloServer({ + schema, + }); + ``` 5. Create a `WebSocketServer` to use as your subscription server. - ```ts title="index.ts" - // Creating the WebSocket server - const wsServer = new WebSocketServer({ - // This is the `httpServer` we created in a previous step. - server: httpServer, - // Pass a different path here if your ApolloServer serves at - // a different path. - path: '/graphql', - }); - - // Hand in the schema we just created and have the - // WebSocketServer start listening. - const serverCleanup = useServer({ schema }, wsServer); - ``` - -6. Add [plugins](../integrations/plugins) to your `ApolloServer` constructor to shutdown both the HTTP server and the `WebSocketServer`: - - ```ts title="index.ts" - const server = new ApolloServer({ - schema, - csrfPrevention: true, - cache: "bounded", - plugins: [ - // Proper shutdown for the HTTP server. - ApolloServerPluginDrainHttpServer({ httpServer }), - - // Proper shutdown for the WebSocket server. - { - async serverWillStart() { - return { - async drainServer() { - await serverCleanup.dispose(); - }, - }; - }, - }, - ApolloServerPluginLandingPageLocalDefault({ embed: true }), - ], - }); - ``` + ```ts title="index.ts" + // Creating the WebSocket server + const wsServer = new WebSocketServer({ + // This is the `httpServer` we created in a previous step. + server: httpServer, + // Pass a different path here if app.use + // serves expressMiddleware at a different path + path: '/graphql', + }); + + // Hand in the schema we just created and have the + // WebSocketServer start listening. + const serverCleanup = useServer({ schema }, wsServer); + ``` + +6. Add [plugins](/apollo-server/integrations/plugins) to your `ApolloServer` constructor to shutdown both the HTTP server and the `WebSocketServer`: + + ```ts title="index.ts" + const server = new ApolloServer({ + schema, + plugins: [ + // Proper shutdown for the HTTP server. + ApolloServerPluginDrainHttpServer({ httpServer }), + + // Proper shutdown for the WebSocket server. + { + async serverWillStart() { + return { + async drainServer() { + await serverCleanup.dispose(); + }, + }; + }, + }, + ], + }); + ``` 7. Finally, ensure you are `listen`ing to your `httpServer`. - Most Express applications call `app.listen(...)`, but for your setup change this to `httpServer.listen(...)` using the same arguments. This way, the server starts listening on the HTTP and WebSocket transports simultaneously. + Most Express applications call `app.listen(...)`, but for your setup change this to `httpServer.listen(...)` using the same arguments. This way, the server starts listening on the HTTP and WebSocket transports simultaneously. A completed example of setting up subscriptions is shown below: @@ -146,13 +138,16 @@ A completed example of setting up subscriptions is shown below:[`subscriptions-transport-ws`](/studio/explorer/additional-features/#subscription-support) + | @@ -633,7 +638,6 @@ If you intend to [switch from `subscriptions-transport-ws` to `graphql-ws`](#swi -
Use [`GraphQLWsLink`](/react/api/link/apollo-link-subscriptions/) @@ -644,11 +648,11 @@ Use [`GraphQLWsLink`](/react/api/link/apollo-link-subscriptions/) | Use [`WebSocketLink`](/react/api/link/apollo-link-ws/) + | -
@@ -656,7 +660,6 @@ Use [`WebSocketLink`](/react/api/link/apollo-link-ws/) | -
[`graphql_transport_ws`](/ios/api/ApolloWebSocket/enums/WebSocket.WSProtocol/#graphql_transport_ws) @@ -667,11 +670,11 @@ Use [`WebSocketLink`](/react/api/link/apollo-link-ws/) | [`graphql_ws`](/ios/api/ApolloWebSocket/enums/WebSocket.WSProtocol/#graphql_ws) + |
@@ -689,147 +692,10 @@ Use [`WebSocketLink`](/react/api/link/apollo-link-ws/) | [`SubscriptionWsProtocol`](/kotlin/essentials/subscriptions/#customizing-your-websocket-protocol) + |