File: README.md
β¨ Features
- π¦ One package to get a working GraphQL client in React, Preact, Vue, Solid and Svelte
- βοΈ Fully customisable behaviour via "exchanges"
- π Logical but simple default behaviour and document caching
- π± Normalized caching via
@urql/exchange-graphcache - π¬ Easy debugging with the
urqldevtools browser extensions
urql is a GraphQL client that exposes a set of helpers for several frameworks. It's built to be highly customisable and versatile so
you can take it from getting started with your first GraphQL project all the way to building complex apps and experimenting with GraphQL clients.
π For more information, check out the docs.
π [Sponsors](https://github.com/sponsors/urql-graphql)
| BigCommerce | WunderGraph | The Guild |
|---|
| BeatGig |
|---|
π Contributing
The urql project was founded by Formidable and is actively developed
by the urql GraphQL team.
If you'd like to get involved, check out our Contributor's guide.
π¦ [Releases](https://github.com/urql-graphql/urql/releases)
All new releases and updates are listed on GitHub with full changelogs. Each package in this
repository further contains an independent CHANGELOG.md file with the historical changelog, for
instance, hereβs @urql/coreβs
changelog.
If youβre upgrading to v4, check out our migration guide, posted as an
issue.
New releases are prepared using
changesets,
which are changelog entries added to each PR, and we have βVersion Packagesβ PRs that once merged
will release new versions of urql packages. You can use @canary releases from npm if youβd
like to get a preview of the merged changes.
π [Documentation](https://urql.dev/goto/docs)
The documentation contains everything you need to know about urql, and contains several sections in order of importance
when you first get started:
- Basics βΒ contains the "Getting Started" guide and all you need to know when first using
urql. - Architecture β explains how
urqlfunctions and is built. - Advanced β covers more uncommon use-cases and things you don't immediately need when getting started.
- Graphcache βΒ documents "Normalized Caching" support which enables more complex apps and use-cases.
- API βΒ the API documentation for each individual package.
Furthermore, all APIs and packages are self-documented using TSDocs. If youβre using a language
server for TypeScript, the documentation for each API should pop up in your editor when hoveringurqlβs code and APIs.
You can find the raw markdown files inside this repository's docs folder.
File: docs/api/auth-exchange.md
title: '@urql/exchange-auth' order: 10
Authentication Exchange
Note: These API docs are deprecated as we now keep TSDocs in all published packages.
You can view TSDocs while using these packages in your editor, as long as it supports the
TypeScript Language Server.
We're planning to replace these API docs with a separate web app soon.
The @urql/exchange-auth package contains an addon authExchange for urql that aims to make it
easy to implement complex authentication and reauthentication flows as are typically found with JWT
token based API authentication.
Installation and Setup
First install @urql/exchange-auth alongside urql:
yarn add @urql/exchange-auth
# or
npm install --save @urql/exchange-authYou'll then need to add the authExchange, that this package exposes to your Client. TheauthExchange is an asynchronous exchange, so it must be placed in front of all fetchExchanges
but after all other synchronous exchanges, like the cacheExchange.
import { createClient, cacheExchange, fetchExchange } from 'urql';
import { authExchange } from '@urql/exchange-auth';
const client = createClient({
url: 'http://localhost:3000/graphql',
exchanges: [
cacheExchange,
authExchange(async utils => {
return {
/* config... */
};
}),
fetchExchange,
],
});The authExchange accepts an initialization function. This function is called when your exchange
and Client first start up, and must return an object of options wrapped in a Promise, which is
used to configure how your authentication method works.
You can use this function to first retrieve your authentication state from a kind
of local storage, or to call your API to validate your authentication state first.
The relevant configuration options, returned to the authExchange, then determine
how the authExchange behaves:
addAuthToOperationmust be provided to tellauthExchangehow to add authentication information
to an operation, e.g. how to add the authentication state to an operation's fetch headers.willAuthErrormay be provided to detect expired tokens or tell whether an operation will likely
fail due to an authentication error.didAuthErrormay be provided to let theauthExchangedetect authentication errors from the
API on results.refreshAuthis called when an authentication error occurs and gives you an opportunity to update
your authentication state. Afterwards, theauthExchangewill retry your operation.
Read more examples in the documentation given here.
File: docs/api/core.md
title: '@urql/core' order: 0
@urql/core
Note: These API docs are deprecated as we now keep TSDocs in all published packages.
You can view TSDocs while using these packages in your editor, as long as it supports the
TypeScript Language Server.
We're planning to replace these API docs with a separate web app soon.
The @urql/core package is the basis of all framework bindings. Each bindings-package,
like urql for React or @urql/preact, will reuse the core logic and
reexport all exports from @urql/core.
Therefore if you're not accessing utilities directly, aren't in a Node.js environment, and are using
framework bindings, you'll likely want to import from your framework bindings package directly.
Read more about urql's core on the "Core Package" page.
Client
The Client manages all operations and ongoing requests to the exchange pipeline.
It accepts several options on creation.
@urql/core also exposes createClient() that is just a convenient alternative to calling new Client().
| Input | Type | Description |
|---|---|---|
exchanges |
Exchange[] |
An array of Exchanges that the client should use |
url |
string |
The GraphQL API URL as used by fetchExchange |
fetchOptions |
RequestInit | () => RequestInit |
Additional fetchOptions that fetch in fetchExchange should use to make a request |
fetch |
typeof fetch |
An alternative implementation of fetch that will be used by the fetchExchange instead of window.fetch |
suspense |
?boolean |
Activates the experimental React suspense mode, which can be used during server-side rendering to prefetch data |
requestPolicy |
?RequestPolicy |
Changes the default request policy that will be used. By default, this will be cache-first. |
preferGetMethod |
?boolean | 'force' | 'within-url-limit' |
This is picked up by the fetchExchange and will force all queries (not mutations) to be sent using the HTTP GET method instead of POST if the length of the resulting URL doesn't exceed 2048 characters. When 'force' is passed a GET request is always sent regardless of how long the resulting URL is. |
client.executeQuery
Accepts a GraphQLRequest and optionally Partial<OperationContext>, and returns aSource<OperationResult> β a stream of query results that can be subscribed to.
Internally, subscribing to the returned source will create an Operation, withkind set to 'query', and dispatch it on the
exchanges pipeline. If no subscribers are listening to this operation anymore and unsubscribe from
the query sources, the Client will dispatch a "teardown" operation.
- Instead of using this method directly, you may want to use the
client.queryshortcut
instead. - See
createRequestfor a utility that createsGraphQLRequestobjects.
client.executeSubscription
This is functionally the same as client.executeQuery, but creates operations for subscriptions
instead, with kind set to 'subscription'.
client.executeMutation
This is functionally the same as client.executeQuery, but creates operations for mutations
instead, with kind set to 'mutation'.
A mutation source is always guaranteed to only respond with a single OperationResult and then complete.
client.query
This is a shorthand method for client.executeQuery, which accepts a query
(DocumentNode | string) and variables separately and creates a GraphQLRequest createRequest automatically.
The returned Source<OperationResult> will also have an added toPromise method, so the stream can
be conveniently converted to a promise.
import { pipe, subscribe } from 'wonka';
const { unsubscribe } = pipe(
client.query('{ test }', {
/* vars */
}),
subscribe(result => {
console.log(result); // OperationResult
})
);
// or with toPromise, which also limits this to one result
client
.query('{ test }', {
/* vars */
})
.toPromise()
.then(result => {
console.log(result); // OperationResult
});Read more about how to use this API on the "Core Package"
page.
client.mutation
This is similar to client.query, but dispatches mutations instead.
Read more about how to use this API on the "Core Package"
page.
client.subscription
This is similar to client.query, but does not provide a toPromise() helper method on the streams it returns.
Read more about how to use this API on the "Subscriptions" page.
client.reexecuteOperation
This method is commonly used in Exchanges to reexecute an Operation on theClient. It will only reexecute when there are still subscribers for the givenOperation.
For an example, this method is used by the cacheExchange when anOperationResult is invalidated in the cache and needs to be refetched.
client.readQuery
This method is typically used to read data synchronously from a cache. It returns an OperationResult if a value is returned immediately or null if no value is returned while cancelling all side effects.
CombinedError
The CombinedError is used in urql to normalize network errors and GraphQLErrors if anything
goes wrong during a GraphQL request.
| Input | Type | Description |
|---|---|---|
networkError |
?Error |
An unexpected error that might've occurred when trying to send the GraphQL request |
graphQLErrors |
?Array<string | GraphQLError> |
GraphQL Errors (if any) that were returned by the GraphQL API |
response |
?any |
The raw response object (if any) from the fetch call |
Read more about errors in urql on the "Error" page.
Types
GraphQLRequest
This often comes up as the input for every GraphQL request.
It consists of query and optionally variables.
| Prop | Type | Description |
|---|---|---|
key |
number |
A unique key that identifies this exact combination of query and variables, which is derived using a stable hash. |
query |
DocumentNode |
The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. |
variables |
?object |
The variables to be used with the GraphQL request. |
The key property is a hash of both the query and the variables, to uniquely
identify the request. When variables are passed it is ensured that they're stably stringified so
that the same variables in a different order will result in the same key, since variables are
order-independent in GraphQL.
A GraphQLRequest may be manually created using the createRequest helper.
OperationType
This determines what kind of operation the exchanges need to perform.
This is one of:
'subscription''query''mutation''teardown'
The 'teardown' operation is special in that it instructs exchanges to cancel
any ongoing operations with the same key as the 'teardown' operation that is
received.
Operation
The input for every exchange that informs GraphQL requests.
It extends the GraphQLRequest type and contains these additional properties:
| Prop | Type | Description |
|---|---|---|
kind |
OperationType |
The type of GraphQL operation being executed. |
context |
OperationContext |
Additional metadata passed to exchange. |
An Operation also contains the operationName property, which is a deprecated alias of the kind
property and outputs a deprecation warning if it's used.
RequestPolicy
This determines the strategy that a cache exchange should use to fulfill an operation.
When you implement a custom cache exchange it's recommended that these policies are
handled.
'cache-first'(default)'cache-only''network-only''cache-and-network'
Read more about request policies on the "Document Caching" page.
OperationContext
The context often carries options or metadata for individual exchanges, but may also contain custom
data that can be passed from almost all API methods in urql that deal withOperations.
Some of these options are set when the Client is initialised, so in the following list of
properties you'll likely see some options that exist on the Client as well.
| Prop | Type | Description |
|---|---|---|
fetchOptions |
?RequestInit | (() => RequestInit) |
Additional fetchOptions that fetch in fetchExchange should use to make a request. |
fetch |
typeof fetch |
An alternative implementation of fetch that will be used by the fetchExchange instead of window.fetch |
requestPolicy |
RequestPolicy |
An optional request policy that should be used specifying the cache strategy. |
url |
string |
The GraphQL endpoint, when using GET you should use absolute url's |
meta |
?OperationDebugMeta |
Metadata that is only available in development for devtools. |
suspense |
?boolean |
Whether suspense is enabled. |
preferGetMethod |
?boolean | 'force' | 'within-url-limit' |
Instructs the fetchExchange to use HTTP GET for queries. |
additionalTypenames |
?string[] |
Allows you to tell the operation that it depends on certain typenames (used in document-cache.) |
It also accepts additional, untyped parameters that can be used to send more
information to custom exchanges.
OperationResult
The result of every GraphQL request, i.e. an Operation. It's very similar to what comes back from
a typical GraphQL API, but slightly enriched and normalized.
| Prop | Type | Description |
|---|---|---|
operation |
Operation |
The operation that this is a result for |
data |
?any |
Data returned by the specified query |
error |
?CombinedError |
A CombinedError instances that wraps network or GraphQLErrors (if any) |
extensions |
?Record<string, any> |
Extensions that the GraphQL server may have returned. |
stale |
?boolean |
A flag that may be set to true by exchanges to indicate that the data is incomplete or out-of-date, and that the result will be updated soon. |
ExchangeInput
This is the input that an Exchange receives when it's initialized by theClient
| Input | Type | Description |
|---|---|---|
forward |
ExchangeIO |
The unction responsible for receiving an observable operation and returning a result |
client |
Client |
The urql application-wide client library. Each execute method starts a GraphQL request and returns a stream of results. |
Exchange
An exchange represents abstractions of small chunks of logic in urql.
They're small building blocks and similar to "middleware".
Read more about Exchanges on the "Authoring Exchanges" page.
An exchange is defined to be a function that receives ExchangeInput and returns
an ExchangeIO function. The ExchangeIO function in turn will receive a stream of operations, and
must return a stream of results. If the exchange is purely transforming data, like themapExchange for instance, it'll call forward, which is the next Exchange's ExchangeIO
function to get a stream of results.
type ExchangeIO = (Source<Operation>) => Source<OperationResult>;
type Exchange = ExchangeInput => ExchangeIO;If you haven't yet seen streams you can read more about "Stream Patterns" on the "Architecture"
page.
Exchanges
cacheExchange
The cacheExchange as described on the "Document Caching" page.. It's of type Exchange.
subscriptionExchange
The subscriptionExchange as described on the "Subscriptions" page.. It's of type Options => Exchange.
It accepts a single input: { forwardSubscription }. This is a function that
receives an enriched operation and must return an Observable-like object that
streams GraphQLResults with data and errors.
The forwardSubscription function is commonly connected to the subscriptions-transport-ws
package.
ssrExchange
The ssrExchange as described on the "Server-side Rendering"
page..
It's of type Options => Exchange.
It accepts three inputs, initialState which is completely
optional and populates the server-side rendered data with
a rehydrated cache, isClient which can be set totrue or false to tell the ssrExchange whether to
write to (server-side) or read from (client-side) the cache, andstaleWhileRevalidate which will treat rehydrated data as stale
and refetch up-to-date data by reexecuring the operation using a network-only requests policy.
By default, isClient defaults to true when the Client.suspense
mode is disabled and to false when the Client.suspense mode
is enabled.
This can be used to extract data that has been queried on
the server-side, which is also described in the Basics section,
and is also used on the client-side to restore server-side
rendered data.
When called, this function creates an Exchange, which also has
two methods on it:
.restoreData(data)which can be used to inject data, typically
on the client-side..extractData()which is typically used on the server-side to
extract the server-side rendered data.
Basically, the ssrExchange is a small cache that collects data
during the server-side rendering pass, and allows you to populate
the cache on the client-side with the same data.
During React rehydration this cache will be emptied, and it will
become inactive and won't change the results of queries after
rehydration.
It needs to be used after other caching Exchanges like thecacheExchange, but before any asynchronous Exchange like
the fetchExchange.
debugExchange
An exchange that writes incoming Operations to console.log and
writes completed OperationResults to console.log.
This exchange is disabled in production and is based on the mapExchange.
If you'd like to customise it, you can replace it with a custom mapExchange.
fetchExchange
The fetchExchange of type Exchange is responsible for sending operations of type 'query' and'mutation' to a GraphQL API using fetch.
mapExchange
The mapExchange allows you to:
- react to or replace operations with
onOperation, - react to or replace results with
onResult, - and; react to errors in results with
onError.
It can therefore be used to quickly react to the core events in the Client without writing a custom
exchange, effectively allowing you to ship your own debugExchange.
mapExchange({
onOperation(operation) {
console.log('operation', operation);
},
onResult(result) {
console.log('result', result);
},
});It can also be used to react only to errors, which is the same as checking for result.error:
mapExchange({
onError(error, operation) {
console.log(`The operation ${operation.key} has errored with:`, error);
},
});Lastly, it can be used to map operations and results, which may be useful to update theOperationContext or perform other standard tasks that require you to wait for a result:
import { mapExchange, makeOperation } from '@urql/core';
mapExchange({
async onOperation(operation) {
// NOTE: This is only for illustration purposes
return makeOperation(operation.kind, operation, {
...operation.context,
test: true,
});
},
async onResult(result) {
// NOTE: This is only for illustration purposes
if (result.data === undefined) result.data = null;
return result;
},
});errorExchange (deprecated)
An exchange that lets you inspect errors. This can be useful for logging, or reacting to
different types of errors (e.g. logging the user out in case of a permission error).
In newer versions of @urql/core, it's identical to the mapExchange and its export has been
replaced as the mapExchange also allows you to pass an onError function.
Utilities
gql
This is a gql tagged template literal function, similar to the one that's also commonly known fromgraphql-tag. It can be used to write GraphQL documents in a tagged template literal and returns a
parsed DocumentNode that's primed against the createRequest's cache for keys.
import { gql } from '@urql/core';
const SharedFragment = gql`
fragment UserFrag on User {
id
name
}
`;
gql`
query {
user
...UserFrag
}
${SharedFragment}
`;Unlike graphql-tag, this function outputs a warning in development when names of fragments in the
document are duplicated. It does not output warnings when fragment names were duplicated globally
however.
stringifyVariables
This function is a variation of JSON.stringify that sorts any object's keys that is being
stringified to ensure that two objects with a different order of keys will be stably stringified to
the same string.
stringifyVariables({ a: 1, b: 2 }); // {"a":1,"b":2}
stringifyVariables({ b: 2, a: 1 }); // {"a":1,"b":2}createRequest
This utility accepts a GraphQL query of type string | DocumentNode and optionally an object of
variables, and returns a GraphQLRequest object.
Since the client.executeQuery and other execute methods only acceptGraphQLRequests, this helper is commonly used to create that request first. Theclient.query and client.mutation methods use this helper as
well to create requests.
The helper takes care of creating a unique key for the GraphQLRequest. This is a hash of thequery and variables if they're passed. The variables will be stringified usingstringifyVariables, which outputs a stable JSON string.
Additionally, this utility will ensure that the query reference will remain stable. This means
that if the same query will be passed in as a string or as a fresh DocumentNode, then the output
will always have the same DocumentNode reference.
makeOperation
This utility is used to either turn a GraphQLRequest object into a newOperation object or to copy an Operation. It adds the kind property, and theoperationName alias that outputs a deprecation warning.
It accepts three arguments:
- An
Operation'skind(SeeOperationType - A
GraphQLRequestobject or anotherOperationthat should be
copied. - and; optionally a partial
OperationContextobject.. This argument may be
left out if the context is to be copied from the operation that may be passed as a second argument.
Hence some valid uses of the utility are:
// Create a new operation from scratch
makeOperation('query', createRequest(query, variables), client.createOperationContext(opts));
// Turn an operation into a 'teardown' operation
makeOperation('teardown', operation);
// Copy an existing operation while modifying its context
makeOperation(operation.kind, operation, {
...operation.context,
preferGetMethod: true,
});makeResult
This is a helper function that converts a GraphQL API result to anOperationResult.
It accepts an Operation, the API result, and optionally the original FetchResponse
for debugging as arguments, in that order.
makeErrorResult
This is a helper function that creates an OperationResult for GraphQL API
requests that failed with a generic or network error.
It accepts an Operation, the error, and optionally the original FetchResponse
for debugging as arguments, in that order.
formatDocument
This utility is used by the cacheExchange and by
Graphcache to add __typename fields to GraphQL DocumentNodes.
composeExchanges
This utility accepts an array of Exchanges and composes them into a single one.
It chains them in the order that they're given, left to right.
function composeExchanges(Exchange[]): Exchange;This can be used to combine some exchanges and is also used by Client
to handle the exchanges input.
File: docs/api/execute-exchange.md
title: '@urql/exchange-execute' order: 6
Execute Exchange
Note: These API docs are deprecated as we now keep TSDocs in all published packages.
You can view TSDocs while using these packages in your editor, as long as it supports the
TypeScript Language Server.
We're planning to replace these API docs with a separate web app soon.
The @urql/exchange-execute package contains an addon executeExchange for urql that may be used to
execute queries against a local schema. It is therefore a drop-in replacement for the default
fetchExchange and useful for the server-side, debugging, or testing.
Installation and Setup
First install @urql/exchange-execute alongside urql:
yarn add @urql/exchange-execute
# or
npm install --save @urql/exchange-executeYou'll then need to add the executeExchange, exposed by this package, to your Client.
It'll typically replace the fetchExchange or similar exchanges and must be used last if possible,
since it'll handle operations and return results.
import { createClient, cacheExchange } from 'urql';
import { executeExchange } from '@urql/exchange-execute';
const client = createClient({
url: 'http://localhost:3000/graphql',
exchanges: [
cacheExchange,
executeExchange({
/* config */
}),
],
});The executeExchange accepts an object of options, which are all similar to the arguments thatgraphql/execution/execute accepts. Typically you'd pass it the schema option, some resolvers
if your schema isn't already executable as fieldResolver / typeResolver / rootValue,
and a context value or function.
Options
| Option | Description |
|---|---|
schema |
This is of type GraphQLSchema and accepts either a schema that is or isn't executable. This field is required while all other fields are optional. |
rootValue |
The root value that graphql's execute will use when starting to execute the schema. |
fieldResolver |
A given field resolver function. Creating an executable schema may be easier than providing this, but this resolver will be passed on to execute as expected. |
typeResolver |
A given type resolver function. Creating an executable schema may be easier than providing this, but this resolver will be passed on to execute as expected. |
context |
This may either be a function that receives an Operation and returns the context value, or just a plain context value. Similarly to a GraphQL server this is useful as all resolvers will have access to your context |
File: docs/api/graphcache.md
title: '@urql/exchange-graphcache' order: 4
@urql/exchange-graphcache
Note: These API docs are deprecated as we now keep TSDocs in all published packages.
You can view TSDocs while using these packages in your editor, as long as it supports the
TypeScript Language Server.
We're planning to replace these API docs with a separate web app soon.
The @urql/exchange-graphcache package contains an addon cacheExchange for urql that may be
used to replace the default cacheExchange, which switches urql from
using "Document Caching" to "Normalized
Caching".
Read more about how to use and configure Graphcache in the "Graphcache"
section
cacheExchange
The cacheExchange function, as exported by @urql/exchange-graphcache, accepts a single object of
options and returns an Exchange.
| Input | Description |
|---|---|
keys |
A mapping of key generator functions for types that are used to override the default key generation that Graphcache uses to normalize data for given types. |
resolvers |
A nested mapping of resolvers, which are used to override the record or entity that Graphcache resolves for a given field for a type. |
directives |
A mapping of directives, which are functions accepting directive arguments and returning a resolver, which can be referenced by @localDirective or @_localDirective in queries. |
updates |
A nested mapping of updater functions for mutation and subscription fields, which may be used to add side-effects that update other parts of the cache when the given subscription or mutation field is written to the cache. |
optimistic |
A mapping of mutation fields to resolvers that may be used to provide Graphcache with an optimistic result for a given mutation field that should be applied to the cached data temporarily. |
schema |
A serialized GraphQL schema that is used by Graphcache to resolve partial data, interfaces, and enums. The schema also used to provide helpful warnings for schema awareness. |
storage |
A persisted storage interface that may be provided to preserve cache data for offline support. |
globalIDs |
A boolean or list of typenames that have globally unique ids, this changes how graphcache internally keys the entities. This can be useful for complex interface relationships. |
logger |
A function that will be invoked for warning/debug/... logs |
The @urql/exchange-graphcache package also exports the offlineExchange; which is identical to
the cacheExchange but activates offline support when the storage option is passed.
`keys` option
This is a mapping of typenames to KeyGenerator functions.
interface KeyingConfig {
[typename: string]: (data: Data) => null | string;
}It may be used to alter how Graphcache generates the key it uses for normalization for individual
types. The key generator function may also always return null when a type should always be
embedded.
Read more about how to set up keys in the "Key Generation" section of the "Normalized Caching"
page.
`resolvers` option
This configuration is a mapping of typenames to field names to Resolver functions.
A resolver may be defined to override the entity or record that a given field on a type should
resolve on the cache.
interface ResolverConfig {
[typeName: string]: {
[fieldName: string]: Resolver;
};
}A Resolver receives four arguments when it's called: parent, args, cache, andinfo.
| Argument | Type | Description |
|---|---|---|
parent |
Data |
The parent entity that the given field is on. |
args |
object |
The arguments for the given field the updater is executed on. |
cache |
Cache |
The cache using which data can be read or written. See Cache. |
info |
Info |
Additional metadata and information about the current operation and the current field. See Info. |
We can use the arguments it receives to either return new data based on just the arguments and other
cache information, but we may also read information about the parent and return new data for the
current field.
{
Todo: {
createdAt(parent, args, cache) {
// Read `createdAt` on the parent but return a Date instance
const date = cache.resolve(parent, 'createdAt');
return new Date(date);
}
}
}Read more about how to set up resolvers on the "Computed Queries"
page.
`updates` option
The updates configuration is a mapping of 'Mutation' | 'Subscription' to field names toUpdateResolver functions. An update resolver may be defined to add side-effects that run when a
given mutation field or subscription field is written to the cache. These side-effects are helpful
to update data in the cache that is implicitly changed on the GraphQL API, that Graphcache can't
know about automatically.
For mutation fields that don't have an updater, Graphcache has a fallback: if a returned entity
isn't currently found in the cache, it assumes a create-mutation and invalidates cached
entities of that type. This behavior was introduced in Graphcache v7 and is skipped once an updater
for the mutation field is added.
interface UpdatesConfig {
Mutation: {
[fieldName: string]: UpdateResolver;
};
Subscription: {
[fieldName: string]: UpdateResolver;
};
}An UpdateResolver receives four arguments when it's called: result, args, cache, andinfo.
| Argument | Type | Description |
|---|---|---|
result |
any |
Always the entire data object from the mutation or subscription. |
args |
object |
The arguments for the given field the updater is executed on. |
cache |
Cache |
The cache using which data can be read or written. See Cache. |
info |
Info |
Additional metadata and information about the current operation and the current field. See Info. |
It's possible to derive more information about the current update using the info argument. For
instance this metadata contains the current fieldName of the updater which may be used to make an
updater function more reusable, along with parentKey and other key fields. It also containsvariables and fragments which remain the same for the entire write operation, and additionally
it may have the error field set to describe whether the current field is null because the API
encountered a GraphQLError.
Read more about how to set up updates on the "Custom Updates"
page.
`optimistic` option
The optimistic configuration is a mapping of Mutation field names to OptimisticMutationResolver
functions, which return optimistic mutation results for given fields. These results are used by
Graphcache to optimistically update the cache data, which provides an immediate and temporary
change to its data before a mutation completes.
interface OptimisticMutationConfig {
[mutationFieldName: string]: OptimisticMutationResolver;
}A OptimisticMutationResolver receives three arguments when it's called: variables, cache, andinfo.
| Argument | Type | Description |
|---|---|---|
args |
object |
The arguments that the given mutation field received. |
cache |
Cache |
The cache using which data can be read or written. See Cache. |
info |
Info |
Additional metadata and information about the current operation and the current field. See Info. |
Read more about how to set up optimistic on the "Custom Updates"
page.
`schema` option
The schema option may be used to pass a IntrospectionQuery data to Graphcache, in other words
it's used to provide schema information to it. This schema is then used to resolve and return
partial results when querying, which are results that the cache can partially resolve as long as no
required fields are missing.
Read more about how to use the schema option on the "Schema Awareness"
page.
`storage` option
The storage option is an interface of methods that are used by the offlineExchange to persist
the cache's data to persisted storage on the user's device. it
NOTE: Offline Support is currently experimental! It hasn't been extensively tested yet and
may not always behave as expected. Please try it out with caution!
| Method | Type | Description |
|---|---|---|
writeData |
(delta: SerializedEntries) => Promise<void> |
This provided method must be able to accept an object of key-value entries that will be persisted to the storage. This method is called as a batch of updated entries becomes ready. |
readData |
() => Promise<SerializedEntries> |
This provided method must be able to return a single combined object of previous key-value entries that have been previously preserved using writeData. It's only called on startup. |
writeMetadata |
(json: SerializedRequest[]) => void |
This provided method must be able to persist metadata for the cache. For backwards compatibility it should be able to accept any JSON data. |
readMetadata |
() => Promise<null | SerializedRequest[]> |
This provided method must be able to read the persisted metadata that has previously been written using writeMetadata. It's only called on startup. |
onOnline |
(cb: () => void) => void |
This method must be able to accept a callback that is called when the user's device comes back online. |
onCacheHydrated |
() => void |
This method will be called when the cacheExchange has finished hydrating the data coming from storage. |
These options are split into three parts:
- The
writeMetadataandreadMetadatamethods are used to persist in-progress optimistic
mutations to a storage so that they may be retried if the app has been closed while some
optimistic mutations were still in progress. - The
writeDataandreadDatamethods are used to persist any cache data. This is the normalized
data that Graphcache usually keeps in memory. ThecacheExchangewill frequently callwriteDatawith a partial object of its cache data, whichreadDatamust then be able to return
in a single combined object on startup. We call the partial objects thatwriteDatais called
with "deltas". - The
onOnlinemethod is only used to receive a trigger that determines whether the user's device
has come back online, which is used to retry optimistic mutations that have previously failed due
to being offline.
The storage option may also be used with the cacheExchange instead of the offlineExchange, but
will then only use readData and writeData to persist its cache data. This is not full offline
support, but will rather be "persistence support".
Read more about how to use the storage option on the "Offline Support"
page.
Cache
An instance of the Cache interface is passed to every resolvers and updater function. It may be
used to read cached data or write cached data, which may be used in combination with thecacheExchange configuration to alter the default behaviour of Graphcache.
keyOfEntity
The cache.keyOfEntity method may be called with a partial Data object and will return the key
for that object, or null if it's not keyable.
An object may not be keyable if it's missing the __typename or id (which falls back to _id)
fields. This method does take the keys configuration into account.
cache.keyOfEntity({ __typename: 'Todo', id: 1 }); // 'Todo:1'
cache.keyOfEntity({ __typename: 'Query' }); // 'Query'
cache.keyOfEntity({ __typename: 'Unknown' }); // nullThere's an alternative method, cache.keyOfField which generates a key for a given field. This is
only rarely needed but similar to cache.keyOfEntity. This method accepts a field name and
optionally a field's arguments.
cache.keyOfField('todo'); // 'todo'
cache.keyOfField('todo', { id: 1 }); // 'todo({"id":1})'Internally, these are the keys that records and links are stored on per entity.
resolve
This method retrieves a value or link for a given field, given a partially keyable Data object or
entity, a field name, and optionally the field's arguments. Internally this method accesses the
cache by using cache.keyOfEntity and cache.keyOfField.
// This may resolve a link:
cache.resolve({ __typename: 'Query' }, 'todo', { id: 1 }); // 'Todo:1'
// This may also resolve records / scalar values:
cache.resolve({ __typename: 'Todo', id: 1 }, 'id'); // 1
// You can also chain multiple calls to `cache.resolve`!
cache.resolve(cache.resolve({ __typename: 'Query' }, 'todo', { id: 1 }), 'id'); // 1As you can see in the last example of this code snippet, the Data object can also be replaced by
an entity key, which makes it possible to pass a key from cache.keyOfEntity or another call tocache.resolve instead of the partial entity.
Note: Because
cache.resolvemay return either a scalar value or another entity key, it may
be dangerous to use in some cases. It's a good idea to make sure first whether the field you're
reading will be a key or a value.
The cache.resolve method may also be called with a field key as generated by cache.keyOfField.
cache.resolve({ __typename: 'Query' }, cache.keyOfField('todo', { id: 1 })); // 'Todo:1'This specialized case is likely only going to be useful in combination withcache.inspectFields.
inspectFields
The cache.inspectFields method may be used to interrogate the cache about all available fields on
a specific entity. It accepts a partial entity or an entity key, like cache.resolve's
first argument.
When calling the method this returns an array of FieldInfo objects, one per field (including
differing arguments) that is known to the cache. The FieldInfo interface has three properties:fieldKey, fieldName, and arguments:
| Argument | Type | Description |
|---|---|---|
fieldName |
string |
The field's name (without any arguments, just the name) |
arguments |
object | null |
The field's arguments, or null if the field doesn't have any arguments |
fieldKey |
string |
The field's cache key, which is similar to what cache.keyOfField would return |
This works on any given entity. When calling this method the cache works in reverse on its data
structure, by parsing the entity's individual field keys.
p
cache.inspectFields({ __typename: 'Query' });
/*
[
{ fieldName: 'todo', arguments: { id: 1 }, fieldKey: 'id({"id":1})' },
{ fieldName: 'todo', arguments: { id: 2 }, fieldKey: 'id({"id":2})' },
...
]
*/readFragment
cache.readFragment accepts a GraphQL DocumentNode as the first argument and a partial entity or
an entity key as the second, like cache.resolve's first argument.
The method will then attempt to read the entity according to the fragment entirely from the cached
data. If any data is uncached and missing it'll return null.
import { gql } from '@urql/core';
cache.readFragment(
gql`
fragment _ on Todo {
id
text
}
`,
{ id: 1 }
); // Data or nullNote that the __typename may be left out on the partial entity if the fragment isn't on an
interface or union type, since in that case the __typename is already present on the fragment
itself.
If any fields on the fragment require variables, you can pass them as the third argument like so:
import { gql } from '@urql/core';
cache.readFragment(
gql`
fragment _ on User {
id
permissions(byGroupId: $groupId)
}
`,
{ id: 1 }, // this identifies the fragment (User) entity
{ groupId: 5 } // any additional field variables
);If you need a specific fragment in a document containing multiple you can leverage
the fourth argument like this:
import { gql } from '@urql/core';
cache.readFragment(
gql`
fragment todoFields on Todo {
id
}
fragment userFields on User {
id
}
`,
{ id: 1 }, // this identifies the fragment (User) entity
undefined,
'userFields' // if not passed we take the first fragment, in this case todoFields
);[Read more about using readFragment on the "Local Resolvers"
page.
readQuery
The cache.readQuery method is similar to cache.readFragment, but instead of reading a fragment
from cache, it reads an entire query. The only difference between how these two methods are used iscache.readQuery's input, which is an object instead of two arguments.
The method accepts a { query, variables } object as the first argument, where query may either
be a DocumentNode or a string and variables may optionally be an object.
cache.readQuery({
query: `
query ($id: ID!) {
todo(id: $id) { id, text }
}
`,
variables: {
id: 1
}
); // Data or null[Read more about using readQuery on the "Local Resolvers"
page.
link
Corresponding to cache.resolve, the cache.link method allows
links in the cache to be updated. While the cache.resolve method reads both
records and links from the cache, the cache.link method will only ever write
links as fragments (See cache.writeFragment below) are more
suitable for updating scalar data in the cache.
The arguments for cache.link are identical to cache.resolve and
the field's arguments are optional. However, the last argument must always be
a link, meaning null, an entity key, a keyable entity, or a list of these.
In other words, cache.link accepts an entity to write to as its first argument,
with the same arguments as cache.keyOfEntity. It then accepts one or two arguments
that are passed to cache.keyOfField to get the targeted field key. And lastly,
you may pass a list or a single entity (or an entity key).
// Link Query.todo field to a todo item
cache.link({ __typename: 'Query' }, 'todo', { __typename: 'Todo', id: 1 });
// You may also pass arguments instead:
cache.link({ __typename: 'Query' }, 'todo', { id: 1 }, { __typename: 'Todo', id: 1 });
// Or use entity keys instead of the entities themselves:
cache.link('Query', 'todo', cache.keyOfEntity({ __typename: 'Todo', id: 1 }));The method may output a
warning when any of the
entities were passed as objects but aren't keyable, which is useful when a scalar or a non-keyable
object have been passed to cache.link accidentally.
writeFragment
Corresponding to cache.readFragment, the cache.writeFragment method allows
data in the cache to be updated.
The arguments for cache.writeFragment are identical to cache.readFragment,
however the second argument, data, should not only contain properties that are necessary to derive
an entity key from the given data, but also the fields that will be written:
import { gql } from '@urql/core';
cache.writeFragment(
gql`
fragment _ on Todo {
text
}
`,
{ id: 1, text: 'New Todo Text' }
);In the example we can see that the writeFragment method returns undefined. Furthermore we passid in our data object so that an entity key can be written, but the fragment itself doesn't have
to include these fields.
If you need a specific fragment in a document containing multiple you can leverage
the fourth argument like this:
import { gql } from '@urql/core';
cache.writeFragment(
gql`
fragment todoFields on Todo {
id
text
}
fragment userFields on User {
id
name
}
`,
{ id: 1, name: 'New Name' }
undefined,
'userFields' // if not passed we take the first fragment, in this case todoFields
);[Read more about using writeFragment on the "Custom Updates"
page.
updateQuery
Similarly to cache.writeFragment, there's an analogous method forcache.readQuery that may be used to update query data.
The cache.updateQuery method accepts the same { query, variables } object input as its first
argument, which is the query we'd like to write to the cache. As a second argument the method
accepts an updater function. This function will be called with the query data that is already in the
cache (which may be null if the data is uncached) and must return the new data that should be
written to the cache.
const TodoQuery = `
query ($id: ID!) {
todo(id: $id) { id, text }
}
`;
cache.updateQuery({ query: TodoQuery, variables: { id: 1 } }, data => {
if (!data) return null;
data.todo.text = 'New Todo Text';
return data;
});As we can see, our updater may return null to cancel updating any data, which we do in case the
query data is uncached.
We can also see that data can simply be mutated and doesn't have to be altered immutably. This is
because all data from the cache is already a deep copy and hence we can do to it whatever we want.
Read more about using updateQuery on the "Custom Updates"
page.
invalidate
The cache.invalidate method can be used to delete (i.e. "evict") an entity from the cache
entirely. This will cause it to disappear from all queries in Graphcache.
Its arguments are identical to cache.resolve.
Since deleting an entity will lead to some queries containing missing and uncached data, callinginvalidate may lead to additional GraphQL requests being sent, unless you're using Graphcache's
"Schema Awareness" feature, which takes optional fields into
account.
This method accepts a partial entity or an entity key as its first argument, similar tocache.resolve's first argument.
cache.invalidate({ __typename: 'Todo', id: 1 }); // Invalidates Todo:1Additionally cache.invalidate may be used to delete specific fields only, which can be useful when
for instance a list is supposed to be evicted from cache, where a full invalidation may be
impossible. This is often the case when a field on the root Query needs to be deleted.
This method therefore accepts two additional arguments, similar to cache.resolve.
// Invalidates `Query.todos` with the `first: 10` argument:
cache.invalidate('Query', 'todos', { first: 10 });Info
This is a metadata object that is passed to every resolver and updater function. It contains basic
information about the current GraphQL document and query, and also some information on the current
field that a given resolver or updater is called on.
| Argument | Type | Description |
|---|---|---|
parent |
Data |
The field's parent entity's data, as it was written or read up until now, which means it may be incomplete. Use cache.resolve to read from it. |
parentTypeName |
string |
The field's parent entity's typename |
parentKey |
string |
The field's parent entity's cache key (if any) |
parentFieldKey |
string |
The current key's cache key, which is the parent entity's key combined with the current field's key (This is mostly obsolete) |
fieldName |
string |
The current field's name |
fragments |
{ [name: string]: FragmentDefinitionNode } |
A dictionary of fragments from the current GraphQL document |
variables |
object |
The current GraphQL operation's variables (may be an empty object) |
error |
GraphQLError | undefined |
The current GraphQLError for a given field. This will always be undefined for resolvers and optimistic updaters, but may be present for updaters when the API has returned an error for a given field. |
partial |
?boolean |
This may be set to true at any point in time (by your custom resolver or by Graphcache) to indicate that some data is uncached and missing |
optimistic |
?boolean |
This is only true when an optimistic mutation update is running |
Note: Using
infois regarded as a last resort. Please only use information from it if
there's no other solution to get to the metadata you need. We don't regard theInfoAPI as
stable and may change it with a simple minor version bump.
The `/extras` import
The extras subpackage is published with Graphcache and contains helpers and utilities that don't
have to be included in every app or aren't needed by all users of Graphcache.
All utilities from extras may be imported from @urql/exchange-graphcache/extras.
Currently the extras subpackage only contains the pagination resolvers that have been mentioned
on the "Computed Queries" page.
simplePagination
Accepts a single object of optional options and returns a resolver that can be inserted into thecacheExchange's resolvers configuration.
| Argument | Type | Description |
|---|---|---|
offsetArgument |
?string |
The field arguments' property, as passed to the resolver, that contains the current offset, i.e. the number of items to be skipped. Defaults to 'skip'. |
limitArgument |
?string |
The field arguments' property, as passed to the resolver, that contains the current page size limit, i.e. the number of items on each page. Defaults to 'limit'. |
mergeMode |
'after' | 'before' |
This option defines whether pages are merged before or after preceding ones when paginating. Defaults to 'after'. |
Once set up, the resulting resolver is able to automatically concatenate all pages of a given field
automatically. Queries to this resolvers will from then on only return the infinite, combined list
of all pages.
Read more about simplePagination on the "Computed Queries"
page.
relayPagination
Accepts a single object of optional options and returns a resolver that can be inserted into thecacheExchange's resolvers configuration.
| Argument | Type | Description |
|---|---|---|
mergeMode |
'outwards' | 'inwards' |
With Relay pagination, pages can be queried forwards and backwards using after and before cursors. This option defines whether pages that have been queried backwards should be concatenated before (outwards) or after (inwards) all pages that have been queried forwards. |
Once set up, the resulting resolver is able to automatically concatenate all pages of a given field
automatically. Queries to this resolvers will from then on only return the infinite, combined list
of all pages.
Read more about relayPagnation on the "Computed Queries"
page.
The `/default-storage` import
The default-storage subpackage is published with Graphcache and contains a default storage
interface that may be used with the storage option.
It contains the makeDefaultStorage export which is a factory function that accepts a few options
and returns a full storage interface. This storage by default persists to
IndexedDB.
| Argument | Type | Description |
|---|---|---|
idbName |
string |
The name of the IndexedDB database that is used and created if needed. By default this is set to "graphcache-v3" |
maxAge |
number |
The maximum age of entries that the storage should use in whole days. By default the storage will discard entries that are older than seven days. |
File: docs/api/preact.md
title: '@urql/preact' order: 2
@urql/preact
Note: These API docs are deprecated as we now keep TSDocs in all published packages.
You can view TSDocs while using these packages in your editor, as long as it supports the
TypeScript Language Server.
We're planning to replace these API docs with a separate web app soon.
The @urql/preact API is the same as the React urql API.
Please refer to the "urql" API docs for details on the Preact API.
File: docs/api/README.md
title: API order: 9
API
urql is a collection of multiple packages. You'll likely be using one of the framework bindings
package or exchange packages, which are all listed in this section.
Most of these packages will refer to or use utilities and types from the @urql/core package. Read
more about the core package on the "Core" page.
Note: These API docs are deprecated as we now keep TSDocs in all published packages.
You can view TSDocs while using these packages in your editor, as long as it supports the
TypeScript Language Server.
We're planning to replace these API docs with a separate web app soon.
@urql/coreAPI docsurqlReact API docs@urql/preactPreact API docs@urql/svelteSvelte API docs@urql/exchange-graphcacheAPI docs@urql/exchange-retryAPI docs@urql/exchange-executeAPI docs@urql/exchange-request-policyAPI docs@urql/exchange-authAPI docs@urql/exchange-refocusAPI docs
File: docs/api/refocus-exchange.md
title: '@urql/exchange-refocus' order: 11
Refocus Exchange
Note: These API docs are deprecated as we now keep TSDocs in all published packages.
You can view TSDocs while using these packages in your editor, as long as it supports the
TypeScript Language Server.
We're planning to replace these API docs with a separate web app soon.
@urql/exchange-refocus is an exchange for the urql that tracks currently active operations and redispatches them when the
window regains focus
Quick Start Guide
First install @urql/exchange-refocus alongside urql:
yarn add @urql/exchange-refocus
# or
npm install --save @urql/exchange-refocusThen add it to your Client, preferably in front of your cacheExchange
import { createClient, cacheExchange, fetchExchange } from 'urql';
import { refocusExchange } from '@urql/exchange-refocus';
const client = createClient({
url: 'http://localhost:3000/graphql',
exchanges: [refocusExchange(), cacheExchange, fetchExchange],
});File: docs/api/request-policy-exchange.md
title: '@urql/exchange-request-policy' order: 9
Request Policy Exchange
Note: These API docs are deprecated as we now keep TSDocs in all published packages.
You can view TSDocs while using these packages in your editor, as long as it supports the
TypeScript Language Server.
We're planning to replace these API docs with a separate web app soon.
The @urql/exchange-request-policy package contains an addon requestPolicyExchange for urql
that may be used to upgrade Operations' Request Policies on a
time-to-live basis.
Read more about request policies on the "Document Caching" page.
This exchange will conditionally upgrade cache-first and cache-only operations to usecache-and-network, so that the client gets an opportunity to update its cached data, when the
operation hasn't been seen within the given ttl time. This is often preferable to setting the
default policy to cache-and-network to avoid an unnecessarily high amount of requests to be sent
to the API when switching pages.
Installation and Setup
First install @urql/exchange-request-policy alongside urql:
yarn add @urql/exchange-request-policy
# or
npm install --save @urql/exchange-request-policyThen add it to your Client, preferably in front of the cacheExchange and in front of any asynchronous
exchanges, like the fetchExchange:
import { createClient, cacheExchange, fetchExchange } from 'urql';
import { requestPolicyExchange } from '@urql/exchange-request-policy';
const client = createClient({
url: 'http://localhost:3000/graphql',
exchanges: [
requestPolicyExchange({
/* config */
}),
cacheExchange,
fetchExchange,
],
});Options
| Option | Description |
|---|---|
ttl |
The "time-to-live" until an Operation will be upgraded to the cache-and-network policy in milliseconds. By default 5 minutes is set. |
shouldUpgrade |
An optional function that receives an Operation as the only argument and may return true or false depending on whether an operation should be upgraded. This can be used to filter out operations that should never be upgraded to cache-and-network. |
File: docs/api/retry-exchange.md
title: '@urql/exchange-retry' order: 5
Retry Exchange
Note: These API docs are deprecated as we now keep TSDocs in all published packages.
You can view TSDocs while using these packages in your editor, as long as it supports the
TypeScript Language Server.
We're planning to replace these API docs with a separate web app soon.
The @urql/exchange-retry package contains an addon retryExchange for urql that may be used to
let failed operations be retried, typically when a previous operation has failed with a network
error.
Read more about how to use and configure the retryExchange on the "Retry Operations"
page.
Options
| Option | Description |
|---|---|
initialDelayMs |
Specify at what interval the retrying should start, this means that if we specify 1000 that when our operation fails we'll wait 1 second and then retry it. |
maxDelayMs |
The maximum delay between retries. The retryExchange will keep increasing the time between retries so that the server doesn't receive simultaneous requests it can't complete. This time between requests will increase with a random back-off factor applied to the initialDelayMs, read more about the thundering herd problem. |
randomDelay |
Allows the randomized delay described above to be disabled. When this option is set to false there will be exactly a initialDelayMs wait between each retry. |
maxNumberAttempts |
Defines the maximum number of attempts (including the initial request). For example, 2 means one retry after the initial attempt. |
retryIf |
Apply a custom test to the returned error to determine whether it should be retried. |
retryWith |
Apply a transform function allowing you to selectively replace a retried Operation or return a nullish value. This will act like retryIf where a truthy value retries (retryIf takes precedence and overrides this function.) |