diff --git a/.api-reports/api-report-link_batch-http.api.md b/.api-reports/api-report-link_batch-http.api.md index e6311f4d5f9..e0a3636f9e4 100644 --- a/.api-reports/api-report-link_batch-http.api.md +++ b/.api-reports/api-report-link_batch-http.api.md @@ -19,10 +19,11 @@ export class BaseBatchHttpLink extends ApolloLink { // @public (undocumented) export namespace BatchHttpLink { - // (undocumented) - export type ContextOptions = HttpLink.ContextOptions; - // (undocumented) - export type Options = Pick & Omit; + export interface ContextOptions extends HttpLink.ContextOptions { + } + export interface Options extends BatchLink.Shared.Options, HttpLink.Shared.Options { + batchMax?: number; + } } // @public diff --git a/.api-reports/api-report-link_batch.api.md b/.api-reports/api-report-link_batch.api.md index d7ba2b71bd2..d6a791ed860 100644 --- a/.api-reports/api-report-link_batch.api.md +++ b/.api-reports/api-report-link_batch.api.md @@ -12,13 +12,19 @@ export namespace BatchLink { // (undocumented) export type BatchHandler = (operations: ApolloLink.Operation[], forward: ApolloLink.ForwardFunction[]) => Observable; // (undocumented) - export interface Options { - batchDebounce?: boolean; + export interface Options extends Shared.Options { batchHandler?: BatchLink.BatchHandler; - batchInterval?: number; - batchKey?: (operation: ApolloLink.Operation) => string; batchMax?: number; } + // (undocumented) + export namespace Shared { + export interface Options { + batchDebounce?: boolean; + batchInterval?: number; + batchKey?: (operation: ApolloLink.Operation) => string; + batchMax?: number; + } + } } // @public (undocumented) diff --git a/.api-reports/api-report-link_client-awareness.api.md b/.api-reports/api-report-link_client-awareness.api.md index e54102f6e5a..2d88f8eb6dd 100644 --- a/.api-reports/api-report-link_client-awareness.api.md +++ b/.api-reports/api-report-link_client-awareness.api.md @@ -20,12 +20,12 @@ export namespace ClientAwarenessLink { } // (undocumented) export interface Options { - clientAwareness?: ClientAwarenessOptions; - enhancedClientAwareness?: EnhancedClientAwarenessOptions; + clientAwareness?: ClientAwarenessLink.ClientAwarenessOptions; + enhancedClientAwareness?: ClientAwarenessLink.EnhancedClientAwarenessOptions; } } -// @public (undocumented) +// @public export class ClientAwarenessLink extends ApolloLink { constructor(constructorOptions?: ClientAwarenessLink.Options); } diff --git a/.api-reports/api-report-link_error.api.md b/.api-reports/api-report-link_error.api.md index 39f5baa2e14..2e9db05e90b 100644 --- a/.api-reports/api-report-link_error.api.md +++ b/.api-reports/api-report-link_error.api.md @@ -14,16 +14,19 @@ export namespace ErrorLink { // (undocumented) (options: ErrorHandlerOptions): Observable | void; } - // (undocumented) export interface ErrorHandlerOptions { error: ErrorLike; forward: ApolloLink.ForwardFunction; operation: ApolloLink.Operation; result?: ApolloLink.Result; } + // (undocumented) + export namespace ErrorLinkDocumentationTypes { + export function ErrorHandler(options: ErrorHandlerOptions): Observable | void; + } } -// @public (undocumented) +// @public export class ErrorLink extends ApolloLink { constructor(errorHandler: ErrorLink.ErrorHandler); } diff --git a/.api-reports/api-report-link_http.api.md b/.api-reports/api-report-link_http.api.md index eb9c5cd446d..92f4ea2d5ae 100644 --- a/.api-reports/api-report-link_http.api.md +++ b/.api-reports/api-report-link_http.api.md @@ -74,7 +74,7 @@ export namespace HttpLink { fetchOptions?: RequestInit; headers?: Record; http?: HttpLink.HttpOptions; - uri?: string | UriFunction; + uri?: string | HttpLink.UriFunction; } export interface HttpOptions { accept?: string[]; @@ -82,25 +82,30 @@ export namespace HttpLink { includeQuery?: boolean; preserveHeaderCase?: boolean; } - export interface Options { - credentials?: RequestCredentials; - fetch?: typeof fetch; - fetchOptions?: RequestInit; - headers?: Record; - includeExtensions?: boolean; - includeUnusedVariables?: boolean; - preserveHeaderCase?: boolean; - print?: Printer; - uri?: string | UriFunction; + export interface Options extends Shared.Options { useGETForQueries?: boolean; } // (undocumented) export type Printer = (node: ASTNode, originalPrint: typeof print_2) => string; // (undocumented) + export namespace Shared { + export interface Options { + credentials?: RequestCredentials; + fetch?: typeof fetch; + fetchOptions?: RequestInit; + headers?: Record; + includeExtensions?: boolean; + includeUnusedVariables?: boolean; + preserveHeaderCase?: boolean; + print?: HttpLink.Printer; + uri?: string | HttpLink.UriFunction; + } + } + // (undocumented) export type UriFunction = (operation: ApolloLink.Operation) => string; } -// @public (undocumented) +// @public export class HttpLink extends ApolloLink { constructor(options?: HttpLink.Options & ClientAwarenessLink.Options); } diff --git a/.api-reports/api-report-link_subscriptions.api.md b/.api-reports/api-report-link_subscriptions.api.md index 71b66e06114..cf2076f956e 100644 --- a/.api-reports/api-report-link_subscriptions.api.md +++ b/.api-reports/api-report-link_subscriptions.api.md @@ -8,7 +8,7 @@ import { ApolloLink } from '@apollo/client/link'; import type { Client } from 'graphql-ws'; import { Observable } from 'rxjs'; -// @public (undocumented) +// @public export class GraphQLWsLink extends ApolloLink { constructor(client: Client); // (undocumented) diff --git a/.api-reports/api-report.api.md b/.api-reports/api-report.api.md index bd5b69aef54..35a378f35c3 100644 --- a/.api-reports/api-report.api.md +++ b/.api-reports/api-report.api.md @@ -667,14 +667,12 @@ namespace ClientAwarenessLink { } // (undocumented) interface Options { - // Warning: (ae-forgotten-export) The symbol "ClientAwarenessLink" needs to be exported by the entry point index.d.ts - clientAwareness?: ClientAwarenessOptions; - // Warning: (ae-forgotten-export) The symbol "ClientAwarenessLink" needs to be exported by the entry point index.d.ts - enhancedClientAwareness?: EnhancedClientAwarenessOptions; + clientAwareness?: ClientAwarenessLink.ClientAwarenessOptions; + enhancedClientAwareness?: ClientAwarenessLink.EnhancedClientAwarenessOptions; } } -// @public (undocumented) +// @public class ClientAwarenessLink extends ApolloLink { constructor(constructorOptions?: ClientAwarenessLink.Options); } @@ -1354,7 +1352,7 @@ export namespace HttpLink { fetchOptions?: RequestInit; headers?: Record; http?: HttpLink.HttpOptions; - uri?: string | UriFunction; + uri?: string | HttpLink.UriFunction; } export interface HttpOptions { accept?: string[]; @@ -1362,16 +1360,7 @@ export namespace HttpLink { includeQuery?: boolean; preserveHeaderCase?: boolean; } - export interface Options { - credentials?: RequestCredentials; - fetch?: typeof fetch; - fetchOptions?: RequestInit; - headers?: Record; - includeExtensions?: boolean; - includeUnusedVariables?: boolean; - preserveHeaderCase?: boolean; - print?: Printer; - uri?: string | UriFunction; + export interface Options extends Shared.Options { useGETForQueries?: boolean; } // Warning: (ae-forgotten-export) The symbol "print_2" needs to be exported by the entry point index.d.ts @@ -1379,10 +1368,24 @@ export namespace HttpLink { // (undocumented) export type Printer = (node: ASTNode, originalPrint: typeof print_2) => string; // (undocumented) + export namespace Shared { + export interface Options { + credentials?: RequestCredentials; + fetch?: typeof fetch; + fetchOptions?: RequestInit; + headers?: Record; + includeExtensions?: boolean; + includeUnusedVariables?: boolean; + preserveHeaderCase?: boolean; + print?: HttpLink.Printer; + uri?: string | HttpLink.UriFunction; + } + } + // (undocumented) export type UriFunction = (operation: ApolloLink.Operation) => string; } -// @public (undocumented) +// @public export class HttpLink extends ApolloLink { constructor(options?: HttpLink.Options & ClientAwarenessLink.Options); } diff --git a/.changeset/plenty-flies-relate.md b/.changeset/plenty-flies-relate.md new file mode 100644 index 00000000000..6dc22b133e1 --- /dev/null +++ b/.changeset/plenty-flies-relate.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +update type of `HttpLink.Options.fetchOptions` to `RequestInit` diff --git a/config/helpers.ts b/config/helpers.ts index e8e3003cac0..12fef37120a 100644 --- a/config/helpers.ts +++ b/config/helpers.ts @@ -248,6 +248,7 @@ export function patchApiExtractorInternals() { for (const excerpt of item.excerptTokens) { const stringified = excerpt.canonicalReference?.toString(); if (stringified?.startsWith("@apollo/client!entrypoint_")) { + // @ts-ignore excerpt["_canonicalReference"] = DeclarationReference.parse( stringified.replace( /(@apollo\/client!entrypoint_[^.]+)[.:]/, diff --git a/config/inlineInheritDoc.ts b/config/inlineInheritDoc.ts index 91350b4faf4..42d96c880f6 100644 --- a/config/inlineInheritDoc.ts +++ b/config/inlineInheritDoc.ts @@ -37,7 +37,7 @@ import { } from "@microsoft/api-extractor"; import { ApiDocumentedItem, ApiModel } from "@microsoft/api-extractor-model"; import type { DocComment, DocExcerpt, DocNode } from "@microsoft/tsdoc"; -import { TextRange } from "@microsoft/tsdoc"; +import type { TextRange } from "@microsoft/tsdoc"; import { DeclarationReference } from "@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js"; import { visit } from "recast"; @@ -77,7 +77,7 @@ function getCommentFor( const unusedVariables = new Set(Object.keys(variables || {})); let string = renderDocComment(apiItem.tsdocComment); - string = string.replaceAll(/\{\{(\w+)\}\}/g, (_, variable) => { + string = string.replaceAll(/\\?\{\\?\{(\w+)\\?\}\\?\}/g, (_, variable) => { unusedVariables.delete(variable); const value = variables?.[variable]; if (value === undefined) { diff --git a/docs/source/_sidebar.yaml b/docs/source/_sidebar.yaml index 610eb3453ae..2957511b0f5 100644 --- a/docs/source/_sidebar.yaml +++ b/docs/source/_sidebar.yaml @@ -189,27 +189,27 @@ items: href: ./api/link/introduction - label: ApolloLink href: ./api/link/apollo-link - - label: HttpLink - href: ./api/link/apollo-link-http - label: BatchHttpLink href: ./api/link/apollo-link-batch-http - - label: SetContextLink - href: ./api/link/apollo-link-context + - label: ClientAwarenessLink + href: ./api/link/apollo-link-client-awareness - label: ErrorLink href: ./api/link/apollo-link-error + - label: GraphQLWsLink + href: ./api/link/apollo-link-subscriptions + - label: HttpLink + href: ./api/link/apollo-link-http - label: PersistedQueryLink href: ./api/link/persisted-queries - label: RemoveTypenameFromVariablesLink href: ./api/link/apollo-link-remove-typename - - label: RestLink - href: ./api/link/apollo-link-rest - label: RetryLink href: ./api/link/apollo-link-retry - label: SchemaLink href: ./api/link/apollo-link-schema - - label: GraphQLWsLink (newer protocol) - href: ./api/link/apollo-link-subscriptions - - label: WebSocketLink (older protocol) + - label: SetContextLink + href: ./api/link/apollo-link-context + - label: WebSocketLink (deprecated) href: ./api/link/apollo-link-ws - label: Community links href: ./api/link/community-links diff --git a/docs/source/api/link/apollo-link-batch-http.mdx b/docs/source/api/link/apollo-link-batch-http.mdx index 0f8b0f9e7df..ae702d6a96d 100644 --- a/docs/source/api/link/apollo-link-batch-http.mdx +++ b/docs/source/api/link/apollo-link-batch-http.mdx @@ -1,241 +1,23 @@ --- -title: Batch HTTP Link +title: BatchHttpLink description: Batch multiple operations into a single HTTP request --- -## Overview + -The `BatchHttpLink` is a terminating link that batches an array of individual GraphQL operations into a single HTTP request that's sent to a single GraphQL endpoint. +## Constructor signature -```js -import { BatchHttpLink } from "@apollo/client/link/batch-http"; +{/* TODO: Remove the custom signature once documentation supports rendering constructor functions */} -const link = new BatchHttpLink({ - uri: "http://localhost:4000/graphql", - batchMax: 5, // No more than 5 operations per batch - batchInterval: 20, // Wait no more than 20ms after first batched operation -}); +```ts +constructor( + options: BatchHttpLink.Options & ClientAwarenessLink.Options = {} +): BatchHttpLink ``` -If you use `BatchHttpLink` instead of [`HttpLink`](./apollo-link-http) as your terminating link, Apollo Client automatically batches executed GraphQL operations and transmits them to your server according to the [batching options](#options) you provide. - -## Options - -The `BatchHttpLink` constructor accepts a configuration object that supports the following options: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name /
Type
Description
- -**Batching options** - -
- -###### `batchMax` - -`number` - - - -The maximum number of operations to include in a single batch. - -The default value is `10`. - -
- -###### `batchInterval` - -`number` - - - -The maximum number of milliseconds to wait before sending each batched request. If `batchMax` operations are batched before `batchInterval` is reached, the request is sent immediately. - -The default value is `10`. - -
- -###### `batchDebounce` - -`boolean` - - - -If `true`, the `batchInterval` timer is reset whenever an operation is added to the batch. In other words, the next batched request is not sent until either: - -- No operation is added for `batchInterval` milliseconds, or -- `batchMax` is reached. - -The default value is `false`. - -
- -###### `batchKey` - -`string` - - - -A function that accepts an operation and returns a string key, which uniquely names the batch the operation belongs to. - -[See the default function](https://github.com/apollographql/apollo-client/blob/main/src/link/batch-http/batchHttpLink.ts#L192-L206) - -
- -**HTTP options** - -
- -###### `uri` - -`String` or `Function` - - - -The URL of the GraphQL endpoint to send requests to. Can also be a function that accepts an `Operation` object and returns the string URL to use for that operation. - -The default value is `/graphql`. - -
- -###### `includeExtensions` - -`Boolean` - - - -If true, includes the `extensions` field in operations sent to your GraphQL endpoint. - -The default value is `false`. - -
- -###### `fetch` - -`Function` - - - -A function to use instead of calling the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) directly when sending HTTP requests to your GraphQL endpoint. The function must conform to the signature of `fetch`. - -By default, the Fetch API is used unless it isn't available in your runtime environment. - -See [Customizing `fetch`](./apollo-link-http/#customizing-fetch). - -
- -###### `headers` - -`Object` - - - -An object representing headers to include in every HTTP request, such as `{Authentication: 'Bearer abc123'}`. - -
- -###### `preserveHeaderCase` - -`Boolean` - - - -If set to true, header names won't be automatically normalized to lowercase. This allows for non-http-spec-compliant servers that might expect capitalized header names. - -The default value is `false`. - -
- -###### `credentials` - -`String` - - - -The credentials policy to use for each `fetch` call. Can be `omit`, `include`, or `same-origin`. - -
- -###### `fetchOptions` - -`Object` - - - -An object containing options to use for each call to `fetch`. If a particular option is not included in this object, the default value of that option is used. - -Note that if you set `fetchOptions.method` to `GET`, `BatchHttpLink` follows [standard GraphQL HTTP GET encoding](http://graphql.org/learn/serving-over-http/#get-request). - -[See available options](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) - -
- ## Context The batch HTTP link currently uses the context in two different ways, per batch and per query. The context fields below are used per batch and taken from the first operation in the batch. @@ -246,32 +28,28 @@ The batch HTTP link currently uses the context in two different ways, per batch | `credentials` | A string representing the credentials policy you want for the fetch call | | `uri` | A string of the endpoint you want to fetch from | | `fetchOptions` | Any overrides of the fetch options argument to pass to the fetch call | -| `response` | This is the raw response from the fetch request after it is made | For each query, the `http` field is used to modify each individual query in the batch, such as persisted queries (see below). -### Persisted queries - -The batch HTTP link supports an advanced GraphQL feature called persisted queries. This allows you to not send the stringified query over the wire, but instead send some kind of identifier for the query. To support this you need to attach the id somewhere in the extensions field, and pass the following options to the context: +## Operation results -```js -operation.setContext({ - http: { - includeExtensions: true, - includeQuery: false, - }, -}); -``` +After your GraphQL endpoint successfully responds with the result of an operation, `BatchHttpLink` sets the `Response` object as the `response` field of the operation `context`. This enables each previous link in your link chain to interact with the response. -From the context `http` object: +## Custom fetching -- `includeExtensions`: Send the extensions object for this request. -- `includeQuery`: Don't send the `query` field for this request. +See [Customizing `fetch`](apollo-link-http/#customizing-fetch). -See the [http option fields](https://www.apollographql.com/docs/react/api/link/apollo-link-http/#http-option-fields) for more information. +## Types -One way to use persisted queries is with [apollo-link-persisted-queries](https://www.apollographql.com/docs/react/api/link/persisted-queries) and [Apollo Server](/apollo-server/performance/apq/). + -## Custom fetching - -See [Customizing `fetch`](apollo-link-http/#customizing-fetch). + diff --git a/docs/source/api/link/apollo-link-client-awareness.mdx b/docs/source/api/link/apollo-link-client-awareness.mdx new file mode 100644 index 00000000000..102354d09ff --- /dev/null +++ b/docs/source/api/link/apollo-link-client-awareness.mdx @@ -0,0 +1,107 @@ +--- +title: ClientAwarenessLink +description: API reference +--- + + + +## Configuring client awareness + +Client awareness can be configured in various ways in Apollo Client. + +### Configuring with Apollo Client + +You can configure client awareness when initializing your Apollo Client instance using the `clientAwareness` and `enhancedClientAwareness` options. Options configured with the `ClientAwarenessLink` constructor, `HttpLink` constructor, or [request context](./introduction#managing-context) take precedence. + +```ts +import { ApolloClient } from "@apollo/client"; + +new ApolloClient({ + clientAwareness: { + name: "My Client", + version: "my_client_version", + }, + enhancedClientAwareness: { + transport: "extensions", + }, +}); +``` + +### Configuring with `HttpLink` + +You can configure client awareness when initializing an `HttpLink` using the `clientAwareness` and `enhancedClientAwareness` options. These options take precedence over options provided to the `ApolloClient` constructor but can be overridden by [request context](./introduction#managing-context). + +```ts +import { ApolloClient, HttpLink } from "@apollo/client"; + +const link = new HttpLink({ + clientAwareness: { + name: "My Client", + version: "my_client_version", + }, + enhancedClientAwareness: { + transport: "extensions", + }, +}); + +const client = new ApolloClient({ + link, + // additional options +}); +``` + + + +If you use `BaseHttpLink`, add `ClientAwarenessLink` to your link chain manually to enable client awareness. `HttpLink` includes `ClientAwarenessLink` by default. + + + +### Configuring with request context + +Configure client awareness on a per-request basis by providing the `clientAwareness` field in the request's `context`. These values take precedence over all other configurations. + + + +The `enhancedClientAwareness` field is not supported in request context. Configure this feature at the client or link level instead. + + + +```ts +const client = new ApolloClient(/* ... */); + +function MyComponent() { + const { data } = useQuery(query, { + context: { + clientAwareness: { + name: "My Client", + version: "my_client_version", + }, + }, + }); + + // ... +} +``` + +## Types + + + + + + diff --git a/docs/source/api/link/apollo-link-error.mdx b/docs/source/api/link/apollo-link-error.mdx index adc0655a97f..7007937c854 100644 --- a/docs/source/api/link/apollo-link-error.mdx +++ b/docs/source/api/link/apollo-link-error.mdx @@ -1,48 +1,31 @@ --- -title: Error Link +title: ErrorLink description: Handle and inspect errors in your GraphQL network stack. --- -> We recommend reading [Apollo Link overview](./introduction/) before learning about individual links. + -Use the `ErrorLink` to perform custom logic when a [GraphQL or network error](../../data/error-handling/) occurs. You create an instance of `ErrorLink` with a function that's executed if an operation returns one or more errors: +## Constructor signature -```js -import { ErrorLink } from "@apollo/client/link/error"; -import { - CombinedGraphQLErrors, - CombinedProtocolErrors, -} from "@apollo/client/errors"; +{/* TODO: Remove the custom signature once documentation supports rendering constructor functions */} -// Log any GraphQL errors, protocol errors, or network error that occurred -const errorLink = new ErrorLink(({ error, operation }) => { - if (CombinedGraphQLErrors.is(error)) { - error.errors.forEach(({ message, locations, path }) => - console.log( - `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` - ) - ); - } else if (CombinedProtocolErrors.is(error)) { - error.errors.forEach(({ message, extensions }) => - console.log( - `[Protocol error]: Message: ${message}, Extensions: ${JSON.stringify( - extensions - )}` - ) - ); - } else { - console.error(`[Network error]: ${error}`); - } -}); +```ts +constructor( + errorHandler: ErrorLink.ErrorHandler +): ErrorLink ``` -This function is called after the GraphQL operation completes and execution is moving back _up_ your [link chain](./introduction/#handling-a-response). The function should not return a value unless you want to [retry the operation](../../data/error-handling#retrying-operations). - -For more information on the types of errors that might be encountered, see the guide on [error handling](../../data/error-handling). - -## Options +## Types -The function you provide to the `ErrorLink` constructor receives an object with the following fields: + We recommend reading [Apollo Link overview](./introduction/) before learning about individual links. + -`HttpLink` is a terminating link that sends a GraphQL operation to a remote endpoint over HTTP. Apollo Client uses `HttpLink` by default when you provide the `uri` option to the `ApolloClient` constructor. +## Constructor signature -`HttpLink` supports both POST and GET requests, and you can configure HTTP options on a per-operation basis. You can use these options for authentication, persisted queries, dynamic URIs, and other granular updates. +{/* TODO: Remove the custom signature once documentation supports rendering constructor functions */} -## Usage - -Import the `HttpLink` class and initialize a link like so: - -```js -import { HttpLink } from "@apollo/client"; - -const link = new HttpLink({ - uri: "http://localhost:4000/graphql", - // Additional options -}); -``` - -## `HttpLink` constructor options - -The `HttpLink` constructor takes an options object that can include the fields below. Note that you can also override some of these options on a per-operation basis using the [operation context](#context-options). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name /
Type
Description
- -###### `uri` - -`String` or `Function` - - - -The URL of the GraphQL endpoint to send requests to. Can also be a function that accepts an `Operation` object and returns the string URL to use for that operation. - -The default value is `/graphql`. - -
- -###### `includeExtensions` - -`Boolean` - - - -If true, includes the `extensions` field in operations sent to your GraphQL endpoint. - -The default value is `false`. - -
- -###### `fetch` - -`Function` - - - -A function to use instead of calling the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) directly when sending HTTP requests to your GraphQL endpoint. The function must conform to the signature of `fetch`. - -By default, the Fetch API is used unless it isn't available in your runtime environment. - -See [Customizing `fetch`](#customizing-fetch). - -
- -###### `headers` - -`Object` - - - -An object representing headers to include in every HTTP request, such as `{Authorization: 'Bearer abc123'}`. - -
- -###### `preserveHeaderCase` - -`Boolean` - - - -If set to true, header names won't be automatically normalized to lowercase. This allows for non-http-spec-compliant servers that might expect capitalized header names. - -The default value is `false`. - -
- -###### `credentials` - -`String` - - - -The credentials policy to use for each `fetch` call. Can be `omit`, `include`, or `same-origin`. - -
- -###### `fetchOptions` - -`Object` - - - -An object containing options to use for each call to `fetch`. If a particular option is not included in this object, the default value of that option is used. - -Note that if you set `fetchOptions.method` to `GET`, `HttpLink` follows [standard GraphQL HTTP GET encoding](http://graphql.org/learn/serving-over-http/#get-request). - -[See available options](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) - -
- -###### `useGETForQueries` - -`Boolean` - - - -If `true`, the link uses an HTTP GET request when sending query operations to your GraphQL endpoint. Mutation operations continue to use `POST` requests. If you want _all_ operations to use `GET` requests, set [`fetchOptions.method`](#fetchoptions) instead. - -The default value is `false`. - -
- -###### `print` - -`Function` - - - -An optional function to use when transforming a query or mutation `DocumentNode` into a string. It accepts an `ASTNode` (typically a `DocumentNode`) and the original `print` function as arguments, and is expected to return a string. This option can be used with `stripIgnoredCharacters` to remove whitespace from queries. - -```js -import { stripIgnoredCharacters } from "graphql"; - -const httpLink = new HttpLink({ - uri: "/graphql", - print: (ast, originalPrint) => stripIgnoredCharacters(originalPrint(ast)), -}); +```ts +constructor( + options: HttpLink.Options & ClientAwarenessLink.Options = {} +): HttpLink ``` -By default the bare [GraphQL `print` function](https://graphql.org/graphql-js/language/#print) is used. - -
- -## Context options - -`HttpLink` checks the [current operation's `context`](./introduction/#managing-context) for certain values before sending its request to your GraphQL endpoint. Previous links in the link chain can set these values to customize the behavior of `HttpLink` for each operation. - -> Some of these values can also be provided as options to [the `HttpLink` constructor](#httplink-constructor-options). If a value is provided to both, the value in the `context` takes precedence. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name /
Type
Description
- -###### `uri` - -`String` or `Function` - - - -The URL of the GraphQL endpoint to send requests to. Can also be a function that accepts an `Operation` object and returns the string URL to use for that operation. - -The default value is `/graphql`. - -
- -###### `headers` - -`Object` - - - -An object representing headers to include in the HTTP request, such as `{Authorization: 'Bearer abc123'}`. - -
- -###### `credentials` - -`String` - - - -The credentials policy to use for this `fetch` call. Can be `omit`, `include`, or `same-origin`. - -
- -###### `fetchOptions` - -`Object` - - - -An object containing options to use for this call to `fetch`. If a particular option is not included in this object, the default value of that option is used. - -Note that if you set `fetchOptions.method` to `GET`, `HttpLink` follows [standard GraphQL HTTP GET encoding](http://graphql.org/learn/serving-over-http/#get-request). - -[See available options](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) - -
- -###### `http` - -`Object` - - - -An object that configures advanced `HttpLink` functionality, such as support for persisted queries. Options are listed in [`http` option fields](#http-option-fields). - -
- -### `http` option fields - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name /
Type
Description
- -###### `includeExtensions` - -`Boolean` - - - -If true, includes the `extensions` field in operations sent to your GraphQL endpoint. - -The default value is `false`. - -
- -###### `includeQuery` - -`Boolean` - - - -If `false`, the GraphQL query string is _not_ included in the request. Set this option if you're sending a request that uses a [persisted query](./persisted-queries/). - -The default value is `true`. - -
- -###### `preserveHeaderCase` - -`Boolean` - - - -If set to true, header names won't be automatically normalized to lowercase. This allows for non-http-spec-compliant servers that might expect capitalized header names. - -The default value is `false`. - -
- ## Operation results -After your GraphQL endpoint (successfully) responds with the result of the sent operation, `HttpLink` sets it as the `response` field of the operation `context`. This enables each previous link in your link chain to interact with the response before it's returned. - -## Handling errors - -`HttpLink` distinguishes between client errors, server errors, and GraphQL errors. You can add the [`onError` link](./apollo-link-error) to your link chain to handle these errors via a [callback](./apollo-link-error#options). - -The following types of errors can occur: - -| Error | Description | Callback | Error Type | -| -------------- | ---------------------------------------------------------------------------------------------------------------------- | :------: | ------------------ | -| Client Parse | The request body is not serializable, for example due to a circular reference. | `error` | `ClientParseError` | -| Server Parse | The server's response cannot be parsed ([response.json()](https://developer.mozilla.org/en-US/docs/Web/API/Body/json)) | `error` | `ServerParseError` | -| Server Network | The server responded with a non-2xx HTTP code. | `error` | `ServerError` | -| Server Data | The server's response didn't contain `data` or `errors`. | `error` | `ServerError` | -| GraphQL Error | Resolving the GraphQL operation resulted in at least one error, which is present in the `errors` field. | `next` | `Object` | - -Because many server implementations can return a valid GraphQL result on a server network error, the thrown `Error` object contains the parsed server result. A server data error also receives the parsed result. - -All error types inherit the `name`, `message`, and nullable `stack` properties from the generic javascript [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error): - -```js -//type ClientParseError -{ - parseError: Error; // Error returned from response.json() -}; - -//type ServerParseError -{ - response: Response; // Object returned from fetch() - statusCode: number; // HTTP status code - bodyText: string // text that was returned from server -}; - -//type ServerError -{ - result: Record; // Parsed object from server response - response: Response; // Object returned from fetch() - statusCode: number; // HTTP status code -}; -``` +After your GraphQL endpoint successfully responds with the result of an operation, `HttpLink` sets the `Response` object as the `response` field of the operation `context`. This enables each previous link in your link chain to interact with the response. ## Customizing `fetch` -You can provide the [`fetch` option](#fetch) to the `HttpLink` constructor to enable many custom networking needs. For example, you can modify the request based on calculated headers or calculate the endpoint URI based on the operation's details. - -If you're targeting an environment that doesn't provide the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) (such as older browsers or the server) you can provide a different implementation of `fetch`. We recommend [`unfetch`](https://github.com/developit/unfetch) for older browsers and [`node-fetch`](https://github.com/bitinn/node-fetch) for running in Node. - -### Custom auth - -This example adds a custom `Authorization` header to every request before calling `fetch`: - -```js -const customFetch = (uri, options) => { - const { header } = Hawk.client.header( - "http://example.com:8000/resource/1?b=1&a=2", - "POST", - { credentials: credentials, ext: "some-app-data" } - ); - options.headers.Authorization = header; - return fetch(uri, options); -}; - -const link = new HttpLink({ fetch: customFetch }); -``` - -### Dynamic URI - -This example customizes the endpoint URL's query parameters before calling `fetch`: - -```js -const customFetch = (uri, options) => { - const { operationName } = JSON.parse(options.body); - return fetch(`${uri}/graph/graphql?opname=${operationName}`, options); -}; - -const link = new HttpLink({ fetch: customFetch }); -``` +Provide a custom [`fetch` option](#options-fetch) to the `HttpLink` constructor if you need fine-grained control over the underlying `fetch` call. The function should behave identically to the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch). + +## Types + + + + + + + + diff --git a/docs/source/api/link/apollo-link-rest.mdx b/docs/source/api/link/apollo-link-rest.mdx deleted file mode 100644 index 8f5c317dddb..00000000000 --- a/docs/source/api/link/apollo-link-rest.mdx +++ /dev/null @@ -1,708 +0,0 @@ ---- -title: REST Link -description: Call REST APIs inside your GraphQL queries. ---- - -## Overview - -> The [Apollo Link Rest](https://github.com/apollographql/apollo-link-rest) library is maintained by Apollo community members and not an Apollo GraphQL maintained library. - -Calling REST APIs from a GraphQL client opens the benefits of GraphQL for more people, whether: - -- You are in a front-end developer team that wants to try GraphQL without asking for the backend team to implement a GraphQL server. -- You have no access to change the backend because it's an existing set of APIs, potentially managed by a 3rd party. -- You have an existing codebase, but you're looking to evaluate whether GraphQL can work for your needs. -- You have a large codebase, and the GraphQL migration is happening on the backend, but you want to use GraphQL _now_ without waiting! - -With `apollo-link-rest`, you can call your endpoints inside your GraphQL queries and have all your data managed by Apollo Client. `apollo-link-rest` is suitable for just dipping your toes in the water, or doing a full-steam ahead integration, and then later on migrating to a backend-driven GraphQL experience. - -> For more advanced or complex back-ends, you may want to consider using [`@apollo/server`](/apollo-server/). - -## Quick start - -To get started, first install Apollo Client and any `peerDependencies` we need: - -```bash -npm install --save @apollo/client apollo-link-rest graphql qs -``` - -After this, you're ready to setup the Apollo Client instance: - -```js -import { ApolloClient, InMemoryCache } from "@apollo/client"; -import { RestLink } from "apollo-link-rest"; - -// Set `RestLink` with your endpoint -const restLink = new RestLink({ uri: "https://swapi.dev/api/" }); - -// Setup your client -const client = new ApolloClient({ - cache: new InMemoryCache(), - link: restLink, -}); -``` - -Now it's time to write our first query: - -```js -import { gql } from "@apollo/client"; - -const query = gql` - query Luke { - person @rest(type: "Person", path: "people/1/") { - name - } - } -`; -``` - -You can then fetch your data using Apollo Client: - -```js -// Invoke the query and log the person's name -client.query({ query }).then((response) => { - console.log(response.data.person.name); -}); -``` - -## Options - -The `RestLink` constructor accepts an options object that can be used to customize the behavior of the link. Supported options are outlined below: - -| Option | Type | Description | -| ------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `uri` | `string` | The URI key is a string endpoint/domain for your requests to hit (_optional_ when `endpoints` provides a default) | -| `endpoints: /map-of-endpoints/` | `any` | _optional_ A map of endpoints. If you use this, you need to provide `endpoint` to the `@rest(...)` directives. | -| `customFetch?` | `any` | _optional_ A custom `fetch` to handle `REST` calls | -| `headers?` | `Headers` | _optional_ An object representing values to be sent as headers with all requests. [Documented here](https://developer.mozilla.org/en-US/docs/Web/API/Request/headers) | -| `credentials?` | `string` | _optional_ A string representing the credentials policy the fetch call should operate with. [Document here](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) | -| `fieldNameNormalizer?: /function/` | `any` | _optional_ A function that takes the response field name and converts it into a GraphQL compliant name. This is useful if your `REST` API returns fields that aren't representable as GraphQL, or if you want to convert between `snake_case` field names in JSON to `camelCase` keyed fields. | -| `fieldNameDenormalizer?: /function/` | `any` | _optional_ A function that takes a GraphQL-compliant field name and converts it back into an endpoint-specific name. | -| `typePatcher: /map-of-functions/` | `any` | _optional_ A structure to allow you to specify the `__typename` when you have nested objects in your REST response. | -| `defaultSerializer /function/` | `any` | _optional_ A function that will be used by the `RestLink` as the default serializer when no `bodySerializer` is defined for a `@rest` call. The function will also be passed the current `Header` set, which can be updated before the request is sent to `fetch`. Default method uses `JSON.stringify` and sets the `Content-Type` to `application/json`. | -| `bodySerializers: /map-of-functions/` | `any` | _optional_ Structure to allow the definition of alternative serializers, which can then be specified by their key. | -| `responseTransformer?: /function/` | `any` | _optional_ Apollo expects a record response to return a root object, and a collection of records response to return an array of objects. Use this function to structure the response into the format Apollo expects if your response data is structured differently. | - -### Multiple endpoints - -If you want to be able to use multiple endpoints, you can create your link like: - -```js -const link = new RestLink({ - endpoints: { v1: "api.com/v1", v2: "api.com/v2" }, -}); -``` - -You then need to specify the endpoint you want to use, in the rest directive: - -```js -const postTitleQuery1 = gql` - query PostTitle { - post @rest(type: "Post", path: "/post", endpoint: "v1") { - id - title - } - } -`; -const postTitleQuery2 = gql` - query PostTitle { - post @rest(type: "[Tag]", path: "/tags", endpoint: "v2") { - id - tags - } - } -`; -``` - -If you have a default endpoint, you can create your link like: - -```js -const link = new RestLink({ - endpoints: { github: "github.com" }, - uri: "api.com", -}); -``` - -If you don't specify an endpoint in your query, the default endpoint (the one you specify in the `uri` option) will be used. - -### Typename patching - -When sending a query like: - -```graphql -query MyQuery { - planets @rest(type: "PlanetPayload", path: "planets/") { - count - next - results { - name - } - } -} -``` - -the outer response object (`data.planets`) gets its `__typename: "PlanetPayload"` from the [`@rest(...)` directive's `type` parameter](#rest-directive). You, however, need to have a way to set the typename of `PlanetPayload.results`. - -One way you can do this is by providing a `typePatcher`: - -```typescript -const restLink = new RestLink({ - uri: "/api", - typePatcher: { - PlanetPayload: ( - data: any, - outerType: string, - patchDeeper: RestLink.FunctionalTypePatcher - ): any => { - if (data.results != null) { - data.results = data.results.map((planet) => ({ - __typename: "Planet", - ...planet, - })); - } - return data; - }, - // ... other nested type patchers - }, -}); -``` - -If you have a very lightweight REST integration, you can use the `@type(name: ...)` directive. - -```graphql -query MyQuery { - planets @rest(type: "PlanetPayload", path: "planets/") { - count - next - results @type(name: "Planet") { - name - } - } -} -``` - -This is appropriate if you have a small list of nested objects. The cost of this strategy is that every query that deals with these objects needs to also include `@type(name: ...)`, which means this approach can be quite verbose and error prone. - -You can also use both of these approaches in tandem: - -```graphql -query MyQuery { - planets @rest(type: "PlanetPayload", path: "planets/") { - count - next - results @type(name: "Results") { - name - } - typePatchedResults { - name - } - } -} -``` - -```typescript -const restLink = new RestLink({ - uri: '/api', - typePatcher: { - PlanetPayload: ( - data: any, - outerType: string, - patchDeeper: RestLink.FunctionalTypePatcher, - ): any => { - if (data.typePatchedResults != null) { - data.typePatchedResults = - data.typePatchedResults.map(planet => { __typename: "Planet", ...planet }); - } - return data; - }, - // ... other nested type patchers - }, -}) -``` - -#### Warning - -It's important to note that at the moment the `typePatcher` is not able to act on nested objects within annotated `@type` objects. For instance, `failingResults` will not be patched if you define it on the `typePatcher`: - -```graphql -query MyQuery { - planets @rest(type: "PlanetPayload", path: "planets/") { - count - next - results @type(name: "Planet") { - name - failingResults { - name - } - } - typePatchedResults { - name - } - } -} -``` - -To make this work you should try to pick one strategy, and stick with it -- either all `typePatcher` or all `@type` directives. - -### Response transforming - -By default, Apollo expects an object at the root for record requests, and an array of objects at the root for collection requests. For example, if fetching a user by ID (`/users/1`), the following response is expected. - -```json -{ - "id": 1, - "name": "Apollo" -} -``` - -And when fetching for a list of users (`/users`), the following response is expected. - -```json -[ - { - "id": 1, - "name": "Apollo" - }, - { - "id": 2, - "name": "Starman" - } -] -``` - -If the structure of your API responses differs than what Apollo expects, you can define a `responseTransformer` in the client. This function receives the response object as the 1st argument, and the current `typeName` as the 2nd argument. It should return a `Promise` as it will be responsible for reading the response stream by calling one of `json()`, `text()` etc. - -For example, if the record is not at the root level: - -```json -{ - "meta": {}, - "data": [ - { - "id": 1, - "name": "Apollo" - }, - { - "id": 2, - "name": "Starman" - } - ] -} -``` - -The following transformer could be used to support it: - -```js -const link = new RestLink({ - uri: "/api", - responseTransformer: async (response) => - response.json().then(({ data }) => data), -}); -``` - -Plaintext, XML, or otherwise-encoded responses can be handled by manually parsing and converting them to JSON (using the previously described format that Apollo expects): - -```js -const link = new RestLink({ - uri: "/xmlApi", - responseTransformer: async (response) => - response.text().then((text) => parseXmlResponseToJson(text)), -}); -``` - -### Custom endpoint responses - -The client level `responseTransformer` applies for all responses, across all URIs and endpoints. If you need a custom `responseTransformer` per endpoint, you can define an object of options for that specific endpoint. - -```js -const link = new RestLink({ - endpoints: { - v1: { - uri: "/v1", - responseTransformer: async (response) => response.data, - }, - v2: { - uri: "/v2", - responseTransformer: async (response, typeName) => response[typeName], - }, - }, -}); -``` - -> When using the object form, the `uri` field is required. - -### Custom Fetch - -By default, Apollo uses the browsers `fetch` method to handle `REST` requests to your domain/endpoint. The `customFetch` option allows you to specify _your own_ request handler by defining a function that returns a `Promise` with a fetch-response-like object: - -```js -const link = new RestLink({ - endpoints: "/api", - customFetch: (uri, options) => - new Promise((resolve, reject) => { - // Your own (asynchronous) request handler - resolve(responseObject); - }), -}); -``` - -To resolve your GraphQL queries quickly, Apollo will issue requests to relevant endpoints as soon as possible. This is generally ok, but can lead to large numbers of `REST` requests to be fired at once; especially for deeply nested queries [(see `@export` directive)](#export-directive). - -> Some endpoints (like public APIs) might enforce _rate limits_, leading to failed responses and unresolved queries in such cases. - -By example, `customFetch` is a good place to manage your apps fetch operations. The following implementation makes sure to only issue 2 requests at a time (concurrency) while waiting at least 500ms until the next batch of requests is fired. - -```js -import pThrottle from "p-throttle"; - -const link = new RestLink({ - endpoints: "/api", - customFetch: pThrottle( - (uri, config) => { - return fetch(uri, config); - }, - 2, // Max. concurrent Requests - 500 // Min. delay between calls - ), -}); -``` - -> Since Apollo issues `Promise` based requests, we can resolve them as we see fit. This example uses [`pThrottle`](https://github.com/sindresorhus/p-throttle); part of the popular [promise-fun](https://github.com/sindresorhus/promise-fun) collection. - -### Complete options - -Here is one way you might customize `RestLink`: - -```js -import fetch from 'cross-fetch'; -import * as camelCase from 'camelcase'; -import * as snake_case from 'snake-case'; - -const link = new RestLink({ - endpoints: { github: 'github.com' }, - uri: 'api.com', - customFetch: fetch, - headers: { - "Content-Type": "application/json" - }, - credentials: "same-origin", - fieldNameNormalizer: (key: string) => camelCase(key), - fieldNameDenormalizer: (key: string) => snake_case(key), - typePatcher: { - Post: ()=> { - bodySnippet... - } - }, - defaultSerializer: (data: any, headers: Headers) => { - const formData = new FormData(); - for (let key in data) { - formData.append(key, data[key]); - } - headers.set("Content-Type", "x-www-form-encoded") - return {data: formData, headers}; - } -}); -``` - -## Link Context - -`RestLink` has an [interface `LinkChainContext`](https://github.com/apollographql/apollo-link-rest/blob/1824da47d5db77a2259f770d9c9dd60054c4bb1c/src/restLink.ts#L557-L570) which it uses as the structure of things that it will look for in the `context`, as it decides how to fulfill a specific `RestLink` request. (Please see the [`@apollo/client/link/context`](./apollo-link-context) page for a discussion of why you might want this). - -| Option | Type | Description | -| --------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `credentials?` | `RequestCredentials` | Overrides the `RestLink`-level setting for `credentials`. [Values documented here](https://developer.mozilla.org/en-US/docs/Web/API/Request/headers) | -| `headers?` | `Headers` | Additional headers provided in this `context-link` [Values documented here](https://developer.mozilla.org/en-US/docs/Web/API/Request/headers) | -| `headersToOverride?` | `string[]` | If you provide this array, we will merge the headers you provide in this link, by replacing any matching headers that exist in the root `RestLink` configuration. Alternatively you can use `headersMergePolicy` for more fine-grained customization of the merging behavior. | -| `headersMergePolicy?` | `RestLink.HeadersMergePolicy` | This is a function that decide how the headers returned in this `contextLink` are merged with headers defined at the `RestLink`-level. If you don't provide this, the headers will be simply appended. To use this option, you can provide your own function that decides how to process the headers. [Code references](https://github.com/apollographql/apollo-link-rest/blob/8e57cabb5344209d9cfa391c1614fe8880efa5d9/src/restLink.ts#L462-L510) | -| `restResponses?` | `Response[]` | This will be populated after the operation has completed with the [Responses](https://developer.mozilla.org/en-US/docs/Web/API/Response) of every REST url fetched during the operation. This can be useful if you need to access the response headers to grab an authorization token for example. | - -### Example - -`RestLink` uses the `headers` field on the [`@apollo/client/link/context`](./apollo-link-context) so you can compose other links that provide additional & dynamic headers to a given query. - -Here is one way to add request `headers` to the context and retrieve the response headers of the operation: - -```js -const authRestLink = new ApolloLink((operation, forward) => { - operation.setContext(({ headers }) => { - const token = localStorage.getItem("token"); - return { - headers: { - ...headers, - Accept: "application/json", - Authorization: token, - }, - }; - }); - return forward(operation).map((result) => { - const { restResponses } = operation.getContext(); - const authTokenResponse = restResponses.find((res) => - res.headers.has("Authorization") - ); - // You might also filter on res.url to find the response of a specific API call - if (authTokenResponse) { - localStorage.setItem( - "token", - authTokenResponse.headers.get("Authorization") - ); - } - return result; - }); -}); - -const restLink = new RestLink({ uri: "uri" }); - -const client = new ApolloClient({ - cache: new InMemoryCache(), - link: ApolloLink.from([authRestLink, restLink]), -}); -``` - -## Link order - -If you are using multiple link types, `restLink` should go before `httpLink`, as `httpLink` will swallow any calls that should be routed through `apollo-link-rest`. - -For example: - -```js -const httpLink = createHttpLink({ uri: "server.com/graphql" }); -const restLink = new RestLink({ uri: "api.server.com" }); - -const client = new ApolloClient({ - cache: new InMemoryCache(), - link: ApolloLink.from([authLink, restLink, errorLink, retryLink, httpLink]), - // Note: httpLink is terminating so must be last, while retry & error wrap - // the links to their right. State & context links should happen before (to - // the left of) restLink. -}); -``` - -_Note: you should also consider this if you're using [`@apollo/client/link/context`](./apollo-link-context) to set `Headers`, you need that link to be before `restLink` as well._ - -## @rest directive - -This is where you setup the endpoint you want to fetch. The rest directive can be used at any depth in a query. - -### Arguments - -An `@rest(…)` directive takes two required and several optional arguments: - -| Option | Type | Description | -| --------------------------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `type` | `string` | The GraphQL type this will return | -| `path` | `string` | uri-path to the REST API. This could be a path or a full url. If a path, the endpoint given on link creation or from the context is concatenated with it to produce a full `URI`. See also: `pathBuilder` | -| `method?` | `GET` `PUT` `POST` `DELETE` | The HTTP method to send the request via (i.e GET, PUT, POST) | -| `endpoint?` | `string` | Key to use when looking up the endpoint in the (optional) `endpoints` table if provided to RestLink at creation time. | -| `pathBuilder?: /function/` | `string` | If provided, this function gets to control what path is produced for this request. | -| `bodyKey?: "input"` | `string` | This is the name of the `variable` to use when looking to build a REST request-body for a `PUT` or `POST` request. It defaults to `input` if not supplied. | -| `bodyBuilder?: /function/` | `string` | If provided, this is the name a `function` that you provided to `variables`, that is called when a request-body needs to be built. This lets you combine arguments or encode the body in some format other than JSON. | -| `bodySerializer?: /string \| function/` | `string` | String key to look up a function in `bodySerializers` or a custom serialization function for the body/headers of this request before it is passed to the fetch call. Defaults to `JSON.stringify` and setting `Content-Type: application-json`. | - -### Variables - -You can use query `variables` inside nested queries, or in the the path argument of your directive: - -```graphql -query PostTitle { - post(id: "1") @rest(type: "Post", path: "/post/{args.id}") { - id - title - } -} -``` - -> **Warning**: Variables in the main path will not automatically have `encodeURIComponent` called on them. - -Additionally, you can also control the query-string: - -```graphql -query PostTitle { - postSearch(query: "some key words", page_size: 5) - @rest(type: "Post", path: "/search?{args}&{context.language}") { - id - title - } -} -``` - -Things to note: - -1. This will be converted into `/search?query=some%20key%20words&page_size=5&lang=en` -2. The `context.language / lang=en` is extracting an object from the Apollo Context, that was added via an `@apollo/client/link/context` Link. -3. The query string arguments are assembled by npm:qs and have `encodeURIComponent` called on them. - -The available variable sources are: - -| Option | Description | -| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `args` | These are the things passed directly to this field parameters. In the above example `postSearch` had `query` and `page_size` in args. | -| `exportVariables` | These are the things in the parent context that were tagged as `@export(as: ...)` | -| `context` | These are the apollo-context, so you can have globals set up via `@apollo/client/link/context` | -| `@rest` | These include any other parameters you pass to the `@rest()` directive. This is probably more useful when working with `pathBuilder`, documented below. | - -#### `pathBuilder` - -If the variable-replacement options described above aren't enough, you can provide a `pathBuilder` to your query. This will be called to dynamically construct the path. This is considered an advanced feature, and is documented in the source -- it also should be considered syntactically unstable, and we're looking for feedback! - -#### `bodyKey` / `bodyBuilder` - -When making a `POST` or `PUT` HTTP request, you often need to provide a request body. By [convention](https://graphql.org/graphql-js/mutations-and-input-types/), GraphQL recommends you name your input-types as `input`, so by default that's where we'll look to find a JSON object for your body. - -##### `bodyKey` - -If you need/want to name it something different, you can pass `bodyKey`, and we'll look at that variable instead. - -In this example the publish API accepts a body in the variable `body` instead of input: - -```graphql -mutation PublishPost($someApiWithACustomBodyKey: PublishablePostInput!) { - publishedPost: publish(input: "Foo", body: $someApiWithACustomBodyKey) - @rest( - type: "Post" - path: "/posts/{args.input}/new" - method: "POST" - bodyKey: "body" - ) { - id - title - } -} -``` - -[Unit Test](https://github.com/apollographql/apollo-link-rest/blob/c9d81ae308e5f61b5ae992061de7abc6cb2f78e0/src/__tests__/restLink.ts#L1803-L1846) - -##### `bodyBuilder` - -If you need to structure your data differently, or you need to custom encode your body (say as form-encoded), you can provide `bodyBuilder` instead: - -```graphql -mutation EncryptedPost($input: PublishablePostInput!, $encryptor: any) { - publishedPost: publish(input: $input) - @rest( - type: "Post" - path: "/posts/new" - method: "POST" - bodyBuilder: $encryptor - ) { - id - title - } -} -``` - -[Unit Test](https://github.com/apollographql/apollo-link-rest/blob/c9d81ae308e5f61b5ae992061de7abc6cb2f78e0/src/__tests__/restLink.ts#L1847-L1904) - -##### `bodySerializer` - -If you need to serialize your data differently (say as form-encoded), you can provide a `bodySerializer` instead of relying on the default JSON serialization. -`bodySerializer` can be either a function of the form `(data: any, headers: Headers) => {body: any, header: Headers}` or a string key. When using the string key -`RestLink` will instead use the corresponding serializer from the `bodySerializers` object, that can optionally be passed in during initialization. - -```graphql -mutation EncryptedForm($input: PublishablePostInput!, $formSerializer: any) { - publishedPost: publish(input: $input) - @rest( - type: "Post" - path: "/posts/new" - method: "POST" - bodySerializer: $formSerializer - ) { - id - title - } - - publishRSS(input: $input) - @rest(type: "Post", path: "/feed", method: "POST", bodySerializer: "xml") -} -``` - -Where `formSerializer` could be defined as - -```typescript -const formSerializer = (data: any, headers: Headers) => { - const formData = new FormData(); - for (let key in data) { - if (data.hasOwnProperty(key)) { - formData.append(key, data[key]); - } - } - - headers.set("Content-Type", "application/x-www-form-urlencoded"); - - return { body: formData, headers }; -}; -``` - -And `"xml"` would have been defined on the `RestLink` directly - -```typescript -const restLink = new RestLink({ - ...otherOptions, - bodySerializers: { - xml: xmlSerializer, - }, -}); -``` - -## @export directive - -The export directive re-exposes a field for use in a later (nested) query. These are the same semantics that will be supported on the server, but when used in a `RestLink` you can use the exported variables for further calls (i.e. waterfall requests from nested fields). - -_Note: If you're constantly using @export you may prefer to take a look at [`@apollo/server`](/apollo-server/)._ - -### Arguments - -- `as: string`: name to create this as a variable to be used down the selection set - -### Example - -An example use-case would be getting a list of users, and hitting a different endpoint to fetch more data using the exported field in the REST query args. - -```graphql -const QUERY = gql` - query RestData($email: String!) { - users @rest(path: '/users/email?{args.email}', method: 'GET', type: 'User') { - id @export(as: "id") - firstName - lastName - friends @rest(path: '/friends/{exportVariables.id}', type: '[User]') { - firstName - lastName - } - } - } -`; -``` - -## Mutations - -You can write also mutations with the apollo-link-rest, for example: - -```graphql -mutation DeletePost($id: ID!) { - deletePostResponse(id: $id) - @rest(type: "Post", path: "/posts/{args.id}", method: "DELETE") { - NoResponse - } -} -``` - -## Troubleshooting - -Here are a few common `apollo-link-rest` problems and solutions. - -- `Missing field __typename in ...` -- If you see this, it's possible you haven't provided `type:` to the [`@rest(...)`](#rest-directive)-directive. Alternately you need to set up a [`typePatcher`](#typename-patching). -- `Headers is undefined` -- If you see something like this, you're running in a browser or other Javascript environment that does not yet support the full specification for the `Headers` API. - -## Example apps - -To get you started, here are some example apps: - -- [Simple](https://github.com/apollographql/apollo-link-rest/tree/master/examples/simple): - A very simple app with a single query that reflects the setup section. -- [Advanced](https://github.com/apollographql/apollo-link-rest/tree/master/examples/advanced): - A more complex app that demonstrates how to use an export directive. - -## Contributing - -Please join us on github [apollographql/apollo-link-rest](https://github.com/apollographql/apollo-link-rest/) and in the [Apollo GraphQL community forums](https://community.apollographql.com). diff --git a/docs/source/api/link/apollo-link-subscriptions.mdx b/docs/source/api/link/apollo-link-subscriptions.mdx index 61121023d1c..2f740ea870d 100644 --- a/docs/source/api/link/apollo-link-subscriptions.mdx +++ b/docs/source/api/link/apollo-link-subscriptions.mdx @@ -1,11 +1,9 @@ --- -title: Subscriptions Link +title: GraphQLWsLink description: Execute subscriptions (or other operations) over WebSocket with the graphql-ws library --- -> We recommend reading [Apollo Link overview](./introduction/) before learning about individual links. - -The `GraphQLWsLink` is a [terminating link](./introduction/#the-terminating-link) that's used most commonly with GraphQL [subscriptions](../../data/subscriptions/) (which usually communicate over WebSocket), although you can send queries and mutations over WebSocket as well. + `GraphQLWsLink` requires the [`graphql-ws`](https://www.npmjs.com/package/graphql-ws) library. Install it in your project like so: @@ -13,27 +11,33 @@ The `GraphQLWsLink` is a [terminating link](./introduction/#the-terminating-link npm install graphql-ws ``` -> **Note**: This link works with the newer `graphql-ws` library. If your server uses the older `subscriptions-transport-ws`, you should use the [`WebSocketLink` link from `@apollo/client/link/ws`](./apollo-link-ws) instead. + + +## Constructor signature -## Constructor +{/* TODO: Remove the custom signature once documentation supports rendering constructor functions */} ```js -import { GraphQLWsLink } from "@apollo/client/link/subscriptions"; -import { createClient } from "graphql-ws"; - -const link = new GraphQLWsLink( - createClient({ - url: "ws://localhost:3000/subscriptions", - }) -); +constructor( + client: Client +): GraphQLWsLink ``` ### Options -The `GraphQLWsLink` constructor takes a single argument, which is a `Client` returned from the `graphql-ws` `createClient` function. +The `GraphQLWsLink` constructor takes a single argument: a `Client` instance from the `graphql-ws` library. To create this instance, call the library's [`createClient`](https://the-guild.dev/graphql/ws/docs/client/functions/createClient) function. This function requires a `url` option, which is the URL to your WebSocket server. WebSocket URLs typically start with `ws://` or `wss://`. + +See the [`ClientOptions`](https://the-guild.dev/graphql/ws/docs/client/interfaces/ClientOptions) documentation for more details on the supported options provided to the `createClient` function. + +#### Retrying failed connections + +See the [`graphql-ws` recipes](https://the-guild.dev/graphql/ws/recipes) for strategies on retrying failed connections from the client. We generally recommend this approach over retrying failed connections from the link chain or your components because it provides more detailed information on why the connection failed. -The `createClient` function can take many options, described in the [`graphql-ws` docs for `ClientOptions`](https://the-guild.dev/graphql/ws/docs/client/interfaces/ClientOptions). The one required option is `url`, which is the URL (typically starting with `ws://` or `wss://`, which are the equivalents of `http://` and `https://` respectively) to your WebSocket server. (Note that this differs from the [older link's URL option](./apollo-link-ws), which is named `uri` instead of `url`.) +Alternatively, you can handle retries more generically within the link chain by using [`RetryLink`](./apollo-link-retry). This link resends the operation to the terminating link upon failure. ## Usage -See [Subscriptions](../../data/subscriptions/). +See [Subscriptions](../../data/subscriptions/) for more information on using subscription operations in Apollo Client. diff --git a/docs/source/api/link/apollo-link.mdx b/docs/source/api/link/apollo-link.mdx index b70e450a2fe..42534fac00b 100644 --- a/docs/source/api/link/apollo-link.mdx +++ b/docs/source/api/link/apollo-link.mdx @@ -10,19 +10,27 @@ description: API reference customOrder={["summary", "remarks", "example"]} /> +## Constructor signature + +```ts +constructor( + request?: ApolloLink.RequestHandler +): ApolloLink +``` + ## Static methods @@ -34,16 +42,16 @@ description: API reference /> diff --git a/src/link/batch-http/batchHttpLink.ts b/src/link/batch-http/batchHttpLink.ts index 42affb2233b..7b532803621 100644 --- a/src/link/batch-http/batchHttpLink.ts +++ b/src/link/batch-http/batchHttpLink.ts @@ -18,20 +18,53 @@ import { compact } from "@apollo/client/utilities/internal"; import { maybe } from "@apollo/client/utilities/internal/globals"; export declare namespace BatchHttpLink { - export type Options = Pick< - BatchLink.Options, - "batchMax" | "batchDebounce" | "batchInterval" | "batchKey" - > & - Omit; + /** + * Options provided to the `BatchHttpLink` constructor. + */ + export interface Options + extends BatchLink.Shared.Options, + HttpLink.Shared.Options { + /** {@inheritDoc @apollo/client/link/batch!BatchLink.Shared.Options#batchMax:member {"defaultValue": 10}} */ + batchMax?: number; + } - export type ContextOptions = HttpLink.ContextOptions; + /** + * Options passed to `BatchHttpLink` through [request context](https://apollographql.com/docs/react/api/link/introduction#managing-context). Previous + * non-terminating links in the link chain also can set these values to + * customize the behavior of `BatchHttpLink` for each operation. + * + * > [!NOTE] + * > Some of these values can also be provided to the `BatchHttpLink` constructor. + * > If a value is provided to both, the value in `context` takes precedence. + */ + export interface ContextOptions extends HttpLink.ContextOptions {} } const backupFetch = maybe(() => fetch); /** - * Transforms Operation for into HTTP results. - * context can include the headers property, which will be passed to the fetch function + * `BatchHttpLink` is a terminating link that batches array of individual + * GraphQL operations into a single HTTP request that's sent to a single GraphQL + * endpoint. It combines the functionality of `BaseBatchHttpLink` and + * `ClientAwarenessLink` into a single link. + * + * @remarks + * + * If you use `BatchHttpLink` instead of `HttpLink` as your terminating link, + * Apollo Client automatically batches executed GraphQL operations and transmits + * them to your server according to the batching options you provide. + * + * @example + * + * ```ts + * import { BatchHttpLink } from "@apollo/client/link/batch-http"; + * + * const link = new BatchHttpLink({ + * uri: "http://localhost:4000/graphql", + * batchMax: 5, // No more than 5 operations per batch + * batchInterval: 20, // Wait no more than 20ms after first batched operation + * }); + * ``` */ export class BatchHttpLink extends ApolloLink { constructor( diff --git a/src/link/batch/batchLink.ts b/src/link/batch/batchLink.ts index ff782d7617d..56a5f7046fa 100644 --- a/src/link/batch/batchLink.ts +++ b/src/link/batch/batchLink.ts @@ -6,42 +6,50 @@ import { ApolloLink } from "@apollo/client/link"; import { OperationBatcher } from "./batching.js"; export declare namespace BatchLink { + export namespace Shared { + /** These options are shared between `BatchLink` and `BatchHttpLink` */ + interface Options { + /** + * The interval at which to batch, in milliseconds. + * + * @defaultValue 10 + */ + batchInterval?: number; + + /** + * "batchInterval" is a throttling behavior by default, if you instead wish + * to debounce outbound requests, set "batchDebounce" to true. More useful + * for mutations than queries. + */ + batchDebounce?: boolean; + + /** + * The maximum number of operations to include in a single batch. + * + * @defaultValue \{\{defaultValue\}\} + */ + batchMax?: number; + + /** + * Creates the key for a batch + */ + batchKey?: (operation: ApolloLink.Operation) => string; + } + } + export type BatchHandler = ( operations: ApolloLink.Operation[], forward: ApolloLink.ForwardFunction[] ) => Observable; - export interface Options { - /** - * The interval at which to batch, in milliseconds. - * - * Defaults to 10. - */ - batchInterval?: number; - - /** - * "batchInterval" is a throttling behavior by default, if you instead wish - * to debounce outbound requests, set "batchDebounce" to true. More useful - * for mutations than queries. - */ - batchDebounce?: boolean; - - /** - * The maximum number of operations to include in one fetch. - * - * Defaults to 0 (infinite operations within the interval). - */ - batchMax?: number; - + export interface Options extends Shared.Options { /** * The handler that should execute a batch of operations. */ batchHandler?: BatchLink.BatchHandler; - /** - * creates the key for a batch - */ - batchKey?: (operation: ApolloLink.Operation) => string; + /** {@inheritDoc @apollo/client/link/batch!BatchLink.Shared.Options#batchMax:member {"defaultValue": 0}} */ + batchMax?: number; } } diff --git a/src/link/client-awareness/ClientAwarenessLink.ts b/src/link/client-awareness/ClientAwarenessLink.ts index e862d32a760..e5239cc7874 100644 --- a/src/link/client-awareness/ClientAwarenessLink.ts +++ b/src/link/client-awareness/ClientAwarenessLink.ts @@ -49,17 +49,50 @@ export declare namespace ClientAwarenessLink { * and Apollo Server logs (and other monitoring or analytics tools) by adding * information about the your application to outgoing requests. */ - clientAwareness?: ClientAwarenessOptions; + clientAwareness?: ClientAwarenessLink.ClientAwarenessOptions; /** * Configures the "enhanced client awareness" feature. * This feature allows you to identify the version of the Apollo Client library * used in your application in Apollo Studio (and other monitoring or analytics tools) * by adding information about the Apollo Client library to outgoing requests. */ - enhancedClientAwareness?: EnhancedClientAwarenessOptions; + enhancedClientAwareness?: ClientAwarenessLink.EnhancedClientAwarenessOptions; } } +/** + * `ClientAwarenessLink` provides support for providing client awareness + * features. + * + * @remarks + * + * Client awareness adds identifying information about the client to HTTP + * requests for use with metrics reporting tools, such as [Apollo GraphOS](https://apollographql.com/docs/graphos/platform). + * It is included in the functionality of [`HttpLink`](https://apollographql.com/docs/react/api/link/apollo-link-http) by default. + * + * Client awareness distinguishes between user-provided client awareness + * (provided by the `clientAwareness` option) and enhanced client awareness + * (provided by the `enhancedClientAwareness` option). User-provided client + * awareness enables you to set a customized client name and version for + * identification in metrics reporting tools. Enhanced client awareness enables + * the identification of the Apollo Client package name and version. + * + * @example + * + * ```ts + * import { ClientAwarenessLink } from "@apollo/client/link/client-awareness"; + * + * const link = new ClientAwarenessLink({ + * clientAwareness: { + * name: "My Client", + * version: "1", + * }, + * enhancedClientAwareness: { + * transport: "extensions", + * }, + * }); + * ``` + */ export class ClientAwarenessLink extends ApolloLink { constructor(constructorOptions: ClientAwarenessLink.Options = {}) { super((operation, forward) => { diff --git a/src/link/error/index.ts b/src/link/error/index.ts index 72a6232a018..624984611f5 100644 --- a/src/link/error/index.ts +++ b/src/link/error/index.ts @@ -11,13 +11,29 @@ import { import { ApolloLink } from "@apollo/client/link"; export declare namespace ErrorLink { - /** - * Callback to be triggered when an error occurs within the link stack. - */ + // Using a different namespace name to avoid clash with + // `ApolloLink.DocumentationTypes` + export namespace ErrorLinkDocumentationTypes { + /** + * Callback that is called by `ErrorLink` when an error occurs from a + * downstream link in link chain. + * + * @param options - The options object provided by `ErrorLink` to the error + * handler when an error occurs. + */ + export function ErrorHandler( + options: ErrorHandlerOptions + ): Observable | void; + } + + /** {@inheritDoc @apollo/client/link/error!ErrorLink.ErrorLinkDocumentationTypes.ErrorHandler:function(1)} */ export interface ErrorHandler { (options: ErrorHandlerOptions): Observable | void; } + /** + * The object provided to the `ErrorHandler` callback function. + */ export interface ErrorHandlerOptions { /** * The error that occurred during the operation execution. This can be a @@ -54,6 +70,50 @@ export function onError(errorHandler: ErrorLink.ErrorHandler) { return new ErrorLink(errorHandler); } +/** + * Use the `ErrorLink` to perform custom logic when a [GraphQL or network error](https://apollographql.com/docs/react/data/error-handling) + * occurs. + * + * @remarks + * + * This link is used after the GraphQL operation completes and execution is + * moving back up your [link chain](https://apollographql.com/docs/react/introduction#handling-a-response). The `errorHandler` function should + * not return a value unless you want to [retry the operation](https://apollographql.com/docs/react/data/error-handling#retrying-operations). + * + * For more information on the types of errors that might be encountered, see + * the guide on [error handling](https://apollographql.com/docs/react/data/error-handling). + * + * @example + * + * ```ts + * import { ErrorLink } from "@apollo/client/link/error"; + * import { + * CombinedGraphQLErrors, + * CombinedProtocolErrors, + * } from "@apollo/client/errors"; + * + * // Log any GraphQL errors, protocol errors, or network error that occurred + * const errorLink = new ErrorLink(({ error, operation }) => { + * if (CombinedGraphQLErrors.is(error)) { + * error.errors.forEach(({ message, locations, path }) => + * console.log( + * `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` + * ) + * ); + * } else if (CombinedProtocolErrors.is(error)) { + * error.errors.forEach(({ message, extensions }) => + * console.log( + * `[Protocol error]: Message: ${message}, Extensions: ${JSON.stringify( + * extensions + * )}` + * ) + * ); + * } else { + * console.error(`[Network error]: ${error}`); + * } + * }); + * ``` + */ export class ErrorLink extends ApolloLink { constructor(errorHandler: ErrorLink.ErrorHandler) { super((operation, forward) => { diff --git a/src/link/http/HttpLink.ts b/src/link/http/HttpLink.ts index 09a563e3781..2ac3504751f 100644 --- a/src/link/http/HttpLink.ts +++ b/src/link/http/HttpLink.ts @@ -8,36 +8,25 @@ import { BaseHttpLink } from "./BaseHttpLink.js"; export declare namespace HttpLink { /** - * Options passed to `HttpLink` through request context. + * Options passed to `HttpLink` through [request context](https://apollographql.com/docs/react/api/link/introduction#managing-context). Previous + * non-terminating links in the link chain also can set these values to + * customize the behavior of `HttpLink` for each operation. + * + * > [!NOTE] + * > Some of these values can also be provided to the `HttpLink` constructor. + * > If a value is provided to both, the value in `context` takes precedence. */ interface ContextOptions { - /** - * The URL of the GraphQL endpoint to send requests to. Can also be a - * function that accepts an `Operation` object and returns the string URL to - * use for that operation. - */ - uri?: string | UriFunction; + /** {@inheritDoc @apollo/client/link/http!HttpLink.Shared.Options#uri:member} */ + uri?: string | HttpLink.UriFunction; - /** - * An object representing headers to include in the HTTP request, such as - * `{Authorization: 'Bearer abc123'}`. - */ + /** {@inheritDoc @apollo/client/link/http!HttpLink.Shared.Options#headers:member} */ headers?: Record; - /** - * The credentials policy to use for this fetch call. Can be `omit`, `include`, - * or `same-origin`. - */ + /** {@inheritDoc @apollo/client/link/http!HttpLink.Shared.Options#credentials:member} */ credentials?: RequestCredentials; - /** - * An object containing options to use for this call to `fetch`. If a - * particular option is not included in this object, the default value of - * that option is used. - * - * Note that if you set `fetchOptions.method` to `GET`, `HttpLink` follows - * [standard GraphQL HTTP GET encoding](http://graphql.org/learn/serving-over-http/#get-request). - */ + /** {@inheritDoc @apollo/client/link/http!HttpLink.Shared.Options#fetchOptions:member} */ fetchOptions?: RequestInit; /** @@ -48,16 +37,11 @@ export declare namespace HttpLink { } /** - * Options passed to `HttpLink` through the `http` constructor option - * or the `http` property of a request context. + * Options passed to `HttpLink` through the `http` property of a request + * context. */ export interface HttpOptions { - /** - * If `true`, includes the `extensions` field in operations sent to your - * GraphQL endpoint. - * - * @defaultValue true - */ + /** {@inheritDoc @apollo/client/link/http!HttpLink.Shared.Options#includeExtensions:member} */ includeExtensions?: boolean; /** @@ -68,19 +52,12 @@ export declare namespace HttpLink { */ includeQuery?: boolean; - /** - * If set to true, header names won't be automatically normalized to - * lowercase. This allows for non-http-spec-compliant servers that might - * expect capitalized header names. - * - * @defaultValue false - */ + /** {@inheritDoc @apollo/client/link/http!HttpLink.Shared.Options#preserveHeaderCase:member} */ preserveHeaderCase?: boolean; /** * A list of additional `accept` headers to include in the request, - * as defined in - * https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2 + * as defined in https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2 * * @example * @@ -91,73 +68,136 @@ export declare namespace HttpLink { accept?: string[]; } + export namespace Shared { + /** These options are shared between `HttpLink` and `BatchHttpLink` */ + export interface Options { + /** + * The URL of the GraphQL endpoint to send requests to. Can also be a + * function that accepts an `ApolloLink.Operation` object and returns the + * string URL to use for that operation. + * + * @defaultValue "/graphql" + */ + uri?: string | HttpLink.UriFunction; + + /** + * If `true`, includes the `extensions` field in operations sent to your + * GraphQL endpoint. + * + * @defaultValue true + */ + includeExtensions?: boolean; + + /** + * A function to use instead of calling the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) directly + * when sending HTTP requests to your GraphQL endpoint. The function must + * conform to the signature of `fetch`. + * + * By default, the Fetch API is used unless it isn't available in your + * runtime environment. + * + * See [Customizing `fetch`](https://apollographql.com/docs/react/api/link/introduction#customizing-fetch). + */ + fetch?: typeof fetch; + + /** + * An object representing headers to include in every HTTP request. + * + * @example + * + * ```json + * { + * "Authorization": "Bearer 1234" + * } + * ``` + */ + headers?: Record; + + /** + * If `true`, header names won't be automatically normalized to lowercase. + * This allows for non-http-spec-compliant servers that might expect + * capitalized header names. + * + * @defaultValue false + */ + preserveHeaderCase?: boolean; + + /** + * The credentials policy to use for each `fetch` call. + */ + credentials?: RequestCredentials; + + /** + * Any overrides of the fetch options argument to pass to the fetch call. + * + * An object containing options to use for each call to `fetch`. If a + * particular option is not included in this object, the default value of + * that option is used. + * + * > [!NOTE] + * > If you set `fetchOptions.method` to `GET`, `HttpLink` follows [standard + * > GraphQL HTTP GET encoding](http://graphql.org/learn/serving-over-http/#get-request). + * + * See [available options](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) + */ + fetchOptions?: RequestInit; + + /** + * If `true`, unused variables from the operation will not be stripped from + * the request and will instead be sent to the GraphQL endpoint. + * + * @remarks + * Unused variables are likely to trigger server-side validation errors, + * per https://spec.graphql.org/draft/#sec-All-Variables-Used. + * `includeUnusedVariables` can be useful if your server deviates + * from the GraphQL specification by not strictly enforcing that rule. + * + * @defaultValue false + */ + includeUnusedVariables?: boolean; + /** + * A function to use when transforming a GraphQL document into a string. It + * accepts an `ASTNode` (typically a `DocumentNode`) and the original `print` + * function as arguments, and is expected to return a string. This option + * enables you to, for example, use `stripIgnoredCharacters` to remove + * whitespace from queries. + * + * By default the [GraphQL `print` function](https://graphql.org/graphql-js/language/#print) is used. + * + * @example + * + * ```ts + * import { stripIgnoredCharacters } from "graphql"; + * + * const httpLink = new HttpLink({ + * uri: "/graphql", + * print: (ast, originalPrint) => stripIgnoredCharacters(originalPrint(ast)), + * }); + * ``` + */ + print?: HttpLink.Printer; + } + } + /** - * Options for the `HttpLink` constructor. + * Options provided to the `HttpLink` constructor. + * + * > [!NOTE] + * > Some of these options are also available to override in [request context](https://apollographql.com/docs/react/api/link/introduction#managing-context). + * > Context options override the options passed to the constructor. Treat + * > these options as default values that are used when the request context + * > does not override the value. */ - export interface Options { - /** - * The URI to use when fetching operations. - * - * Defaults to '/graphql'. - */ - uri?: string | UriFunction; - + interface Options extends Shared.Options { /** - * Passes the extensions field to your graphql server. + * If `true`, the link uses an HTTP `GET` request when sending query + * operations to your GraphQL endpoint. Mutation operations continue to use + * `POST` requests. If you want all operations to use `GET` requests, + * set `fetchOptions.method` instead. * - * Defaults to true. - */ - includeExtensions?: boolean; - - /** - * A `fetch`-compatible API to use when making requests. - */ - fetch?: typeof fetch; - - /** - * An object representing values to be sent as headers on the request. - */ - headers?: Record; - - /** - * If set to true, header names won't be automatically normalized to - * lowercase. This allows for non-http-spec-compliant servers that might - * expect capitalized header names. - */ - preserveHeaderCase?: boolean; - - /** - * The credentials policy you want to use for the fetch call. - */ - credentials?: RequestCredentials; - - /** - * Any overrides of the fetch options argument to pass to the fetch call. - */ - fetchOptions?: RequestInit; - - /** - * If set to true, use the HTTP GET method for query operations. Mutations - * will still use the method specified in fetchOptions.method (which defaults - * to POST). + * @defaultValue false */ useGETForQueries?: boolean; - - /** - * If set to true, the default behavior of stripping unused variables - * from the request will be disabled. - * - * Unused variables are likely to trigger server-side validation errors, - * per https://spec.graphql.org/draft/#sec-All-Variables-Used, but this - * includeUnusedVariables option can be useful if your server deviates - * from the GraphQL specification by not strictly enforcing that rule. - */ - includeUnusedVariables?: boolean; - /** - * A function to substitute for the default query print function. Can be - * used to apply changes to the results of the print function. - */ - print?: Printer; } interface Body { @@ -171,6 +211,28 @@ export declare namespace HttpLink { type UriFunction = (operation: ApolloLink.Operation) => string; } +/** + * `HttpLink` is a terminating link that sends a GraphQL operation to a remote + * endpoint over HTTP. It combines the functionality of `BaseHttpLink` and + * `ClientAwarenessLink` into a single link. + * + * @remarks + * + * `HttpLink` supports both POST and GET requests, and you can configure HTTP + * options on a per-operation basis. You can use these options for + * authentication, persisted queries, dynamic URIs, and other granular updates. + * + * @example + * + * ```ts + * import { HttpLink } from "@apollo/client"; + * + * const link = new HttpLink({ + * uri: "http://localhost:4000/graphql", + * // Additional options + * }); + * ``` + */ export class HttpLink extends ApolloLink { constructor(options: HttpLink.Options & ClientAwarenessLink.Options = {}) { const { left, right, request } = ApolloLink.from([ diff --git a/src/link/subscriptions/index.ts b/src/link/subscriptions/index.ts index 95a0403f485..7f6295117fe 100644 --- a/src/link/subscriptions/index.ts +++ b/src/link/subscriptions/index.ts @@ -47,6 +47,29 @@ function isLikeErrorEvent(err: unknown): err is Event { return isNonNullObject(err) && err.target?.readyState === WebSocket.CLOSED; } +/** + * The `GraphQLWsLink` is a terminating link sends GraphQL operations over a + * WebSocket connection using the [`graphql-ws`](https://www.npmjs.com/package/graphql-ws) library. It's used most + * commonly with GraphQL [subscriptions](https://apollographql.com/docs/react/data/subscriptions/), + * + * > [!NOTE] + * > This link works with the `graphql-ws` library. If your server uses + * > the deprecated `subscriptions-transport-ws` library, use the deprecated + * > [`WebSocketLink`](https://apollographql.com/docs/react/api/link/apollo-link-ws) link instead. + * + * @example + * + * ```ts + * import { GraphQLWsLink } from "@apollo/client/link/subscriptions"; + * import { createClient } from "graphql-ws"; + * + * const link = new GraphQLWsLink( + * createClient({ + * url: "ws://localhost:3000/subscriptions", + * }) + * ); + * ``` + */ export class GraphQLWsLink extends ApolloLink { constructor(public readonly client: Client) { super();