@urql/core
Version:
The shared core for the highly customizable and versatile GraphQL client
564 lines (546 loc) • 27.8 kB
TypeScript
import { AnyVariables, TypedDocumentNode, DocumentNode, GraphQLError, Exchange, FetchBody, Operation, ExecutionResult, DefinitionNode, DocumentInput, RequestExtensions, GraphQLRequest, OperationResult, FormattedNode, OperationType, OperationContext, CombinedError } from './urql-core-chunk.js';
export { CacheOutcome, Client, ClientOptions, DebugEvent, DebugEventArg, DebugEventTypes, ErrorLike, ExchangeIO, ExchangeInput, GraphQLRequestParams, IncrementalPayload, OperationDebugMeta, OperationInstance, OperationResultSource, PersistedDocument, PersistedRequestExtensions, RequestPolicy, createClient } from './urql-core-chunk.js';
/** A GraphQL parse function, which may be called as a tagged template literal, returning a parsed {@link DocumentNode}.
*
* @remarks
* The `gql` tag or function is used to parse a GraphQL query document into a {@link DocumentNode}.
*
* When used as a tagged template, `gql` will automatically merge fragment definitions into the resulting
* document and deduplicate them.
*
* It enforces that all fragments have a unique name. When fragments with different definitions share a name,
* it will log a warning in development.
*
* Hint: It’s recommended to use this `gql` function over other GraphQL parse functions, since it puts the parsed
* results directly into `@urql/core`’s internal caches and prevents further unnecessary work.
*
* @example
* ```ts
* const AuthorFragment = gql`
* fragment AuthorDisplayComponent on Author {
* id
* name
* }
* `;
*
* const BookFragment = gql`
* fragment ListBookComponent on Book {
* id
* title
* author {
* ...AuthorDisplayComponent
* }
* }
*
* ${AuthorFragment}
* `;
*
* const BookQuery = gql`
* query Book($id: ID!) {
* book(id: $id) {
* ...BookFragment
* }
* }
*
* ${BookFragment}
* `;
* ```
*/
declare function gql<Data = any, Variables extends AnyVariables = AnyVariables>(strings: TemplateStringsArray, ...interpolations: Array<TypedDocumentNode | DocumentNode | string>): TypedDocumentNode<Data, Variables>;
declare function gql<Data = any, Variables extends AnyVariables = AnyVariables>(string: string): TypedDocumentNode<Data, Variables>;
/** A serialized version of an {@link OperationResult}.
*
* @remarks
* All properties are serialized separately as JSON strings, except for the
* {@link CombinedError} to speed up JS parsing speed, even if a result doesn’t
* end up being used.
*
* @internal
*/
interface SerializedResult {
hasNext?: boolean;
/** JSON-serialized version of {@link OperationResult.data}. */
data?: string | undefined;
/** JSON-serialized version of {@link OperationResult.extensions}. */
extensions?: string | undefined;
/** JSON version of {@link CombinedError}. */
error?: {
graphQLErrors: Array<Partial<GraphQLError> | string>;
networkError?: string;
};
}
/** A dictionary of {@link Operation.key} keys to serializable {@link SerializedResult} objects.
*
* @remarks
* It’s not recommended to modify the serialized data manually, however, multiple payloads of
* this dictionary may safely be merged and combined.
*/
interface SSRData {
[key: string]: SerializedResult;
}
/** Options for the `ssrExchange` allowing it to either operate on the server- or client-side. */
interface SSRExchangeParams {
/** Indicates to the {@link SSRExchange} whether it's currently in server-side or client-side mode.
*
* @remarks
* Depending on this option, the {@link SSRExchange} will either capture or replay results.
* When `true`, it’s in client-side mode and results will be serialized. When `false`, it’ll
* use its deserialized data and replay results from it.
*/
isClient?: boolean;
/** May be used on the client-side to pass the {@link SSRExchange} serialized data from the server-side.
*
* @remarks
* Alternatively, {@link SSRExchange.restoreData} may be called to imperatively add serialized data to
* the exchange.
*
* Hint: This method also works on the server-side to add to the initial serialized data, which enables
* you to combine multiple {@link SSRExchange} results, as needed.
*/
initialState?: SSRData;
/** Forces a new API request to be sent in the background after replaying the deserialized result.
*
* @remarks
* Similarly to the `cache-and-network` {@link RequestPolicy}, this option tells the {@link SSRExchange}
* to send a new API request for the {@link Operation} after replaying a serialized result.
*
* Hint: This is useful when you're caching SSR results and need the client-side to update itself after
* rendering the initial serialized SSR results.
*/
staleWhileRevalidate?: boolean;
/** Forces {@link OperationResult.extensions} to be serialized alongside the rest of a result.
*
* @remarks
* Entries in the `extension` object of a GraphQL result are often non-standard metdata, and many
* APIs use it for data that changes between every request. As such, the {@link SSRExchange} will
* not serialize this data by default, unless this flag is set.
*/
includeExtensions?: boolean;
}
/** An `SSRExchange` either in server-side mode, serializing results, or client-side mode, deserializing and replaying results..
*
* @remarks
* This same {@link Exchange} is used in your code both for the client-side and server-side as it’s “universal”
* and can be put into either client-side or server-side mode using the {@link SSRExchangeParams.isClient} flag.
*
* In server-side mode, the `ssrExchange` will “record” results it sees from your API and provide them for you
* to send to the client-side using the {@link SSRExchange.extractData} method.
*
* In client-side mode, the `ssrExchange` will use these serialized results, rehydrated either using
* {@link SSRExchange.restoreData} or {@link SSRexchangeParams.initialState}, to replay results the
* server-side has seen and sent before.
*
* Each serialized result will only be replayed once, as it’s assumed that your cache exchange will have the
* results cached afterwards.
*/
interface SSRExchange extends Exchange {
/** Client-side method to add serialized results to the {@link SSRExchange}.
* @param data - {@link SSRData},
*/
restoreData(data: SSRData): void;
/** Server-side method to get all serialized results the {@link SSRExchange} has captured.
* @returns an {@link SSRData} dictionary.
*/
extractData(): SSRData;
}
/** Creates a server-side rendering `Exchange` that either captures responses on the server-side or replays them on the client-side.
*
* @param params - An {@link SSRExchangeParams} configuration object.
* @returns the created {@link SSRExchange}
*
* @remarks
* When dealing with server-side rendering, we essentially have two {@link Client | Clients} making requests,
* the server-side client, and the client-side one. The `ssrExchange` helps implementing a tiny cache on both
* sides that:
*
* - captures results on the server-side which it can serialize,
* - replays results on the client-side that it deserialized from the server-side.
*
* Hint: The `ssrExchange` is basically an exchange that acts like a replacement for any fetch exchange
* temporarily. As such, you should place it after your cache exchange but in front of any fetch exchange.
*/
declare const ssrExchange: (params?: SSRExchangeParams) => SSRExchange;
/** Default document cache exchange.
*
* @remarks
* The default document cache in `urql` avoids sending the same GraphQL request
* multiple times by caching it using the {@link Operation.key}. It will invalidate
* query results automatically whenever it sees a mutation responses with matching
* `__typename`s in their responses.
*
* The document cache will get the introspected `__typename` fields by modifying
* your GraphQL operation documents using the {@link formatDocument} utility.
*
* This automatic invalidation strategy can fail if your query or mutation don’t
* contain matching typenames, for instance, because the query contained an
* empty list.
* You can manually add hints for this exchange by specifying a list of
* {@link OperationContext.additionalTypenames} for queries and mutations that
* should invalidate one another.
*
* @see {@link https://urql.dev/goto/docs/basics/document-caching} for more information on this cache.
*/
declare const cacheExchange: Exchange;
/** An abstract observer-like interface.
*
* @remarks
* Observer-like interfaces are passed to {@link ObservableLike.subscribe} to provide them
* with callbacks for their events.
*
* @see {@link https://github.com/tc39/proposal-observable} for the full TC39 Observable proposal.
*/
interface ObserverLike<T> {
/** Callback for values an {@link ObservableLike} emits. */
next: (value: T) => void;
/** Callback for an error an {@link ObservableLike} emits, which ends the subscription. */
error: (err: any) => void;
/** Callback for the completion of an {@link ObservableLike}, which ends the subscription. */
complete: () => void;
}
/** An abstract observable-like interface.
*
* @remarks
* Observable, or Observable-like interfaces, are often used by GraphQL transports to abstract
* how they send {@link ExecutionResult | ExecutionResults} to consumers. These generally contain
* a `subscribe` method accepting an {@link ObserverLike} structure.
*
* @see {@link https://github.com/tc39/proposal-observable} for the full TC39 Observable proposal.
*/
interface ObservableLike<T> {
/** Start the Observable-like subscription and returns a subscription handle.
*
* @param observer - an {@link ObserverLike} object with result, error, and completion callbacks.
* @returns a subscription handle providing an `unsubscribe` method to stop the subscription.
*/
subscribe(observer: ObserverLike<T>): {
unsubscribe: () => void;
};
}
/** A more cross-compatible version of the {@link GraphQLRequest} structure.
* {@link FetchBody} for more details
*/
type SubscriptionOperation = FetchBody;
/** A subscription forwarding function, which must accept a {@link SubscriptionOperation}.
*
* @param operation - A {@link SubscriptionOperation}
* @returns An {@link ObservableLike} object issuing {@link ExecutionResult | ExecutionResults}.
*/
type SubscriptionForwarder = (request: FetchBody, operation: Operation) => ObservableLike<ExecutionResult>;
/** This is called to create a subscription and needs to be hooked up to a transport client. */
interface SubscriptionExchangeOpts {
/** A subscription forwarding function, which must accept a {@link SubscriptionOperation}.
*
* @param operation - A {@link SubscriptionOperation}
* @returns An {@link ObservableLike} object issuing {@link ExecutionResult | ExecutionResults}.
*
* @remarks
* This callback is called for each {@link Operation} that this `subscriptionExchange` will
* handle. It receives the {@link SubscriptionOperation}, which is a more compatible version
* of the raw {@link Operation} objects and must return an {@link ObservableLike} of results.
*/
forwardSubscription: SubscriptionForwarder;
/** Flag to enable this exchange to handle all types of GraphQL operations.
*
* @remarks
* When you aren’t using fetch exchanges and GraphQL over HTTP as a transport for your GraphQL requests,
* or you have a third-party GraphQL transport implementation, which must also be used for queries and
* mutations, this flag may be used to allow this exchange to handle all kinds of GraphQL operations.
*
* By default, this flag is `false` and the exchange will only handle GraphQL subscription operations.
*/
enableAllOperations?: boolean;
/** A predicate function that causes an operation to be handled by this `subscriptionExchange` if `true` is returned.
*
* @param operation - an {@link Operation}
* @returns true when the operation is handled by this exchange.
*
* @remarks
* In some cases, a `subscriptionExchange` will be used to only handle some {@link Operation | Operations},
* e.g. all that contain `@live` directive. For these cases, this function may be passed to precisely
* determine which `Operation`s this exchange should handle, instead of forwarding.
*
* When specified, the {@link SubscriptionExchangeOpts.enableAllOperations} flag is disregarded.
*/
isSubscriptionOperation?: (operation: Operation) => boolean;
}
/** Generic subscription exchange factory used to either create an exchange handling just subscriptions or all operation kinds.
*
* @remarks
* `subscriptionExchange` can be used to create an {@link Exchange} that either
* handles just GraphQL subscription operations, or optionally all operations,
* when the {@link SubscriptionExchangeOpts.enableAllOperations} flag is passed.
*
* The {@link SubscriptionExchangeOpts.forwardSubscription} function must
* be provided and provides a generic input that's based on {@link Operation}
* but is compatible with many libraries implementing GraphQL request or
* subscription interfaces.
*/
declare const subscriptionExchange: ({ forwardSubscription, enableAllOperations, isSubscriptionOperation, }: SubscriptionExchangeOpts) => Exchange;
/** Simple log debugger exchange.
*
* @remarks
* An exchange that logs incoming {@link Operation | Operations} and
* {@link OperationResult | OperationResults} in development.
*
* This exchange is a no-op in production and often used in issue reporting
* to understand certain usage patterns of `urql` without having access to
* the original source code.
*
* Hint: When you report an issue you’re having with `urql`, adding
* this as your first exchange and posting its output can speed up
* issue triaging a lot!
*/
declare const debugExchange: Exchange;
/** Default GraphQL over HTTP fetch exchange.
*
* @remarks
* The default fetch exchange in `urql` supports sending GraphQL over HTTP
* requests, can optionally send GraphQL queries as GET requests, and
* handles incremental multipart responses.
*
* This exchange does not handle persisted queries or multipart uploads.
* Support for the former can be added using `@urql/exchange-persisted-fetch`
* and the latter using `@urql/exchange-multipart-fetch`.
*
* Hint: The `fetchExchange` and the two other exchanges all use the built-in fetch
* utilities in `@urql/core/internal`, which you can also use to implement
* a customized fetch exchange.
*
* @see {@link makeFetchSource} for the shared utility calling the Fetch API.
*/
declare const fetchExchange: Exchange;
/** Composes an array of Exchanges into a single one.
*
* @param exchanges - An array of {@link Exchange | Exchanges}.
* @returns - A composed {@link Exchange}.
*
* @remarks
* `composeExchanges` returns an {@link Exchange} that when instantiated
* composes the array of passed `Exchange`s into one, calling them from
* right to left, with the prior `Exchange`’s {@link ExchangeIO} function
* as the {@link ExchangeInput.forward} input.
*
* This simply merges all exchanges into one and is used by the {@link Client}
* to merge the `exchanges` option it receives.
*
* @throws
* In development, if {@link ExchangeInput.forward} is called repeatedly
* by an {@link Exchange} an error is thrown, since `forward()` must only
* be called once per `Exchange`.
*/
declare const composeExchanges: (exchanges: Exchange[]) => Exchange;
/** A cached printing function for GraphQL documents.
*
* @param node - A string of a document or a {@link DocumentNode}
* @returns A normalized printed string of the passed GraphQL document.
*
* @remarks
* This function accepts a GraphQL query string or {@link DocumentNode},
* then prints and sanitizes it. The sanitizer takes care of removing
* comments, which otherwise alter the key of the document although the
* document is otherwise equivalent to another.
*
* When a {@link DocumentNode} is passed to this function, it caches its
* output by modifying the `loc.source.body` property on the GraphQL node.
*/
declare const stringifyDocument: (node: string | DefinitionNode | DocumentNode) => string;
/** Creates a `GraphQLRequest` from the passed parameters.
*
* @param q - A string of a document or a {@link DocumentNode}
* @param variables - A variables object for the defined GraphQL operation.
* @returns A {@link GraphQLRequest}
*
* @remarks
* `createRequest` creates a {@link GraphQLRequest} from the passed parameters,
* while replacing the document as needed with a canonical version of itself,
* to avoid parsing, printing, or hashing the same input multiple times.
*
* If no variables are passed, canonically it'll default to an empty object,
* which is removed from the resulting hash key.
*/
declare const createRequest: <Data = any, Variables extends AnyVariables = AnyVariables>(_query: DocumentInput<Data, Variables>, _variables: Variables, extensions?: RequestExtensions | undefined) => GraphQLRequest<Data, Variables>;
/** Returns the name of the `DocumentNode`'s operation, if any.
* @param query - A {@link DocumentNode}
* @returns the operation's name contained within the document, or `undefined`
*/
declare const getOperationName: (query: DocumentNode) => string | undefined;
/** Converts the `ExecutionResult` received for a given `Operation` to an `OperationResult`.
*
* @param operation - The {@link Operation} for which the API’s result is for.
* @param result - The GraphQL API’s {@link ExecutionResult}.
* @param response - Optionally, a raw object representing the API’s result (Typically a {@link Response}).
* @returns An {@link OperationResult}.
*
* @remarks
* This utility can be used to create {@link OperationResult | OperationResults} in the shape
* that `urql` expects and defines, and should be used rather than creating the results manually.
*
* @throws
* If no data, or errors are contained within the result, or the result is instead an incremental
* response containing a `path` property, a “No Content” error is thrown.
*
* @see {@link ExecutionResult} for the type definition of GraphQL API results.
*/
declare const makeResult: (operation: Operation, result: ExecutionResult, response?: any) => OperationResult;
/** Merges an incrementally delivered `ExecutionResult` into a previous `OperationResult`.
*
* @param prevResult - The {@link OperationResult} that preceded this result.
* @param path - The GraphQL API’s {@link ExecutionResult} that should be patching the `prevResult`.
* @param response - Optionally, a raw object representing the API’s result (Typically a {@link Response}).
* @returns A new {@link OperationResult} patched with the incremental result.
*
* @remarks
* This utility should be used to merge subsequent {@link ExecutionResult | ExecutionResults} of
* incremental responses into a prior {@link OperationResult}.
*
* When directives like `@defer`, `@stream`, and `@live` are used, GraphQL may deliver new
* results that modify previous results. In these cases, it'll set a `path` property to modify
* the result it sent last. This utility is built to handle these cases and merge these payloads
* into existing {@link OperationResult | OperationResults}.
*
* @see {@link ExecutionResult} for the type definition of GraphQL API results.
*/
declare const mergeResultPatch: (prevResult: OperationResult, nextResult: ExecutionResult, response?: any, pending?: ExecutionResult["pending"]) => OperationResult;
/** Creates an `OperationResult` containing a network error for requests that encountered unexpected errors.
*
* @param operation - The {@link Operation} for which the API’s result is for.
* @param error - The network-like error that prevented an API result from being delivered.
* @param response - Optionally, a raw object representing the API’s result (Typically a {@link Response}).
* @returns An {@link OperationResult} containing only a {@link CombinedError}.
*
* @remarks
* This utility can be used to create {@link OperationResult | OperationResults} in the shape
* that `urql` expects and defines, and should be used rather than creating the results manually.
* This function should be used for when the {@link CombinedError.networkError} property is
* populated and no GraphQL execution actually occurred.
*/
declare const makeErrorResult: (operation: Operation, error: Error, response?: any) => OperationResult;
/** A stable stringifier for GraphQL variables objects.
*
* @param x - any JSON-like data.
* @return A JSON string.
*
* @remarks
* This utility creates a stable JSON string from any passed data,
* and protects itself from throwing.
*
* The JSON string is stable insofar as objects’ keys are sorted,
* and instances of non-plain objects are replaced with random keys
* replacing their values, which remain stable for the objects’
* instance.
*/
declare const stringifyVariables: (x: any, includeFiles?: boolean) => string;
/** Formats a GraphQL document to add `__typename` fields and process client-side directives.
*
* @param node - a {@link DocumentNode}.
* @returns a {@link FormattedDocument}
*
* @remarks
* Cache {@link Exchange | Exchanges} will require typename introspection to
* recognize types in a GraphQL response. To retrieve these typenames,
* this function is used to add the `__typename` fields to non-root
* selection sets of a GraphQL document.
*
* Additionally, this utility will process directives, filter out client-side
* directives starting with an `_` underscore, and place a `_directives` dictionary
* on selection nodes.
*
* This utility also preserves the internally computed key of the
* document as created by {@link createRequest} to avoid any
* formatting from being duplicated.
*
* @see {@link https://spec.graphql.org/October2021/#sec-Type-Name-Introspection} for more information
* on typename introspection via the `__typename` field.
*/
declare const formatDocument: <T extends TypedDocumentNode<any, any>>(node: T) => FormattedNode<T>;
/** Creates a {@link Operation} from the given parameters.
*
* @param kind - The {@link OperationType} of GraphQL operation, i.e. `query`, `mutation`, or `subscription`.
* @param request - The {@link GraphQLRequest} or {@link Operation} used as a template for the new `Operation`.
* @param context - The {@link OperationContext} `context` data for the `Operation`.
* @returns A new {@link Operation}.
*
* @remarks
* This method is both used to create new {@link Operation | Operations} as well as copy and modify existing
* operations. While it’s not required to use this function to copy an `Operation`, it is recommended, in case
* additional dynamic logic is added to them in the future.
*
* Hint: When an {@link Operation} is passed to the `request` argument, the `context` argument does not have to be
* a complete {@link OperationContext} and will instead be combined with passed {@link Operation.context}.
*
* @example
* An example of copying an existing `Operation` to modify its `context`:
*
* ```ts
* makeOperation(
* operation.kind,
* operation,
* { requestPolicy: 'cache-first' },
* );
* ```
*/
declare function makeOperation<Data = any, Variables extends AnyVariables = AnyVariables>(kind: OperationType, request: GraphQLRequest<Data, Variables>, context: OperationContext): Operation<Data, Variables>;
declare function makeOperation<Data = any, Variables extends AnyVariables = AnyVariables>(kind: OperationType, request: Operation<Data, Variables>, context?: Partial<OperationContext>): Operation<Data, Variables>;
/** Options for the `mapExchange` allowing it to react to incoming operations, results, or errors. */
interface MapExchangeOpts {
/** Accepts a callback for incoming `Operation`s.
*
* @param operation - An {@link Operation} that the {@link mapExchange} received.
* @returns optionally a new {@link Operation} replacing the original.
*
* @remarks
* You may return new {@link Operation | Operations} from this function replacing
* the original that the {@link mapExchange} received.
* It’s recommended that you use the {@link makeOperation} utility to create a copy
* of the original when you do this. (However, this isn’t required)
*
* Hint: The callback may also be promisified and return a new {@link Operation} asynchronously,
* provided you place your {@link mapExchange} after all synchronous {@link Exchange | Exchanges},
* like after your `cacheExchange`.
*/
onOperation?(operation: Operation): Promise<Operation> | Operation | void;
/** Accepts a callback for incoming `OperationResult`s.
*
* @param result - An {@link OperationResult} that the {@link mapExchange} received.
* @returns optionally a new {@link OperationResult} replacing the original.
*
* @remarks
* This callback may optionally return a new {@link OperationResult} that replaces the original,
* which you can use to modify incoming API results.
*
* Hint: The callback may also be promisified and return a new {@link Operation} asynchronously,
* provided you place your {@link mapExchange} after all synchronous {@link Exchange | Exchanges},
* like after your `cacheExchange`.
*/
onResult?(result: OperationResult): Promise<OperationResult> | OperationResult | void;
/** Accepts a callback for incoming `CombinedError`s.
*
* @param error - A {@link CombinedError} that an incoming {@link OperationResult} contained.
* @param operation - The {@link Operation} of the incoming {@link OperationResult}.
*
* @remarks
* The callback may also be promisified and return a new {@link Operation} asynchronously,
* provided you place your {@link mapExchange} after all synchronous {@link Exchange | Exchanges},
* like after your `cacheExchange`.
*/
onError?(error: CombinedError, operation: Operation): void;
}
/** Creates an `Exchange` mapping over incoming operations, results, and/or errors.
*
* @param opts - A {@link MapExchangeOpts} configuration object, containing the callbacks the `mapExchange` will use.
* @returns the created {@link Exchange}
*
* @remarks
* The `mapExchange` may be used to react to or modify incoming {@link Operation | Operations}
* and {@link OperationResult | OperationResults}. Optionally, it can also modify these
* asynchronously, when a promise is returned from the callbacks.
*
* This is useful to, for instance, add an authentication token to a given request, when
* the `@urql/exchange-auth` package would be overkill.
*
* It can also accept an `onError` callback, which can be used to react to incoming
* {@link CombinedError | CombinedErrors} on results, and trigger side-effects.
*
*/
declare const mapExchange: ({ onOperation, onResult, onError, }: MapExchangeOpts) => Exchange;
export { AnyVariables, CombinedError, DocumentInput, Exchange, ExecutionResult, FormattedNode, GraphQLRequest, MapExchangeOpts, Operation, OperationContext, OperationResult, OperationType, RequestExtensions, SSRData, SSRExchange, SSRExchangeParams, SerializedResult, SubscriptionExchangeOpts, SubscriptionForwarder, SubscriptionOperation, TypedDocumentNode, cacheExchange, composeExchanges, createRequest, debugExchange, mapExchange as errorExchange, fetchExchange, formatDocument, getOperationName, gql, makeErrorResult, makeOperation, makeResult, mapExchange, mergeResultPatch, ssrExchange, stringifyDocument, stringifyVariables, subscriptionExchange };