UNPKG

@apollo/client

Version:

A fully-featured caching GraphQL client.

152 lines 6.9 kB
import { Observable, throwError } from "rxjs"; import { ApolloLink } from "@apollo/client/link"; import { BatchLink } from "@apollo/client/link/batch"; import { checkFetcher, defaultPrinter, fallbackHttpConfig, parseAndCheckHttpResponse, selectHttpOptionsAndBodyInternal, selectURI, } from "@apollo/client/link/http"; import { filterOperationVariables } from "@apollo/client/link/utils"; import { __DEV__ } from "@apollo/client/utilities/environment"; import { compact } from "@apollo/client/utilities/internal"; import { maybe } from "@apollo/client/utilities/internal/globals"; const backupFetch = maybe(() => fetch); /** * `BaseBatchHttpLink` 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 serves as a base link to `BatchHttpLink`. * * @remarks * * > [!NOTE] * > Prefer using `BatchHttpLink` over `BaseBatchHttpLink`. Use * > `BaseBatchHttpLink` when you need to disable client awareness features and * > would like to tree-shake the implementation of `ClientAwarenessLink` out * > of your app bundle. * * @example * * ```ts * import { BaseBatchHttpLink } from "@apollo/client/link/batch-http"; * * const link = new BaseBatchHttpLink({ * 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 BaseBatchHttpLink extends ApolloLink { batchDebounce; batchInterval; batchMax; batcher; constructor(options = {}) { super(); let { uri = "/graphql", // use default global fetch if nothing is passed in fetch: preferredFetch, print = defaultPrinter, includeExtensions, preserveHeaderCase, batchInterval, batchDebounce, batchMax, batchKey, includeUnusedVariables = false, ...requestOptions } = options; if (__DEV__) { // Make sure at least one of preferredFetch, window.fetch, or backupFetch // is defined, so requests won't fail at runtime. checkFetcher(preferredFetch || backupFetch); } const linkConfig = { http: compact({ includeExtensions, preserveHeaderCase }), options: requestOptions.fetchOptions, credentials: requestOptions.credentials, headers: requestOptions.headers, }; this.batchDebounce = batchDebounce; this.batchInterval = batchInterval || 10; this.batchMax = batchMax || 10; const batchHandler = (operations) => { const chosenURI = selectURI(operations[0], uri); const context = operations[0].getContext(); const contextConfig = { http: context.http, options: context.fetchOptions, credentials: context.credentials, headers: context.headers, }; //uses fallback, link, and then context to build options const optsAndBody = operations.map((operation) => { const result = selectHttpOptionsAndBodyInternal(operation, print, fallbackHttpConfig, linkConfig, contextConfig); if (result.body.variables && !includeUnusedVariables) { result.body.variables = filterOperationVariables(result.body.variables, operation.query); } return result; }); const loadedBody = optsAndBody.map(({ body }) => body); const options = optsAndBody[0].options; // There's no spec for using GET with batches. if (options.method === "GET") { return throwError(() => new Error("apollo-link-batch-http does not support GET requests")); } try { options.body = JSON.stringify(loadedBody); } catch (parseError) { return throwError(() => parseError); } let controller; if (!options.signal && typeof AbortController !== "undefined") { controller = new AbortController(); options.signal = controller.signal; } return new Observable((observer) => { // Prefer BatchHttpLink.Options.fetch (preferredFetch) if provided, and // otherwise fall back to the *current* global window.fetch function // (see issue #7832), or (if all else fails) the backupFetch function we // saved when this module was first evaluated. This last option protects // against the removal of window.fetch, which is unlikely but not // impossible. const currentFetch = preferredFetch || maybe(() => fetch) || backupFetch; currentFetch(chosenURI, options) .then((response) => { // Make the raw response available in the context. operations.forEach((operation) => operation.setContext({ response })); return response; }) .then(parseAndCheckHttpResponse(operations)) .then((result) => { controller = undefined; // we have data and can send it to back up the link chain observer.next(result); observer.complete(); return result; }) .catch((err) => { controller = undefined; observer.error(err); }); return () => { // XXX support canceling this request // https://developers.google.com/web/updates/2017/09/abortable-fetch if (controller) controller.abort(); }; }); }; batchKey = batchKey || ((operation) => { const context = operation.getContext(); const contextConfig = { http: context.http, options: context.fetchOptions, credentials: context.credentials, headers: context.headers, }; //may throw error if config not serializable return selectURI(operation, uri) + JSON.stringify(contextConfig); }); this.batcher = new BatchLink({ batchDebounce: this.batchDebounce, batchInterval: this.batchInterval, batchMax: this.batchMax, batchKey, batchHandler, }); } request(operation, forward) { return this.batcher.request(operation, forward); } } //# sourceMappingURL=BaseBatchHttpLink.js.map