Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.

docs: Update documentation site for @urql/core@4 (#3065)

+2 -3
docs/advanced/authentication.md
···
in front of all `fetchExchange`s but after all other synchronous exchanges, like the `cacheExchange`.
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql';
+
import { Client, cacheExchange, fetchExchange } from 'urql';
import { authExchange } from '@urql/exchange-auth';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
exchanges: [
-
dedupExchange,
cacheExchange,
authExchange(async utils => {
return {
+29 -29
docs/advanced/authoring-exchanges.md
···
## Using Exchanges
-
The `Client` accepts an `exchanges` option that defaults to the three default exchanges mentioned above. When we pass a custom list of exchanges the `Client` uses the `composeExchanges`
-
utility, which starts chaining these exchanges.
+
The `Client` accepts an `exchanges` option that. Initially, we may choose to just
+
set this to two very standard exchanges — `cacheExchange` and `fetchExchange`.
In essence these exchanges build a pipeline that runs in the order they're passed; _Operations_ flow
in from the start to the end, and _Results_ are returned through the chain in reverse.
-
If we look at our list of default exchanges — `dedupExchange`, `cacheExchange`, and then
-
`fetchExchange` — an incoming operation is treated as follows:
+
Suppose we pass the `cacheExchange` and then the `fetchExchange` to the `exchanges`.
-
**First,** ongoing operations are deduplicated. It wouldn't make sense to send the
-
same operation / request twice in parallel.
-
-
**Second,** operations are checked against the cache. Depending on the `requestPolicy`,
+
**First,** operations are checked against the cache. Depending on the `requestPolicy`,
cached results can be resolved from here instead, which would mean that the cache sends back the
result, and the operation doesn't travel any further in the chain.
-
**Third,** operations are sent to the API, and the result is turned into an `OperationResult`.
+
**Second,** operations are sent to the API, and the result is turned into an `OperationResult`.
**Lastly,** operation results then travel through the exchanges in _reverse order_, which is because
exchanges are a pipeline where all operations travel forward deeper into the exchange chain, and
···
result.
```js
-
import { createClient, dedupExchange, fetchExchange, cacheExchange } from 'urql';
+
import { Client, fetchExchange, cacheExchange } from 'urql';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
});
```
-
We can add more exchanges to this chain, for instance, we can add the `errorExchange`, which calls a
-
global callback whenever it sees [a `CombinedError`](../basics/errors.md) on an `OperationResult`.
+
We can add more exchanges to this chain, for instance, we can add the `mapExchange`, which can call a
+
callback whenever it sees [a `CombinedError`](../basics/errors.md) occur on a result.
```js
-
import { createClient, dedupExchange, fetchExchange, cacheExchange, errorExchange } from 'urql';
+
import { Client, fetchExchange, cacheExchange, mapExchange } from 'urql';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
exchanges: [
-
dedupExchange,
cacheExchange,
-
errorExchange({
+
mapExchange({
onError(error) {
console.error(error);
},
···
### Forward and Return Composition
When you create a `Client` and pass it an array of exchanges, `urql` composes them left-to-right.
-
If we look at our previous `noopExchange` example in context, we can track what it does if it is located between the `dedupExchange` and the `fetchExchange`.
+
If we look at our previous `noopExchange` example in context, we can track what it does if it is located between the `cacheExchange` and the `fetchExchange`.
```js
-
import { Client, dedupExchange, fetchExchange } from 'urql';
+
import { Client, cacheExchange, fetchExchange } from 'urql';
const noopExchange = ({ client, forward }) => {
return operations$ => {
// <-- The ExchangeIO function
-
// We receive a stream of Operations from `dedupExchange` which
+
// We receive a stream of Operations from `cacheExchange` which
// we can modify before...
const forwardOperations$ = operations$;
···
const operationResult$ = forward(operations$);
// We get back `fetchExchange`'s stream of results, which we can
-
// also change before returning, which is what `dedupExchange`
+
// also change before returning, which is what `cacheExchange`
// will receive when calling `forward`.
return operationResult$;
};
};
const client = new Client({
-
exchanges: [dedupExchange, noopExchange, fetchExchange],
+
exchanges: [cacheExchange, noopExchange, fetchExchange],
});
```
···
This why **all exchanges should be ordered synchronous first and
asynchronous last**.
-
The default order of exchanges is:
+
What we for instance repeat as the default setup in our docs is this:
```js
-
import { dedupExchange, cacheExchange, fetchExchange } from 'urql';
+
import { Client, cacheExchange, fetchExchange } from 'urql';
-
[dedupExchange, cacheExchange, fetchExchange];
+
new Client({
+
// ...
+
exchanges: [cacheExchange, fetchExchange];
+
});
```
-
Both the `dedupExchange` and `cacheExchange` are completely
-
synchronous. The `fetchExchange` is asynchronous since
-
it makes a `fetch` request and waits for a server response.
+
The `cacheExchange` is completely synchronous.
+
The `fetchExchange` is asynchronous since it makes a `fetch` request and waits for a server response.
+
If we put an asynchronous exchange in front of the `cacheExchange`, that would be unexpected, and
+
since all results would then be delayed, nothing would ever be "cached" and instead always take some
+
amount of time to be returned.
When you're adding more exchanges, it's often crucial
to put them in a specific order. For instance, an authentication exchange
+4 -4
docs/advanced/auto-populate-mutations.md
···
client options.
```ts
-
import { createClient, dedupExchange, fetchExchange } from '@urql/core';
+
import { Client, fetchExchange } from '@urql/core';
import { populateExchange } from '@urql/exchange-populate';
-
const client = createClient({
+
const client = new Client({
// ...
-
exchanges: [dedupExchange, populateExchange({ schema }), cacheExchange, fetchExchange],
+
exchanges: [populateExchange({ schema }), cacheExchange, fetchExchange],
});
```
The `populateExchange` should be placed in front of the `cacheExchange`, especially if you're using
[Graphcache](../graphcache/README.md), since it won't understand the `@populate` directive on its
-
own. It should also be placed after the `dedupExchange` to avoid unnecessary work.
+
own. It should also be placed in front the `cacheExchange` to avoid unnecessary work.
Adding the `populateExchange` now enables us to use the `@populate` directive in our mutations.
+1 -1
docs/advanced/debugging.md
···
```
const { unsubscribe } = client.subscribeToDebugTarget(event => {
-
if (event.source === 'dedupExchange')
+
if (event.source === 'cacheExchange')
return;
console.log(event); // { type, message, operation, data, source, timestamp }
});
+34 -34
docs/advanced/persistence-and-uploads.md
···
# Persisted Queries and Uploads
-
`urql` supports both [Automatic Persisted
-
Queries](https://www.apollographql.com/docs/apollo-server/performance/apq/), Persisted Queries, and
-
[File Uploads](https://www.apollographql.com/docs/apollo-server/data/file-uploads/).
-
-
While File Uploads should work without any modifications, an additional exchange must be installed
-
and added for Persisted Queries to work.
+
`urql` supports (Automatic) Persisted Queries, and File Uploads via GraphQL
+
Multipart requests. For persisted queries to work, some setup work is needed,
+
while File Upload support is built into `@urql/core@4`.
## Automatic Persisted Queries
···
easier for a CDN to cache, as by default most caches would not cache POST requests automatically.
In `urql`, we may use the `@urql/exchange-persisted` package's `persistedExchange` to
-
implement Automatic Persisted Queries. This exchange works alongside the default `fetchExchange`
-
and other exchanges by adding the `extensions.persistedQuery` parameters to a GraphQL request.
+
enable support for Automatic Persisted Queries. This exchange works alongside other fetch or
+
subscription exchanges by adding metadata for persisted queries to each GraphQL
+
request by modifying the `extensions` object of operations.
### Installation & Setup
···
```
You'll then need to add the `persistedExchange` function, that this package exposes,
-
to your `exchanges`.
+
to your `exchanges`, in front of exchanges that communicate with the API:
```js
-
import { createClient, dedupExchange, fetchExchange, cacheExchange } from 'urql';
-
import { persistedExchange } from '@urql/exchange-persisted-fetch';
+
import { Client, fetchExchange, cacheExchange } from 'urql';
+
import { persistedExchange } from '@urql/exchange-persisted';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:1234/graphql',
exchanges: [
-
dedupExchange,
cacheExchange,
persistedExchange({
preferGetForPersistedQueries: true,
···
As we can see, typically it's recommended to set `preferGetForPersistedQueries` to `true` to force
all persisted queries to use GET requests instead of POST so that CDNs can do their job.
-
We also added the `persistedExchange` in front of the usual `fetchExchange`, since it has to
-
update operations before they reach an exchange that talks to an API.
+
It does so by setting the `preferGetMethod` option to `'force'` when it's
+
updating operations.
-
The `preferGetForPersistedQueries` is similar to the [`Client`'s
-
`preferGetMethod`](../api/core.md#client) but only switches persisted queries to use GET requests
-
instead. This is preferable since sometimes the GraphQL query can grow too large for a simple GET
-
query to handle, while the `persistedExchange`'s SHA256 hashes will remain predictably small.
+
The `fetchExchange` can see the modifications that the `persistedExchange` is
+
making to operations, and understands to leave out the `query` from any request
+
as needed. The same should be happening to the `subscriptionExchange`, if you're
+
using it for queries.
### Customizing Hashing
···
second argument, a GraphQL `DocumentNode` object.
```js
-
persistedFetchExchange({
+
persistedExchange({
generateHash: (_, document) => document.documentId,
});
```
···
```js
import sha256 from 'hash.js/lib/hash/sha/256';
-
persistedFetchExchange({
-
generateHash: async query => {
+
persistedExchange({
+
async generateHash(query) {
return sha256().update(query).digest('hex');
},
});
···
## File Uploads
-
Many GraphQL server frameworks and APIs support the ["GraphQL Multipart Request
-
Spec](https://github.com/jaydenseric/graphql-multipart-request-spec) to allow files to be uploaded.
-
Often, this is defined in schemas using a `File` or `Upload` input.
-
This allows us to pass a `File` or `Blob` directly to our GraphQL requests as variables, and the
-
spec requires us to perform this request as a multipart upload.
+
GraphQL server APIs commonly support the [GraphQL Multipart Request
+
spec](https://github.com/jaydenseric/graphql-multipart-request-spec) to allow for File Uploads
+
directly with a GraphQL API.
-
Files are often handled in the browser via the [File API](https://developer.mozilla.org/en-US/docs/Web/API/File),
-
which we may typically get to via a [file input](https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications)
+
If a GraphQL API supports this, we can pass a [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File)
+
or a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) directly into our variables and
+
define the corresponding scalar for our variable, which is often called `File` or `Upload`.
+
+
In a browser, the `File` object may often be retrieved via a
+
[file input](https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications),
for example.
-
In `urql`, these are supported natively, so as long as your JS environment supports either `File` or
-
`Blob`s, you can pass these directly to any `urql` API via your `variables`, and the default
-
`fetchExchange` will swich to using a multipart request instead.
+
The `@urql/core@4` package supports File Uploads natively, so we won't have to do any installation
+
or setup work. When `urql` sees a `File` or a `Blob` anywhere in your `variables`, it switches to
+
a `multipart/form-data` request, converts the request to a `FormData` object, according to the
+
GraphQL Multipart Request specification, and sends it off to the API.
-
Previously, this worked by installing the [`@urql/multipart-fetch-exchange` package](../api/multipart-fetch-exchange.md),
-
however, this package has been deprecated and file uploads are now built into `@urql/core`.
+
> **Note:** Previously, this worked by installing the `@urql/multipart-fetch-exchange` package.
+
> however, this package has been deprecated and file uploads are now built into `@urql/core@4`.
+5 -7
docs/advanced/retry-operations.md
···
You'll then need to add the `retryExchange`, exposed by this package, to your `urql` Client:
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql';
+
import { Client, cacheExchange, fetchExchange } from 'urql';
import { retryExchange } from '@urql/exchange-retry';
// None of these options have to be added, these are the default values.
···
// Note the position of the retryExchange - it should be placed prior to the
// fetchExchange and after the cacheExchange for it to function correctly
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:1234/graphql',
exchanges: [
-
dedupExchange,
cacheExchange,
retryExchange(options), // Use the retryExchange factory to add a new exchange
fetchExchange,
···
let's look at an example:
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql';
+
import { Client, cacheExchange, fetchExchange } from 'urql';
import { retryExchange } from '@urql/exchange-retry';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:1234/graphql',
exchanges: [
-
dedupExchange,
cacheExchange,
retryExchange({
retryIf: error => {
-
return !!(error.graphQLErrors.length > 0 || error.networkError);
+
return !!(error.graphQLErrors.length > 0 || error.networkError);
},
}),
fetchExchange,
+6 -7
docs/advanced/server-side-rendering.md
···
To start out with the `ssrExchange` we have to add the exchange to our `Client`:
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange, ssrExchange } from '@urql/core';
+
import { Client, cacheExchange, fetchExchange, ssrExchange } from '@urql/core';
const isServerSide = typeof window === 'undefined';
···
initialState: !isServerSide ? window.__URQL_DATA__ : undefined,
});
-
const client = createClient({
+
const client = new Client({
exchanges: [
-
dedupExchange,
cacheExchange,
ssr, // Add `ssr` in front of the `fetchExchange`
fetchExchange,
···
import prepass from 'react-ssr-prepass';
import {
-
createClient,
-
dedupExchange,
+
Client,
cacheExchange,
fetchExchange,
ssrExchange,
···
// ...
const ssr = ssrExchange({ isClient: false });
-
const client createClient({
+
const client new Client({
+
url: 'https://??',
suspense: true, // This activates urql's Suspense mode on the server-side
-
exchanges: [dedupExchange, cacheExchange, ssr, fetchExchange]
+
exchanges: [cacheExchange, ssr, fetchExchange]
});
const element = (
+13 -18
docs/advanced/subscriptions.md
···
To add support for subscriptions we need to add the `subscriptionExchange` to our `Client`.
```js
-
import { Client, dedupExchange, cacheExchange, fetchExchange, subscriptionExchange } from 'urql';
+
import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql';
const client = new Client({
url: 'http://localhost:3000/graphql',
exchanges: [
-
dedupExchange,
cacheExchange,
fetchExchange,
subscriptionExchange({
···
For backends supporting `graphql-ws`, we recommend using the [graphql-ws](https://github.com/enisdenjo/graphql-ws) client.
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange, subscriptionExchange } from 'urql';
+
import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql';
import { createClient as createWSClient } from 'graphql-ws';
const wsClient = createWSClient({
url: 'ws://localhost/graphql',
});
-
const client = createClient({
+
const client = new Client({
url: '/graphql',
exchanges: [
-
dedupExchange,
cacheExchange,
fetchExchange,
subscriptionExchange({
···
> The `subscriptions-transport-ws` package isn't actively maintained. If your API supports the new protocol or you can swap the package out, consider using [`graphql-ws`](#setting-up-graphql-ws) instead.
```js
-
import { Client, dedupExchange, cacheExchange, fetchExchange, subscriptionExchange } from 'urql';
+
import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql';
import { SubscriptionClient } from 'subscriptions-transport-ws';
const subscriptionClient = new SubscriptionClient('ws://localhost/graphql', { reconnect: true });
···
const client = new Client({
url: '/graphql',
exchanges: [
-
dedupExchange,
cacheExchange,
fetchExchange,
subscriptionExchange({
···
from
text
}
-
}`
-
});
+
}
+
`,
+
});
</script>
{#if !$messages.data}
···
text
}
}
-
`
+
`,
}, handleSubscription)
return {
···
use subscriptions in a Node.js environment.
```js
-
import { pipe, subscribe } from 'wonka';
+
import { gql } from '@urql/core';
-
const MessageSub = `
+
const MessageSub = gql`
subscription MessageSub {
newMessages {
id
···
}
`;
-
const { unsubscribe } = pipe(
-
client.subscription(MessageSub),
-
subscribe(result => {
-
console.log(result); // { data: ... }
-
})
-
);
+
const { unsubscribe } = client.subscription(MessageSub).subscribe(result => {
+
console.log(result); // { data: ... }
+
});
```
+5 -1
docs/api/README.md
···
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.](../basics/core.md)
+
> **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/core` API docs](./core.md)
- [`urql` React API docs](./urql.md)
- [`@urql/preact` Preact API docs](./preact.md)
···
- [`@urql/exchange-graphcache` API docs](./graphcache.md)
- [`@urql/exchange-retry` API docs](./retry-exchange.md)
- [`@urql/exchange-execute` API docs](./execute-exchange.md)
-
- [`@urql/exchange-multipart-fetch` API docs](./multipart-fetch-exchange.md)
- [`@urql/exchange-request-policy` API docs](./request-policy-exchange.md)
- [`@urql/exchange-auth` API docs](./auth-exchange.md)
- [`@urql/exchange-refocus` API docs](./refocus-exchange.md)
+5
docs/api/auth-exchange.md
···
# 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.
+6 -1
docs/api/core.md
···
# @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](./urql.md) or [`@urql/preact`](./preact.md), will reuse the core logic and
reexport all exports from `@urql/core`.
···
| Input | Type | Description |
| ----------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-
| `exchanges` | `Exchange[]` | An array of `Exchange`s that the client should use |
+
| `exchanges` | `Exchange[]` | An array of `Exchange`s 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` |
+5
docs/api/execute-exchange.md
···
# 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.
+5
docs/api/graphcache.md
···
# @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`](./core.md#cacheexchange), which switches `urql` from
using ["Document Caching"](../basics/document-caching.md) to ["Normalized
-46
docs/api/multipart-fetch-exchange.md
···
-
---
-
title: '@urql/exchange-multipart-fetch'
-
order: 7
-
---
-
-
# Multipart Fetch Exchange
-
-
> **Deprecation**: The `multipartFetchExchange` has been deprecated, and
-
> `@urql/core` now supports GraphQL Multipart Requests natively. This won't
-
> break the behaviour of your existing apps, however, it's recommended to remove
-
> the `multipartFetchExchange` from your apps.
-
-
The `@urql/exchange-multipart-fetch` package contains an addon `multipartFetchExchange` for `urql`
-
that enables file uploads via `multipart/form-data` POST requests.
-
-
It follows the unofficial [GraphQL Multipart Request
-
Spec](https://github.com/jaydenseric/graphql-multipart-request-spec) which is supported by the
-
[Apollo Sever package](https://www.apollographql.com/docs/apollo-server/data/file-uploads/).
-
-
This exchange uses the same fetch logic as the [`fetchExchange`](./core.md#fetchexchange) and by reusing logic from `@urql/core/internal`.
-
The `multipartFetchExchange` is a drop-in replacement for the default
-
[`fetchExchange`](./core.md#fetchexchange) and will act exactly like the `fetchExchange` unless the
-
`variables` that it receives for mutations contain any `File`s as detected by the `extract-files` package.
-
-
## Installation and Setup
-
-
First install `@urql/exchange-multipart-fetch` alongside `urql`:
-
-
```sh
-
yarn add @urql/exchange-multipart-fetch
-
# or
-
npm install --save @urql/exchange-multipart-fetch
-
```
-
-
The `multipartFetchExchange` is a drop-in replacement for the `fetchExchange`, which should be
-
replaced in the list of `exchanges`:
-
-
```js
-
import { createClient, dedupExchange, cacheExchange } from 'urql';
-
import { multipartFetchExchange } from '@urql/exchange-multipart-fetch';
-
-
const client = createClient({
-
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, multipartFetchExchange],
-
});
-
```
+5
docs/api/preact.md
···
# @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](./urql.md) for details on the Preact API.
+5
docs/api/refocus-exchange.md
···
# 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
+5
docs/api/request-policy-exchange.md
···
# 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](./core.md#requestpolicy) on a
time-to-live basis.
+5
docs/api/retry-exchange.md
···
# 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.
+5
docs/api/svelte.md
···
# Svelte API
+
> **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.
+
## queryStore
The `queryStore` factory accepts properties as inputs and returns a Svelte pausable, readable store
+12 -7
docs/api/urql.md
···
# React API
+
> **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.
+
## useQuery
Accepts a single required options object as an input with the following properties:
···
| `query` | `string \| 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. |
| `requestPolicy` | `?RequestPolicy` | An optional [request policy](./core.md#requestpolicy) that should be used specifying the cache strategy. |
-
| `pause` | `?boolean` | A boolean flag instructing [execution to be paused](../basics/react-preact.md#pausing-usequery). |
+
| `pause` | `?boolean` | A boolean flag instructing [execution to be paused](../basics/react-preact.md#pausing-usequery). |
| `context` | `?object` | Holds the contextual information for the query. |
This hook returns a tuple of the shape `[result, executeQuery]`.
···
Accepts a single required options object as an input with the following properties:
-
| Prop | Type | Description |
-
| ---------------------------------------------------- | ------------------------ | ---------------------------------------------------------------------------------- |
-
| `query` | `string \| 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. |
-
| `pause` | `?boolean` | A boolean flag instructing [execution to be paused](../basics/react-preact.md#pausing-usequery). |
-
| `context` | `?object` | Holds the contextual information for the query. |
+
| Prop | Type | Description |
+
| ----------- | ------------------------ | ------------------------------------------------------------------------------------------------ |
+
| `query` | `string \| 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. |
+
| `pause` | `?boolean` | A boolean flag instructing [execution to be paused](../basics/react-preact.md#pausing-usequery). |
+
| `context` | `?object` | Holds the contextual information for the query. |
The hook optionally accepts a second argument, which may be a handler function with a type signature
of:
+5
docs/api/vue.md
···
# Vue API
+
> **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.
+
## useQuery
Accepts a single required options object as an input with the following properties:
+110 -91
docs/architecture.md
···
# Architecture
-
`urql` is a highly customizable and flexible GraphQL client, that happens to come with some default
-
[core behavior in the core package](./basics/core.md).
+
`urql` is a highly customizable and flexible GraphQL client.
+
As you use it in your app, it's split into three parts:
+
+
- Bindings — such as for React, Preact, Vue, or Svelte — which interact with `@urql/core`'s
+
`Client`.
+
- The Client — as created [with the core `@urql/core` package](./basics/code.md), which interacts with "exchanges" to execute GraphQL
+
operations, and which you can also use directly.
+
- Exchanges, which provide functionality like fetching or caching to the `Client`.
By default, `urql` aims to provide the minimal amount of features that allow us to build an app
quickly. However, `urql` has also been designed to be a GraphQL Client
···
## Requests and Operations on the Client
If `urql` was a train it would take several stops to arrive at its terminus, our API. It starts with us
-
defining queries or mutations. Any GraphQL request can be abstracted into their query documents and
-
their variables. In `urql`, these GraphQL requests are treated as unique objects, which are uniquely
-
identified by the query document and variables (which is why a `key` is generated from the two). This
-
`key` is a hash number of the query document and variables and uniquely identifies our
-
[`GraphQLRequest`](./api/core.md#graphqlrequest).
+
defining queries or mutations by writing in GraphQL's query language.
+
+
Any GraphQL request can be abstracted into its query documents and its variables.
+
+
```js
+
import { gql } from '@urql/core';
+
+
const query = gql`
+
query($name: String!) {
+
helloWorld(name: $name)
+
}
+
`;
+
+
const request = createRequest(query, {
+
name: 'Urkel',
+
});
+
```
+
+
In `urql`, these GraphQL requests are treated as unique objects and each GraphQL request will have
+
a `key` generated for them. This `key` is a hash of the query document and the variables you provide
+
and are set on the `key` property of a [`GraphQLRequest`](./api/core.md#graphqlrequest).
-
Whenever we decide to send a request to our API we start by using `urql`'s
-
[`Client`](./api/core.md#client). It accepts several options like `url` or `requestPolicy` which are
-
extra information on how the GraphQL requests are executed.
+
Whenever we decide to send our GraphQL requests to a GraphQL API we start by using `urql`'s
+
[`Client`](./api/core.md#client).
+
The `Client` accepts several options to configure its behaviour and the behaviour of exchanges,
+
like the `fetchExchange`. For instance, we can pass it a `url` which the `fetchExchange` will
+
use to make a `fetch` call to our GraphQL API.
```js
-
import { Client, dedupExchange, cacheExchange, fetchExchange } from '@urql/core';
+
import { Client, cacheExchange, fetchExchange } from '@urql/core';
-
new Client({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
requestPolicy: 'cache-first',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange]
+
exchanges: [cacheExchange, fetchExchange],
});
```
-
The bindings that we've seen in [the "Basics" section](./basics/README.md) interact with [the
-
`Client`](./api/core.md#client) directly and are a thin abstraction on top of it. Though some methods can be called on it directly, as seen [on the "Core Usage"
+
Above, we're defining a `Client` that is ready to accept our requests. It will apply basic
+
document caching and will send uncached requests to the `url` we pass it.
+
The bindings that we've seen in [the "Basics" section](./basics/README.md), like `useQuery` for
+
React for example, interact with [the `Client`](./api/core.md#client) directly and are a thin
+
abstraction.
+
+
Some methods can be called on it directly however, as seen [on the "Core Usage"
page](./basics/core.md#one-off-queries-and-mutations).
-
When we send our queries or mutations to the `Client`, internally they will be managed as
-
[`Operation`](./api/core.md#operation)s. An "Operation" is an extension of `GraphQLRequest`s. Not
-
only do they carry the `query`, `variables`, and a `key` property, they will also identify the
-
`kind` of operation that is executed, like `"query"` or `"mutation"`. We can also find the
-
`Client`'s options on `operation.context` which carries an operation's metadata.
+
```js
+
// Given our request and client defined above, we can call
+
const subscription = client.executeQuery(request).subscribe(result => {
+
console.log(result.data);
+
});
+
```
+
+
As we've seen, `urql` defines our query documents and variables as
+
[`GraphQLRequest`s](./api/core.md#graphqlrequest). However, since we have more metadata that is
+
needed, like our `url` option on the `Client`, `urql` internally creates [`Operation`s](./api/core.md#operation)
+
each time a request is executed. The operations are then forwarded to the exchanges, like the
+
`cacheExchange` and `fetchExchange`.
+
+
An "Operation" is an extension of `GraphQLRequest`s. Not only do they carry the `query`, `variables`,
+
and a `key` property, they will also identify the `kind` of operation that is executed, like
+
`"query"` or `"mutation"`, and they contain the `Client`'s options on `operation.context`.
![Operations and Results](./assets/urql-event-hub.png)
-
It's the `Client`s responsibility to accept an `Operation` and execute it. The bindings internally
-
call the `client.executeQuery`, `client.executeMutation`, or `client.executeSubscription` methods,
-
and we'll get a "stream" of results. This "stream" allows us to register a callback with it to
-
receive results.
+
This means, once we hand over a GraphQL request to the `Client`, it will create an `Operation`,
+
and then hand it over to the exchanges until a result comes back.
-
In the diagram we can see that each operation is a signal for our request to start at which point
-
we can expect to receive our results eventually on a callback. Once we're not interested in results
-
anymore a special "teardown" signal is issued on the `Client`. While we don't see operations outside
-
the `Client`, they're what travel through the "Exchanges" on the `Client`.
+
As shown in the diagram, each operation is like an event or signal for a GraphQL request to start,
+
and the exchanges will eventually send back a corresponding result.
+
However, because the cache can send updates to us whenever it detects a change, or you could cancel
+
a GraphQL request before it finishes, a special "teardown" `Operation` also exists, which cancels
+
ongoing requests.
## The Client and Exchanges
To reiterate, when we use `urql`'s bindings for our framework of choice, methods are called on the
`Client`, but we never see the operations that are created in the background from our bindings. We
call a method like `client.executeQuery` (or it's called for us in the bindings), an operation is
-
issued internally when we subscribe with a callback, and later our callback is called with results.
+
issued internally when we subscribe with a callback, and later, we're given results.
![Operations stream and results stream](./assets/urql-client-architecture.png)
···
### The Exchanges
-
The default set of exchanges that `@urql/core` contains and applies to a `Client` are:
+
By default, the `Client` doesn't do anything with GraphQL requests. It contains only the logic to
+
manage and differentiate between active and inactive requests and converts them to operations.
+
To actually do something with our GraphQL requests, it needs _exchanges_, which are like plugins
+
that you can pass to create a pipeline of how GraphQL operations are executed.
+
+
By default, you may want to add the `cacheExchange` and the `fetchExchange` from `@urql/core`:
-
- `dedupExchange`: Deduplicates pending operations (pending = waiting for a result)
-
- `cacheExchange`: The default caching logic with ["Document Caching"](./basics/document-caching.md)
-
- `fetchExchange`: Sends an operation to the API using `fetch` and adds results to the output stream
+
- `cacheExchange`: Caches GraphQL results with ["Document Caching"](./basics/document-caching.md)
+
- `fetchExchange`: Executes GraphQL requests with a `fetch` HTTP call
-
When we don't pass the `exchanges` option manually to our `Client` then these are the ones that will
-
be applied. As we can see, an exchange exerts a lot of power over our operations and results. They
-
determine a lot of the logic of the `Client`, taking care of things like deduplication, caching, and
-
sending requests to our API.
+
```js
+
import { Client, cacheExchange, fetchExchange } from '@urql/core';
-
Some of the exchanges that are available to us are:
+
const client = new Client({
+
url: 'http://localhost:3000/graphql',
+
exchanges: [cacheExchange, fetchExchange],
+
});
+
```
-
- [`mapExchange`](./api/core.md#mapexchange): Allows reacting to operations, results, and errors
+
As we can tell, exchanges define not only how GraphQL requests are executed and handled, but also
+
get control over caching. Exchanges can be used to change almost any behaviour in the `Client`,
+
although internally they only handle incoming & outgoing requests and incoming & outgoing results.
+
+
Some more exchanges that we can use with our `Client` are:
+
+
- [`mapExchange`](./api/core.md#mapexchange): Allows changing and reacting to operations, results, and errors
- [`ssrExchange`](./advanced/server-side-rendering.md): Allows for a server-side renderer to
collect results for client-side rehydration.
-
- [`retryExchange`](./advanced/retry-operations.md): Allows operations to be retried
-
- [`multipartFetchExchange`](./advanced/persistence-and-uploads.md#file-uploads): Provides multipart file upload capability
+
- [`retryExchange`](./advanced/retry-operations.md): Allows operations to be retried on errors
- [`persistedFetchExchange`](./advanced/persistence-and-uploads.md#automatic-persisted-queries): Provides support for Automatic
Persisted Queries
-
- [`authExchange`](./advanced/authentication.md): Allows complex authentication flows to be implemented
-
easily.
-
- [`requestPolicyExchange`](./api/request-policy-exchange.md): Automatically upgrades `cache-only` and `cache-first` operations to `cache-and-network` after a given amount of time.
-
- [`refocusExchange`](./api/refocus-exchange.md): Tracks open queries and refetches them
-
when the window regains focus.
+
- [`authExchange`](./advanced/authentication.md): Allows refresh authentication to be implemented easily.
+
- [`requestPolicyExchange`](./api/request-policy-exchange.md): Automatically refreshes results given a TTL.
- `devtoolsExchange`: Provides the ability to use the [urql-devtools](https://github.com/urql-graphql/urql-devtools)
We can even swap out our [document cache](./basics/document-caching.md), which is implemented by
···
directly](./basics/core.md#one-off-queries-and-mutations) or write exchanges then we'll see streams
and will have to deal with their API.
-
### The Wonka library
-
-
`urql` utilises the [Wonka](https://github.com/kitten/wonka) library for its streams. It has a
-
few advantages that are specifically tailored for the `urql` library and ecosystem:
-
-
- It is extremely lightweight and treeshakeable, with a size of around 3.7kB minzipped.
-
- It's cross-platform and cross-language compatible, having been written in
-
[Reason](https://reasonml.github.io/) and provides support for [Flow](https://flow.org/)
-
and [TypeScript](https://www.typescriptlang.org/v2/).
-
- It's a predictable and iterable toolchain, emitting synchronous events whenever possible.
-
-
Typical usage of Wonka will involve creating a _source_ of some values and a _sink_.
-
-
```js
-
import { fromArray, map, subscribe, pipe } from 'wonka';
-
-
const { unsubscribe } = pipe(
-
fromArray([1, 2, 3]),
-
map(x => x * 2),
-
subscribe(x => {
-
console.log(x); // 2, 4, 6
-
})
-
);
-
```
-
-
In Wonka, like with Observables, streams are cancellable by calling the `unsubscribe` method that a
-
subscription returns.
-
-
[Read more about Wonka in its documentation](https://wonka.kitten.sh/basics/background).
-
### Stream patterns with the client
When we call methods on the `Client` like [`client.executeQuery`](./api/core.md#clientexecutequery)
-
or [`client.query`](./api/core.md#clientquery) then these will return a Wonka stream. Those are
-
essentially just a bunch of callbacks.
+
or [`client.query`](./api/core.md#clientquery) then these will return a "stream" of results.
-
We can use [`wonka`'s `subscribe`](https://wonka.kitten.sh/api/sinks#subscribe) function to start
-
this stream. We pass this function a callback and will receive results back from the `Client`, as it
-
starts our operation. When we unsubscribe then the `Client` will stop this operation by sending a
-
special "teardown" operation to our exchanges.
+
It's normal for GraphQL subscriptions to deliver multiple results, however, even GraphQL queries can
+
give you multiple results in `urql`. This is because operations influence one another. When a cache
+
invalidates a query, this query may refetch, and a new result is delivered to your application.
+
+
Multiple results mean that once you subscribe to a GraphQL query via the `Client`, you may receive
+
new results in the future.
```js
-
import { pipe, subscribe } from 'wonka';
+
import { gql } from '@urql/core';
-
const QUERY = `
+
const QUERY = gql`
query Test($id: ID!) {
getUser(id: $id) {
id
···
}
`;
-
const { unsubscribe } = pipe(
-
client.query(QUERY, { id: 'test' }),
-
subscribe(result => {
-
console.log(result); // { data: ... }
-
})
-
);
+
client.query(QUERY, { id: 'test' }).subscribe(result => {
+
console.log(result); // { data: ... }
+
});
```
Read more about the available APIs on the `Client` in the [Core API docs](./api/core.md).
+
+
Internally, these streams and all exchanges are written using a library called
+
[`wonka`](https://wonka.kitten.sh/basics/background), which is a tiny Observable-like
+
library. It is used to write exchanges and when we interact with the `Client` it is used internally
+
as well.
+56 -32
docs/basics/core.md
···
`@urql/core` or use it standalone, e.g. in a Node.js environment.
```sh
-
yarn add @urql/core graphql
+
yarn add @urql/core
# or
-
npm install --save @urql/core graphql
+
npm install --save @urql/core
```
Since all bindings and all exchanges depend on `@urql/core`, we may sometimes run into problems
···
deduplicating our dependencies.
```sh
+
# npm
+
npm dedupe
+
# pnpm
+
pnpm dedupe
+
# yarn
npx yarn-deduplicate && yarn
-
# or
-
npm dedupe
```
## GraphQL Tags
···
### Setting up the `Client`
-
The `@urql/core` package exports a function called `createClient` which we can use to
+
The `@urql/core` package exports a `Client` class, which we can use to
create the GraphQL client. This central `Client` manages all of our GraphQL requests and results.
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/core';
+
import { Client, cacheExchange, fetchExchange } from '@urql/core';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
});
```
-
At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started.
+
At the bare minimum we'll need to pass an API's `url`, and the `fetchExchange`,
+
when we create a `Client` to get started.
Another common option is `fetchOptions`. This option allows us to customize the options that will be
passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object, or
···
GraphQL API.
```js
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
fetchOptions: () => {
const token = getToken();
return {
···
### The `Client`s options
-
As we've seen above, the most important option for the `Client` is `url`, since it won't work
-
without it. However, another important option on the `Client` is the `exchanges` option.
+
As we've seen above, the most important options for the `Client` are `url` and `exchanges`.
+
The `url` option is used by the `fetchExchange` to send GraphQL requests to an API.
-
This option passes a list of exchanges to the `Client`, which tell it how to execute our requests
-
and how to cache data in a certain order.
+
The `exchanges` option is of particular importance however because it tells the `Client` what to do
+
with our GraphQL requests:
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/core';
+
import { Client, cacheExchange, fetchExchange } from '@urql/core';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
});
```
+
For instance, here, the `Client`'s caching and fetching features are only available because we're
+
passing it exchanges. In the above example, the `Client` will try to first read a GraphQL request
+
from a local cache, and if this request isn't cached it'll make an HTTP request.
+
The caching in `urql` is also implemented as an exchange, so for instance, the behavior described
+
on the ["Document Caching" page](./document-caching.md) is all contained within the `cacheExchange`
+
above.
+
Later, [in the "Advanced" section](../advanced/README.md) we'll see many more features that `urql`
supports by adding new exchanges to this list. On [the "Architecture" page](../architecture.md)
we'll also learn more about what exchanges are and why they exist.
-
-
For now, it's enough for us to know that our requests are executed using the logic in the
-
exchanges in order. First, the `dedupExchange` deduplicates requests if we send the same queries
-
twice, the `cacheExchange` implements the default "document caching" behaviour (as we'll learn about
-
on the ["Document Caching"](./document-caching.md) page), and lastly the `fetchExchange` is
-
responsible for sending our requests to our GraphQL API.
### One-off Queries and Mutations
···
result as a promise. This may be useful when we don't plan on cancelling queries, or we don't
care about future updates to this data and are just looking to query a result once.
+
This can also be written using async/await by simply awaiting the return value of `client.query`:
+
+
```js
+
const QUERY = `
+
query Test($id: ID!) {
+
getUser(id: $id) {
+
id
+
name
+
}
+
}
+
`;
+
+
async function query() {
+
const result = await client.query(QUERY, { id: 'test' });
+
console.log(result); // { data: ... }
+
}
+
```
+
The same can be done for mutations by calling the `client.mutation` method instead of the
`client.query` method.
+
+
It's worth noting that promisifying a query result will always only give us _one_ result, because
+
we're not calling `subscribe`. This means that we'll never see cache updates when we're asking for
+
a single result like we do above.
+
+
#### Reading only cache data
Similarly there's a way to read data from the cache synchronously, provided that the cache has
received a result for a given query before. The `Client` has a `readQuery` method, which is a
···
somewhere else then we'll get notified of the new API result as well, as long as we're subscribed.
```js
-
import { pipe, subscribe } from 'wonka';
-
const QUERY = `
query Test($id: ID!) {
getUser(id: $id) {
···
}
`;
-
const { unsubscribe } = pipe(
-
client.query(QUERY, { id: 'test' }),
-
subscribe(result => {
-
console.log(result); // { data: ... }
-
})
-
);
+
const { unsubscribe } = client.query(QUERY, { id: 'test' }).subscribe(result => {
+
console.log(result); // { data: ... }
+
});
```
This code example is similar to the one before. However, instead of sending a one-off query, we're
+21 -21
docs/basics/react-preact.md
···
with at first. We'll install the package with our package manager of choice.
```sh
-
yarn add urql graphql
+
yarn add urql
# or
-
npm install --save urql graphql
+
npm install --save urql
```
To use `urql` with Preact, we have to install `@urql/preact` instead of `urql` and import from
···
### Setting up the `Client`
-
The `urql` and `@urql/preact` packages export a method called `createClient` which we can use to
+
The `urql` and `@urql/preact` packages export a `Client` class, which we can use to
create the GraphQL client. This central `Client` manages all of our GraphQL requests and results.
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql';
+
import { Client, cacheExchange, fetchExchange } from 'urql';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
});
```
-
At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started.
+
At the bare minimum we'll need to pass an API's `url` and `exchanges` when we create a `Client`
+
to get started.
Another common option is `fetchOptions`. This option allows us to customize the options that will be
passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object, or
···
GraphQL API.
```js
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
fetchOptions: () => {
const token = getToken();
return {
···
the `Provider` export.
```jsx
-
import { createClient, Provider, dedupExchange, cacheExchange, fetchExchange } from 'urql';
+
import { Client, Provider, cacheExchange, fetchExchange } from 'urql';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
});
const App = () => (
···
todo items. Let's dive right into it!
```jsx
-
import { useQuery } from 'urql';
+
import { gql, useQuery } from 'urql';
-
const TodosQuery = `
+
const TodosQuery = gql`
query {
todos {
id
···
to supply variables to our query.
```jsx
-
const TodosListQuery = `
+
const TodosListQuery = gql`
query ($from: Int!, $limit: Int!) {
-
todos (from: $from, limit: $limit) {
+
todos(from: $from, limit: $limit) {
id
title
}
···
```jsx
const Todos = ({ from, limit }) => {
-
const shouldPause = from === undefined || from === null ||
-
limit === undefined || limit === null;
+
const shouldPause = from === undefined || from === null || limit === undefined || limit === null;
const [result, reexecuteQuery] = useQuery({
query: TodosListQuery,
variables: { from, limit },
···
we may also change the `Client`'s default `requestPolicy` by passing it there.
```js
-
import { createClient } from 'urql';
+
import { Client, cacheExchange, fetchExchange } from 'urql';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
// every operation will by default use cache-and-network rather
// than cache-first now:
requestPolicy: 'cache-and-network',
+17 -16
docs/basics/svelte.md
···
Installing `@urql/svelte` is quick and no other packages are immediately necessary.
```sh
-
yarn add @urql/svelte graphql
+
yarn add @urql/svelte
# or
-
npm install --save @urql/svelte graphql
+
npm install --save @urql/svelte
```
Most libraries related to GraphQL also need the `graphql` package to be installed as a peer
···
### Setting up the `Client`
-
The `@urql/svelte` package exports a method called `createClient` which we can use to create
+
The `@urql/svelte` package exports a `Client` class, which we can use to create
the GraphQL client. This central `Client` manages all of our GraphQL requests and results.
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/svelte';
+
import { Client, cacheExchange, fetchExchange } from '@urql/svelte';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
});
```
-
At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started.
+
At the bare minimum we'll need to pass an API's `url` and `exchanges`
+
when we create a `Client` to get started.
Another common option is `fetchOptions`. This option allows us to customize the options that will be
passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object or
···
GraphQL API.
```js
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
fetchOptions: () => {
const token = getToken();
return {
···
```html
<script>
-
import { createClient, setContextClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/svelte';
+
import { Client, setContextClient, cacheExchange, fetchExchange } from '@urql/svelte';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
});
setContextClient(client);
···
we may also change the `Client`'s default `requestPolicy` by passing it there.
```js
-
import { createClient } from '@urql/svelte';
+
import { Client, cacheExchange, fetchExchange } from '@urql/svelte';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
// every operation will by default use cache-and-network rather
// than cache-first now:
requestPolicy: 'cache-and-network',
···
result = mutationStore({
client,
query: gql`
-
mutation($id: ID!, $title: String!) {
+
mutation ($id: ID!, $title: String!) {
updateTodo(id: $id, title: $title) {
id
title
+6 -5
docs/basics/typescript-integration.md
···
To get and running, install the following packages:
```sh
-
yarn add graphql
-
yarn add -D typescript @graphql-codegen/cli @graphql-codegen/client-preset
+
yarn add -D graphql typescript @graphql-codegen/cli @graphql-codegen/client-preset
# or
-
npm install graphql
-
npm install -D typescript @graphql-codegen/cli @graphql-codegen/client-preset
+
npm install -D graphql typescript @graphql-codegen/cli @graphql-codegen/client-preset
```
Then, add the following script to your `package.json`:
···
function App() {
// `data` is typed!
-
const [{ data }] = useQuery({ query: allFilmsWithVariablesQueryDocument, variables: { first: 10 } });
+
const [{ data }] = useQuery({
+
query: allFilmsWithVariablesQueryDocument,
+
variables: { first: 10 },
+
});
return (
<div className="App">
{data && (
+3 -3
docs/basics/ui-patterns.md
···
else our requests will never get upgraded.
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql';
+
import { Client, cacheExchange, fetchExchange } from 'urql';
import { refocusExchange } from '@urql/exchange-refocus';
-
const client = createClient({
+
const client = new Client({
url: 'some-url',
-
exchanges: [dedupExchange, refocusExchange(), cacheExchange, fetchExchange],
+
exchanges: [refocusExchange(), cacheExchange, fetchExchange],
});
```
+42 -41
docs/basics/vue.md
···
### Setting up the `Client`
-
The `@urql/vue` package exports a method called `createClient` which we can use to create
+
The `@urql/vue` package exports a `Client` class, which we can use to create
the GraphQL client. This central `Client` manages all of our GraphQL requests and results.
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/vue';
+
import { Client, cacheExchange, fetchExchange } from '@urql/vue';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
});
```
-
At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started.
+
At the bare minimum we'll need to pass an API's `url` and `exchanges` when we create a `Client`
+
to get started.
Another common option is `fetchOptions`. This option allows us to customize the options that will be
passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object or
···
GraphQL API.
```js
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
fetchOptions: () => {
const token = getToken();
return {
···
The first method is to use `@urql/vue`'s `provideClient` function. This must be called in any of
your parent components and accepts either a `Client` directly or just the options that you'd pass to
-
`createClient`.
+
`Client`.
```html
<script>
-
import { createClient, provideClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/vue';
+
import { Client, provideClient, cacheExchange, fetchExchange } from '@urql/vue';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
});
provideClient(client);
···
app.mount('#app');
```
-
The plugin also accepts `createClient`'s options or a `Client` as its inputs.
+
The plugin also accepts `Client`'s options or a `Client` as its inputs.
## Queries
···
</template>
<script>
-
import { useQuery } from '@urql/vue';
+
import { gql, useQuery } from '@urql/vue';
export default {
setup() {
const result = useQuery({
-
query: `
+
query: gql`
{
todos {
id
···
</template>
<script>
-
import { useQuery } from '@urql/vue';
+
import { gql, useQuery } from '@urql/vue';
export default {
props: ['from', 'limit'],
setup({ from, limit }) {
return useQuery({
-
query: `
+
query: gql`
query ($from: Int!, $limit: Int!) {
todos(from: $from, limit: $limit) {
id
···
</template>
<script>
-
import { useQuery } from '@urql/vue';
+
import { gql, useQuery } from '@urql/vue';
export default {
setup() {
const from = ref(0);
const result = useQuery({
-
query: `
+
query: gql`
query ($from: Int!, $limit: Int!) {
todos(from: $from, limit: $limit) {
id
···
```js
import { reactive } from 'vue'
-
import { useQuery } from '@urql/vue';
+
import { gql, useQuery } from '@urql/vue';
export default {
props: ['from', 'limit'],
setup({ from, limit }) {
const shouldPause = computed(() => from == null || limit == null);
return useQuery({
-
query: `
+
query: gql`
query ($from: Int!, $limit: Int!) {
todos(from: $from, limit: $limit) {
id
···
</template>
<script>
-
import { useQuery } from '@urql/vue';
+
import { gql, useQuery } from '@urql/vue';
export default {
setup() {
return useQuery({
-
query: `
+
query: gql`
{
todos {
id
···
we may also change the `Client`'s default `requestPolicy` by passing it there.
```js
-
import { createClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/vue';
+
import { Client, cacheExchange, fetchExchange } from '@urql/vue';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange, fetchExchange],
+
exchanges: [cacheExchange, fetchExchange],
// every operation will by default use cache-and-network rather
// than cache-first now:
requestPolicy: 'cache-and-network',
···
`requestPolicy` of our query just once and set it to `network-only` to skip the cache.
```js
-
import { useQuery } from '@urql/vue';
+
import { gql, useQuery } from '@urql/vue';
export default {
setup() {
const result = useQuery({
-
query: `
+
query: gql`
{
todos {
id
···
</template>
<script>
-
import { useQuery } from '@urql/vue';
+
import { gql, useQuery } from '@urql/vue';
export default {
async setup() {
const { data, error } = await useQuery({
-
query: `
+
query: gql`
{
todos {
id
···
example! We'll set up a mutation that _updates_ a todo item's title.
```js
-
import { useMutation } from '@urql/vue';
+
import { gql, useMutation } from '@urql/vue';
export default {
setup() {
-
const { executeMutation: updateTodo } = useMutation(`
-
mutation ($id: ID!, $title: String!) {
-
updateTodo (id: $id, title: $title) {
+
const { executeMutation: updateTodo } = useMutation(gql`
+
mutation($id: ID!, $title: String!) {
+
updateTodo(id: $id, title: $title) {
id
title
}
···
`executeMutation` method returns when it's called:
```js
-
import { useMutation } from '@urql/vue';
+
import { gql, useMutation } from '@urql/vue';
export default {
setup() {
-
const updateTodoResult = useMutation(`
-
mutation ($id: ID!, $title: String!) {
-
updateTodo (id: $id, title: $title) {
+
const updateTodoResult = useMutation(gql`
+
mutation($id: ID!, $title: String!) {
+
updateTodo(id: $id, title: $title) {
id
title
}
···
[Read more about errors on our "Errors" page.](./errors.md)
```js
-
import { useMutation } from '@urql/vue';
+
import { gql, useMutation } from '@urql/vue';
export default {
setup() {
-
const updateTodoResult = useMutation(`
-
mutation ($id: ID!, $title: String!) {
-
updateTodo (id: $id, title: $title) {
+
const updateTodoResult = useMutation(gql`
+
mutation($id: ID!, $title: String!) {
+
updateTodo(id: $id, title: $title) {
id
title
}
+46 -47
docs/comparison.md
···
### Core Features
-
| | urql | Apollo | Relay |
-
| ------------------------------------------ | ---------------------------------- | -------------------------------------------------------------------------- | ------------------------------ |
-
| Extensible on a network level | ✅ Exchanges | ✅ Links | ✅ Network Layers |
-
| Extensible on a cache / control flow level | ✅ Exchanges | 🛑 | 🛑 |
-
| Base Bundle Size | **5.9kB** (7.1kB with bindings) | 32.9kB | 27.7kB (34.1kB with bindings) |
-
| Devtools | ✅ | ✅ | ✅ |
-
| Subscriptions | ✅ | ✅ | ✅ |
-
| Client-side Rehydration | ✅ | ✅ | ✅ |
-
| Polled Queries | 🔶 | ✅ | ✅ |
-
| Lazy Queries | ✅ | ✅ | ✅ |
-
| Stale while Revalidate / Cache and Network | ✅ | ✅ | ✅ |
-
| Focus Refetching | ✅ `@urql/exchange-refocus` | 🛑 | 🛑 |
-
| Stale Time Configuration | ✅ `@urql/exchange-request-policy` | ✅ | 🛑 |
-
| Persisted Queries | ✅ `@urql/exchange-persisted` | ✅ `apollo-link-persisted-queries` | ✅ |
-
| Batched Queries | 🛑 | ✅ `apollo-link-batch-http` | 🟡 `react-relay-network-layer` |
-
| Live Queries | 🛑 | 🛑 | ✅ |
-
| Defer & Stream Directives | ✅ | ✅ / 🛑 (`@defer` is supported in >=3.7.0, `@stream` is not yet supported) | 🟡 (unreleased) |
-
| Switching to `GET` method | ✅ | ✅ | 🟡 `react-relay-network-layer` |
-
| File Uploads | ✅ | 🟡 `apollo-upload-client` | 🛑 |
-
| Retrying Failed Queries | ✅ `@urql/exchange-retry` | ✅ `apollo-link-retry` | ✅ `DefaultNetworkLayer` |
-
| Easy Authentication Flows | ✅ `@urql/exchange-auth` | 🛑 (no docs for refresh-based authentication) | 🟡 `react-relay-network-layer` |
-
| Automatic Refetch after Mutation | ✅ (with document cache) | 🛑 | ✅ |
+
| | urql | Apollo | Relay |
+
| ------------------------------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
+
| Extensible on a network level | ✅ Exchanges | ✅ Links | ✅ Network Layers |
+
| Extensible on a cache / control flow level | ✅ Exchanges | 🛑 | 🛑 |
+
| Base Bundle Size | **10kB** (11kB with bindings) | ~50kB (55kB with React hooks) | 45kB (66kB with bindings) |
+
| Devtools | ✅ | ✅ | ✅ |
+
| Subscriptions | 🔶 [Docs](./advanced/subscriptions.md) | 🔶 [Docs](https://www.apollographql.com/docs/react/data/subscriptions/#setting-up-the-transport) | 🔶 [Docs](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer) |
+
| Client-side Rehydration | ✅ [Docs](./advanced/server-side-rendering.md) | ✅ [Docs](https://www.apollographql.com/docs/react/performance/server-side-rendering) | 🛑 |
+
| Polled Queries | 🔶 | ✅ | ✅ |
+
| Lazy Queries | ✅ | ✅ | ✅ |
+
| Stale while Revalidate / Cache and Network | ✅ | ✅ | ✅ |
+
| Focus Refetching | ✅ `@urql/exchange-refocus` | 🛑 | 🛑 |
+
| Stale Time Configuration | ✅ `@urql/exchange-request-policy` | ✅ | 🛑 |
+
| Persisted Queries | ✅ `@urql/exchange-persisted` | ✅ `apollo-link-persisted-queries` | 🔶 |
+
| Batched Queries | 🛑 | ✅ `apollo-link-batch-http` | 🟡 `react-relay-network-layer` |
+
| Live Queries | ✅ (via Incremental Delivery) | 🛑 | ✅ |
+
| Defer & Stream Directives | ✅ | ✅ / 🛑 (`@defer` is supported in >=3.7.0, `@stream` is not yet supported) | 🟡 (unreleased) |
+
| Switching to `GET` method | ✅ | ✅ | 🟡 `react-relay-network-layer` |
+
| File Uploads | ✅ | 🟡 `apollo-upload-client` | 🛑 |
+
| Retrying Failed Queries | ✅ `@urql/exchange-retry` | ✅ `apollo-link-retry` | ✅ `DefaultNetworkLayer` |
+
| Easy Authentication Flows | ✅ `@urql/exchange-auth` | 🛑 (no docs for refresh-based authentication) | 🟡 `react-relay-network-layer` |
+
| Automatic Refetch after Mutation | ✅ (with document cache) | 🛑 | ✅ |
Typically these are all additional addon features that you may expect from a GraphQL client, no
matter which framework you use it with. It's worth mentioning that all three clients support some
···
| | urql | Apollo | Relay |
| ------------------------------ | -------------- | ------------------- | ------------------ |
| React Bindings | ✅ | ✅ | ✅ |
-
| React Concurrent Hooks Support | ✅ | 🛑 | ✅ (experimental) |
-
| React Legacy Hooks Support | ✅ | ✅ | 🟡 `relay-hooks` |
-
| React Suspense (Experimental) | ✅ | 🛑 | ✅ |
+
| React Concurrent Hooks Support | ✅ | ✅ | ✅ |
+
| React Suspense | ✅ | 🛑 | ✅ |
| Next.js Integration | ✅ `next-urql` | 🟡 | 🔶 |
| Preact Support | ✅ | 🔶 | 🔶 |
| Svelte Bindings | ✅ | 🟡 `svelte-apollo` | 🟡 `svelte-relay` |
···
### Caching and State
-
| | urql | Apollo | Relay |
-
| ------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------- | ---------------------------------------------- |
-
| Caching Strategy | Document Caching, Normalized Caching with `@urql/exchange-graphcache` | Normalized Caching | Normalized Caching (schema restrictions apply) |
-
| Added Bundle Size | +6.5kB (with Graphcache) | +0 (default) | +0 (default) |
-
| Automatic Garbage Collection | ✅ | 🔶 | ✅ |
-
| Local State Management | 🛑 | ✅ | ✅ |
-
| Pagination Support | 🔶 | 🔶 | ✅ |
-
| Optimistic Updates | ✅ | ✅ | ✅ |
-
| Local Updates | ✅ | ✅ | ✅ |
-
| Out-of-band Cache Updates | 🛑 (stays true to server data) | ✅ | ✅ |
-
| Local Resolvers and Redirects | ✅ | ✅ | 🛑 (not needed) |
-
| Complex Resolvers (nested non-normalized return values) | ✅ | 🛑 | 🛑 (not needed) |
-
| Commutativity Guarantees | ✅ | 🛑 | ✅ |
-
| Partial Results | ✅ | ✅ | 🛑 |
-
| Safe Partial Results (schema-based) | ✅ | 🛑 | 🛑 |
-
| Persistence Support | ✅ | ✅ `apollo-cache-persist` | 🟡 `@wora/relay-store` |
-
| Offline Support | ✅ | 🛑 | 🟡 `@wora/relay-offline` |
+
| | urql | Apollo | Relay |
+
| ------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------- | ---------------------------------------------- |
+
| Caching Strategy | Document Caching, Normalized Caching with `@urql/exchange-graphcache` | Normalized Caching | Normalized Caching (schema restrictions apply) |
+
| Added Bundle Size | +8kB (with Graphcache) | +0 (default) | +0 (default) |
+
| Automatic Garbage Collection | ✅ | 🔶 | ✅ |
+
| Local State Management | 🛑 | ✅ | ✅ |
+
| Pagination Support | 🔶 | 🔶 | ✅ |
+
| Optimistic Updates | ✅ | ✅ | ✅ |
+
| Local Updates | ✅ | ✅ | ✅ |
+
| Out-of-band Cache Updates | 🛑 (stays true to server data) | ✅ | ✅ |
+
| Local Resolvers and Redirects | ✅ | ✅ | 🛑 |
+
| Complex Resolvers (nested non-normalized return values) | ✅ | 🛑 | 🛑 |
+
| Commutativity Guarantees | ✅ | 🛑 | ✅ |
+
| Partial Results | ✅ | ✅ | 🛑 |
+
| Safe Partial Results (schema-based) | ✅ | 🔶 (experimental via `useFragment`) | 🛑 |
+
| Persistence Support | ✅ | ✅ `apollo-cache-persist` | 🟡 `@wora/relay-store` |
+
| Offline Support | ✅ | 🛑 | 🟡 `@wora/relay-offline` |
`urql` is the only of the three clients that doesn't pick [normalized
caching](./graphcache/normalized-caching.md) as its default caching strategy. Typically this is seen
···
but often this comparison is hard to make. When you start comparing bundle sizes of these three
GraphQL clients you should keep in mind that:
-
- Parts of the `graphql` package tree-shake away and may also be replaced (e.g. `parse`)
-
- All packages in `urql` reuse parts of `@urql/core` and `wonka`, which means adding all their total
+
- Some dependencies may be external and the above sizes listed are total minified+gzipped sizes
+
- `@urql/core` imports from `wonka` for stream utilities and `@0no-co/graphql.web` for GraphQL query
+
language utilities
+
- Other GraphQL clients may import other exernal dependencies.
+
- All `urql` packages reuse parts of `@urql/core` and `wonka`, which means adding all their total
sizes up doesn't give you a correct result of their total expected bundle size.
-
- These sizes may change drastically given the code you write and add yourself, but can be managed
-
via precompilation (e.g. with `babel-plugin-graphql-tag` or GraphQL Code Generator for Apollo and
-
`urql`)
+3 -3
docs/graphcache/README.md
···
without passing any options.
```js
-
import { createClient, dedupExchange, fetchExchange } from 'urql';
+
import { Client, fetchExchange } from 'urql';
import { cacheExchange } from '@urql/exchange-graphcache';
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cacheExchange({}), fetchExchange],
+
exchanges: [cacheExchange({}), fetchExchange],
});
```
+6 -6
docs/graphcache/offline.md
···
`offlineExchange` that's also exported by `@urql/exchange-graphcache`.
```js
-
import { createClient, dedupExchange, fetchExchange } from 'urql';
+
import { Client, fetchExchange } from 'urql';
import { offlineExchange } from '@urql/exchange-graphcache';
const cache = offlineExchange({
···
},
});
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cache, fetchExchange],
+
exchanges: [cache, fetchExchange],
});
```
···
function from `@urql/exchange-graphcache/default-storage`.
```js
-
import { createClient, dedupExchange, fetchExchange } from 'urql';
+
import { Client, fetchExchange } from 'urql';
import { offlineExchange } from '@urql/exchange-graphcache';
import { makeDefaultStorage } from '@urql/exchange-graphcache/default-storage';
···
},
});
-
const client = createClient({
+
const client = new Client({
url: 'http://localhost:3000/graphql',
-
exchanges: [dedupExchange, cache, fetchExchange],
+
exchanges: [cache, fetchExchange],
});
```