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

(docs) - Replace "Main Concepts" section with "Architecture" and "Authoring Exchanges" pages (#1418)

* Move "Document Caching" and "Errors" to "Basics"

* Add new "Architecture" page

* Move "Exchanges" page to "Advanced"

* Remove "Main Concepts" section

* Apply suggestions from code review

Co-authored-by: Will Golledge <35961363+wgolledge@users.noreply.github.com>

Co-authored-by: Will Golledge <35961363+wgolledge@users.noreply.github.com>

+4 -5
docs/README.md
···
customized GraphQL infrastructure. In short, `urql` prioritizes usability and adaptability.
As you're adopting GraphQL, `urql` becomes your primary data layer and can handle content-heavy
-
pages through ["Document Caching"](./concepts/document-caching.md) as well as dynamic and data-heavy
+
pages through ["Document Caching"](./basics/document-caching.md) as well as dynamic and data-heavy
apps through ["Normalized Caching"](./graphcache/normalized-caching.md).
`urql` can be understood as a collection of connected parts and packages.
···
`@urql/core`](./basics/core.md), which we can imagine as the brain
of `urql` with most of its logic. As we progress with implementing `urql` into our application,
we're later able to extend it by adding ["addon packages", which we call
-
_Exchanges_](./concepts/exchanges.md)
+
_Exchanges_](./advanced/authoring-exchanges.md)
If at this point you're still unsure of whether to use `urql`, [have a look at the **Comparison**
page](./comparison.md) and check whether `urql` supports all features you're looking for.
···
- **Basics** is the section where we'll want to start learning about `urql` as it contains "Getting
Started" guides for our framework of choice.
-
- **Main Concepts** then explains more about how `urql` functions, what it's made up of, and covers
-
the main aspects of the `Client` and GraphQL clients in general, on the ["Philosophy"
-
page](./concepts/philosophy.md).
+
- **Architecture** then explains more about how `urql` functions, what it's made up of, and covers
+
the main aspects of the `Client` and exchanges.
- **Advanced** covers all more uncommon use-cases and contains guides that we won't need immediately
when we get started with `urql`.
- **Graphcache** documents one of the most important addons to `urql`, which adds ["Normalized
+5 -2
docs/advanced/README.md
···
# Advanced
In this chapter we'll dive into various topics of "advanced" `urql` usage. This is admittedly a
-
catch-all chapter of various use-cases that can only be covered after [the "Concepts"
-
chapter.](../concepts/README.md)
+
catch-all chapter of various use-cases that can only be covered after [the "Architecture"
+
chapter.](../architecture.md)
- [**Subscriptions**](./subscriptions.md) covers how to use `useSubscription` and how to set up GraphQL subscriptions with
`urql`.
···
- [**Retrying operations**](./retry-operations.md) shows the `retryExchange` which allows you to retry operations when they've failed.
- [**Authentication**](./authentication.md) describes how to implement authentication using the `authExchange`
- [**Testing**](./testing.md) covers how to test components that use `urql` particularly in React.
+
- [**Authoring Exchanges**](./authoring-exchanges.md) describes how to implement exchanges from
+
scratch and how they work internally. This is a good basis to understanding how some of the
+
features in this section function.
- [**Auto-populate Mutations**](./auto-populate-mutations.md) presents the `populateExchange` addon which can make it easier to
update normalized data after mutations.
+1 -1
docs/advanced/auto-populate-mutations.md
···
---
title: Auto-populate Mutations
-
order: 8
+
order: 9
---
# Automatically populating Mutations
+2 -2
docs/advanced/debugging.md
···
As demonstrated above, the `client.subscribeToDebugTarget` accepts a callback function and returns
a subscription with an `unsubscribe` method. We've seen this pattern in the prior ["Stream Patterns"
-
section.](../concepts/stream-patterns.md#the-wonka-library)
+
section on the "Architecture" page.](../architecture.md)
## Adding your own Debug Events
···
#### Dispatching an event
-
In the ["Exchanges" section](../concepts/exchanges.md) we've learned about the [`ExchangeInput`
+
[On the "Authoring Exchanges" page](./authoring-exchanges.md) we've learned about the [`ExchangeInput`
object](../api/core.md#exchangeinput), which comes with a `client` and a `forward` property.
It also contains a `dispatchDebug` property.
+7 -3
docs/advanced/subscriptions.md
···
});
```
-
[Read more about _Exchanges_ and how they work on the "Exchanges" page.](../concepts/exchanges.md)
+
Read more about Exchanges and how they work [on the "Authoring Exchanges"
+
page.](./authoring-exchanges.md) or what they are [on the "Architecture"
+
page.](../architecture.md)
In the above example, we add the `subscriptionExchange` to the `Client` with the default exchanges
add before it. The `subscriptionExchange` is a factory that accepts additional options and returns
···
## One-off Subscriptions
-
When you're using subscriptions directly without `urql`'s framework bindings, you can use the `Client`'s `subscription` method for one-off subscriptions. This method is similar to the ones for mutations and subscriptions [that we've seen before on the "Core Package" page.](../concepts/core-package.md#one-off-queries-and-mutations)
+
When you're using subscriptions directly without `urql`'s framework bindings, you can use the
+
`Client`'s `subscription` method for one-off subscriptions. This method is similar to the ones for
+
mutations and subscriptions [that we've seen before on the "Core Package" page.](../basics/core.md)
-
This method will always [returns a Wonka stream](../concepts/stream-patterns.md#the-wonka-library) and doesn't have a `.toPromise()` shortcut method, since promises won't return the multiple values that a subscription may deliver. Let's convert the above example to one without framework code, as we may use subscriptions in a Node.js environment.
+
This method will always [returns a Wonka stream](../architecture.md#the-wonka-library) and doesn't have a `.toPromise()` shortcut method, since promises won't return the multiple values that a subscription may deliver. Let's convert the above example to one without framework code, as we may use subscriptions in a Node.js environment.
```js
import { pipe, subscribe } from 'wonka';
+4 -1
docs/advanced/testing.md
···
- `useMutation` calls `executeMutation`
- `useSubscription` calls `executeSubscription`
-
In the section [Stream Patterns](../concepts/stream-patterns.md) we've seen, that all methods on the client operate with and return streams. These streams are created using the [Wonka](../concepts/stream-patterns.md#the-wonka-library) library and we're able to create streams ourselves to mock the different states of our operations, e.g. fetching, errors, or success with data.
+
In the section ["Stream Patterns" on the "Architecture" page](../architecture.md) we've seen, that
+
all methods on the client operate with and return streams. These streams are created using
+
[the Wonka library](../architecture.md#the-wonka-library) and we're able to create streams
+
ourselves to mock the different states of our operations, e.g. fetching, errors, or success with data.
You'll probably use one of these utility functions to create streams:
+1 -1
docs/api/README.md
···
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 Package" page.](../concepts/core-package.md)
+
more about the core package on the "Core" page.](../basics/core.md)
- [`@urql/core` API docs](./core.md)
- [`urql` React API docs](./urql.md)
+9 -9
docs/api/core.md
···
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.](../concepts/core-package.md)
+
[Read more about `urql`'s core on the "Core Package" page.](../basics/core.md)
## Client
···
```
[Read more about how to use this API on the "Core Package"
-
page.](../concepts/core-package.md#one-off-queries-and-mutations)
+
page.](../basics/core.md#one-off-queries-and-mutations)
### client.mutation
This is similar to [`client.query`](#clientquery), but dispatches mutations instead.
[Read more about how to use this API on the "Core Package"
-
page.](../concepts/core-package.md#one-off-queries-and-mutations)
+
page.](../basics/core.md#one-off-queries-and-mutations)
### client.subscription
···
| `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.](../concepts/errors.md)
+
[Read more about errors in `urql` on the "Error" page.](../basics/errors.md)
## Types
···
- `'network-only'`
- `'cache-and-network'`
-
[Read more about request policies on the "Document Caching" page.](../concepts/document-caching.md#request-policies)
+
[Read more about request policies on the "Document Caching" page.](../basics/document-caching.md#request-policies)
### OperationContext
···
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 "Exchanges" page.](../concepts/exchanges.md)
+
[Read more about _Exchanges_ on the "Authoring Exchanges" page.](../advanced/authoring-exchanges.md)
An exchange is defined to be a function that receives [`ExchangeInput`](#exchangeinput) and returns
an `ExchangeIO` function. The `ExchangeIO` function in turn will receive a stream of operations, and
···
type Exchange = ExchangeInput => ExchangeIO;
```
-
[If you haven't yet seen `Source`, read more about "Stream
-
Patterns".](../concepts/stream-patterns.md)
+
[If you haven't yet seen streams you can read more about "Stream Patterns" on the "Architecture"
+
page.](../architecture.md)
## Exchanges
### cacheExchange
-
The `cacheExchange` as [described on the "Document Caching" page.](../concepts/document-caching.md). It's of type `Exchange`.
+
The `cacheExchange` as [described on the "Document Caching" page.](../basics/document-caching.md). It's of type `Exchange`.
### subscriptionExchange
+1 -1
docs/api/request-policy-exchange.md
···
that may be used to upgrade [Operations' Request Policies](./core.md#requestpolicy) on a
time-to-live basis.
-
[Read more about request policies on the "Document Caching" page.](../concepts/document-caching.md#request-policies)
+
[Read more about request policies on the "Document Caching" page.](../basics/document-caching.md#request-policies)
This exchange will conditionally upgrade `cache-first` and `cache-only` operations to use
`cache-and-network`, so that the client gets an opportunity to update its cached data, when the
+245
docs/architecture.md
···
+
---
+
title: Architecture
+
order: 3
+
---
+
+
# Architecture
+
+
`urql` is a highly customizable and flexible GraphQL client, that happens to come with some default
+
[core behavior in the core package](./core-package.md).
+
+
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
+
that grows with our usage and demands. As we go from building our smallest or first GraphQL apps to
+
utilising its full functionality, we have tools at our disposal to extend and customize `urql` to
+
our liking.
+
+
## Using GraphQL Clients
+
+
You may have worked with a GraphQL API previously and noticed that using GraphQL in your app can be
+
as straightforward as sending a plain HTTP request with your query to fetch some data.
+
+
GraphQL also provides an opportunity to abstract away a lot of the manual work that goes with
+
sending these queries and managing the data. Ultimately, this lets you focus on building
+
your app without having to handle the technical details of state management in detail.
+
+
Specifically, `urql` simplifies three common aspects of using GraphQL:
+
+
- Sending queries and mutations and receiving results _declaratively_
+
- Abstracting _caching_ and state management internally
+
- Providing a central point of _extensibility_ and integration with your API
+
+
In the following sections we'll talk about the way that `urql` solves these three problems and how the logic abstracted away internally.
+
+
## 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).
+
+
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.
+
+
```js
+
import { Client } from '@urql/core';
+
+
new Client({
+
url: '/graphql',
+
requestPolicy: 'cache-first',
+
})
+
```
+
+
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"
+
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`s.](./api/core.md#operation). 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.
+
+
![Operations and Results](./assets/urql-event-hub.png)
+
+
It's the `Client`s responsibility to accept an `Operation` and execute it. The bindings interally
+
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.
+
+
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
+
of the `Client`, they're what travel through the "Exchanges" on the `Client`.
+
+
## 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.
+
+
![Operations stream and results stream](./assets/urql-client-architecture.png)
+
+
While we know that, for us, we're only interested in a single [`Operation`](./api/core.md#operation)
+
and its [`OperationResult`s](./api/core.md#operationresult) at a time, the `Client` treats these as
+
one big stream. The `Client` sees an incoming flow of all of our operations.
+
+
As we've learned before, each operation carries a `key` and each result we receive carries the
+
original `operation`. Because an `OperationResult` also carries an `operation` property the `Client`
+
will always know which results correspond to an individual operation.
+
However, internally, all of our operations are processed at the same time concurrently. However from
+
our perspective:
+
+
- We subscribe to a "stream" and expect to get results on a callback
+
- The `Client` issues the operation and we'll receive some results back eventually as either the
+
cache responds (synchronously) or the request gets sent to our API.
+
- We eventually unsubscribe and the `Client` issues a "teardown" operation with the same `key` as
+
the original operation, which concludes our flow.
+
+
The `Client` itself doesn't actually know what to do with operations. Instead, it sends them through
+
"exchanges". Exchanges are akin to [middleware in Redux](https://redux.js.org/advanced/middleware)
+
and have access to all operations and all results. Multiple exchanges are chained to process our
+
operations and to execute logic on them, one of them being the `fetchExchange`, which as the name
+
implies sends our requests to our API.
+
+
### How operations get to exchanges
+
+
We now know how we get to operations and to the `Client`:
+
+
- Any bindings or calls to the `Client` create an **operation**
+
- This operation identifies itself as either a `"query"`, `"mutation"` or `"subscription"` and has a
+
unique `key`.
+
- This operation is sent into the **exchanges** and eventually ends up at the `fetchExchange`
+
(or a similar exchange)
+
- The operation is sent to the API and a **result** comes back which is wrapped in an `OperationResult`
+
- The `Client` filters the `OperationResult` by the `operation.key` and — via a callback — gives us
+
a **stream of results**.
+
+
To come back to our train analogy from earlier, an operation, like a train, travels from one end
+
of the track to the terminus — our API. The results then come back on the same path as they're just
+
travelling the same line in reverse.
+
+
### The Exchanges
+
+
The default set of exchanges that `@urql/core` contains and applies to a `Client` are:
+
+
- `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
+
+
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 exters 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.
+
+
Some of the exchanges that are available to us are:
+
+
- [`errorExchange`](./api/core.md#errorexchange): Allows a global callback to be called when any error occurs
+
- [`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
+
- [`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.
+
- `devtoolsExchange`: Provides the ability to use the [urql-devtools](https://github.com/FormidableLabs/urql-devtools)
+
+
We can even swap out our [document cache](./basics/document-caching.md), which is implemented by
+
`@urql/core`'s `cacheExchange`, with `urql`'s [normalized cache,
+
Graphcache](./graphcache/README.md).
+
+
[Read more about exchanges and how to write them from scratch on the "Authoring Exchanges"
+
page.](./advanced/authoring-exchanges.md)
+
+
## Stream Patterns in `urql`
+
+
In the previous sections we've learned a lot about how the `Client` works, but we've always learned
+
it in vague terms — for instance, we've learned that we get a "stream of results" or `urql` sees all
+
operations as "one stream of operations" that it sends to the exchanges.
+
But, **what are streams?**
+
+
Generally we refer to _streams_ as abstractions that allow us to program with asynchronous events
+
over time. Within the JavaScript context we're thinking specifically in terms of of
+
[Observables](https://github.com/tc39/proposal-observable)
+
and [Reactive Programming with Observables.](http://reactivex.io/documentation/observable.html)
+
These concepts may sound initimidating, but from a high-level view what we're talking about can be
+
thought of as a combination of promises and iterables (e.g. arrays). We're dealing with multiple
+
events but our callback is called over time. It's like calling `forEach` on an array but expecting
+
the results to come in asynchronously.
+
+
As a user, if we're using the one framework bindings that we've seen in [the "Basics"
+
section](./basics/README.md), we may never see these streams in action or may never use them even,
+
since the bindings internally use them for us. But if we [use the `Client`
+
directly](./basics/core.md#one-off-queries-and-mutations) or write exchanges then we'll see streams
+
and wil 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.
+
+
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.
+
+
```js
+
import { pipe, subscribe } from 'wonka';
+
+
const QUERY = `
+
query Test($id: ID!) {
+
getUser(id: $id) {
+
id
+
name
+
}
+
}
+
`;
+
+
const { unsubscribe } = pipe(
+
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).
+4 -5
docs/basics/README.md
···
- [**Core Package**](./core-package.md) defines why a shared package exists that contains the main
logic of `urql`, and how we can use it directly in Node.js.
-
After reading "Basics" you may want to [read the "Concepts" chapter of the
-
documentation](../concepts/README.md) as it explains the motivation and architecture that drives
-
`urql`. Two notable sections there include:
+
After reading the page for your bindings and the "Core" page you may want to the next two pages in
+
this section of the documentation:
-
- [**Document Caching**](../concepts/document-caching.md) explains the default cache mechanism of `urql`, as opposed to the opt-in
+
- [**Document Caching**](./document-caching.md) explains the default cache mechanism of `urql`, as opposed to the opt-in
[Normalized Cache](../graphcache/normalized-caching.md).
-
- [**Errors**](../concepts/errors.md) contains information on error handling in `urql`.
+
- [**Errors**](../basics/errors.md) contains information on error handling in `urql`.
+5 -5
docs/basics/core.md
···
---
-
title: Core
+
title: Core Usage
order: 3
---
···
This concludes the introduction for using `@urql/core` without any framework bindings. This showed
just a couple of ways to use `gql` or the `Client`, however you may also want to learn more about
-
[how to use `urql`'s streams](../concepts/stream-patterns.md). Furthermore, apart from the framework
+
[how to use `urql`'s streams](../architecture.md#stream-patterns-in-urql). Furthermore, apart from the framework
binding introductions, there are some other pages that provide more information on how to get fully
set up with `urql`:
-
- [How does the default "document cache" work?](../concepts/document-caching.md)
-
- [How are errors handled and represented?](../concepts/errors.md)
-
- [A quick overview of `urql`'s philosophy and structure.](../concepts/philosophy.md)
+
- [How does the default "document cache" work?](./document-caching.md)
+
- [How are errors handled and represented?](./errors.md)
+
- [A quick overview of `urql`'s architecture and structure.](../architecture.md)
- [Setting up other features, like authentication, uploads, or persisted queries.](../advanced/README.md)
+7 -7
docs/basics/react-preact.md
···
---
-
title: React/Preact
+
title: React/Preact Bindings
order: 0
---
···
The result object contains several properties. The `fetching` field indicates whether we're currently
loading data, `data` contains the actual `data` from the API's result, and `error` is set when either
the request to the API has failed or when our API result contained some `GraphQLError`s, which
-
we'll get into later on the ["Errors" page](../concepts/errors.md).
+
we'll get into later on the ["Errors" page](./errors.md).
### Variables
···
Request policies aren't specific to `urql`'s React API, but are a common feature in its core. [You
can learn more about how the cache behaves given the four different policies on the "Document
-
Caching" page.](../concepts/document-caching.md)
+
Caching" page.](../basics/document-caching.md)
### Reexecuting Queries
···
If you're checking for errors, you should use `result.error` instead, which will be set
to a `CombinedError` when any kind of errors occurred while executing your mutation.
-
[Read more about errors on our "Errors" page.](../concepts/errors.md)
+
[Read more about errors on our "Errors" page.](./errors.md)
```jsx
const Todo = ({ id, title }) => {
···
which is the same between all framework bindings. Hence, next we may want to learn more about one of
the following to learn more about the internals:
-
- [How does the default "document cache" work?](../concepts/document-caching.md)
-
- [How are errors handled and represented?](../concepts/errors.md)
-
- [A quick overview of `urql`'s philosophy and structure.](../concepts/philosophy.md)
+
- [How does the default "document cache" work?](./document-caching.md)
+
- [How are errors handled and represented?](./errors.md)
+
- [A quick overview of `urql`'s architecture and structure.](../architecture.md)
- [Setting up other features, like authentication, uploads, or persisted queries.](../advanced/README.md)
+6 -6
docs/basics/svelte.md
···
---
-
title: Svelte
+
title: Svelte Bindings
order: 2
---
···
This "Getting Started" guide covers how to install and set up `urql` and provide a `Client` for
Svelte. The `@urql/svelte` package, which provides bindings for Svelte, doesn't fundamentally
function differently from `@urql/preact` or `urql` and uses the same [Core Package and
-
`Client`](../concepts/core-package.md).
+
`Client`](./core.md).
### Installation
···
If you're checking for errors, you should use `operationStore.error` instead, which will be set
to a `CombinedError` when any kind of errors occurred while executing your mutation.
-
[Read more about errors on our "Errors" page.](../concepts/errors.md)
+
[Read more about errors on our "Errors" page.](./errors.md)
```jsx
mutateTodo({ id, title: newTitle }).then(result => {
···
which is the same between all framework bindings. Hence, next we may want to learn more about one of
the following to learn more about the internals:
-
- [How does the default "document cache" work?](../concepts/document-caching.md)
-
- [How are errors handled and represented?](../concepts/errors.md)
-
- [A quick overview of `urql`'s philosophy and structure.](../concepts/philosophy.md)
+
- [How does the default "document cache" work?](./document-caching.md)
+
- [How are errors handled and represented?](./errors.md)
+
- [A quick overview of `urql`'s architecture and structure.](../architecture.md)
- [Setting up other features, like authentication, uploads, or persisted queries.](../advanced/README.md)
+6 -6
docs/basics/vue.md
···
---
-
title: Vue
+
title: Vue Bindings
order: 1
---
···
The result object contains several properties. The `fetching` field indicates whether we're currently
loading data, `data` contains the actual `data` from the API's result, and `error` is set when either
the request to the API has failed or when our API result contained some `GraphQLError`s, which
-
we'll get into later on the ["Errors" page](../concepts/errors.md).
+
we'll get into later on the ["Errors" page](./errors.md).
All of these properties on the result are derived from the [shape of
`OperationResult`](../api/core.md#operationresult) and are marked as [reactive
···
If you're checking for errors, you should use `result.error` instead, which will be set
to a `CombinedError` when any kind of errors occurred while executing your mutation.
-
[Read more about errors on our "Errors" page.](../concepts/errors.md)
+
[Read more about errors on our "Errors" page.](./errors.md)
```js
import { useMutation } from '@urql/vue';
···
which is the same between all framework bindings. Hence, next we may want to learn more about one of
the following to learn more about the internals:
-
- [How does the default "document cache" work?](../concepts/document-caching.md)
-
- [How are errors handled and represented?](../concepts/errors.md)
-
- [A quick overview of `urql`'s philosophy and structure.](../concepts/philosophy.md)
+
- [How does the default "document cache" work?](./document-caching.md)
+
- [How are errors handled and represented?](./errors.md)
+
- [A quick overview of `urql`'s architecture and structure.](../architecture.md)
- [Setting up other features, like authentication, uploads, or persisted queries.](../advanced/README.md)
-27
docs/concepts/README.md
···
-
---
-
title: Main Concepts
-
order: 3
-
---
-
-
# Main Concepts
-
-
In this chapter we'll learn about the motivation behind `urql`, the architecture that drives it, the
-
inner workings of the `Client`, and how to write extensions and addons, also known as _Exchanges_.
-
-
Each page goes a little further in explaining a core concept of `urql`.
-
-
- [**Document Caching**](./document-caching.md) explains the default cache mechanism of `urql`, as opposed to the opt-in
-
[Normalized Cache](../graphcache/normalized-caching.md).
-
- [**Errors**](./errors.md) contains information on error handling in `urql`.
-
- [**Philosophy**](./philosophy.md) gives a quick overview of the different aspects of GraphQL clients and `urql` in
-
particular, which shines a light on why you may want to use `urql`.
-
- [**Stream Pattern**](./stream-patterns.md) explains the inner working of `urql`, which is _stream-based_, also known as
-
Observable patterns in JS.
-
- [**Exchanges**](./exchanges.md) finally introduces _Exchanges_ and how to write extensions or addons and use them
-
in `urql`.
-
-
Finally, some _Exchanges_ are covered in different sections of the documentation, like
-
["Subscriptions"](../advanced/subscriptions.md), ["Server-side
-
Rendering"](../advanced/server-side-rendering.md), or ["Normalized
-
Caching"](../graphcache/normalized-caching.md). It's advisable to read this chapter before moving on
-
to using _Exchanges_.
+2 -2
docs/concepts/document-caching.md docs/basics/document-caching.md
···
---
title: Document Caching
-
order: 1
+
order: 4
---
# Document Caching
···
a query and its variables.
The default _document caching_ logic is implemented in the default `cacheExchange`. We'll learn more
-
about ["Exchanges" on a later page.](../concepts/exchanges.md)
+
about ["Exchanges" on the "Architecture" page.](../architecture.md)
## Operation Keys
+1 -1
docs/concepts/errors.md docs/basics/errors.md
···
---
title: Errors
-
order: 2
+
order: 5
---
# Error handling
+11 -32
docs/concepts/exchanges.md docs/advanced/authoring-exchanges.md
···
---
-
title: Exchanges
-
order: 5
+
title: Authoring Exchanges
+
order: 8
---
-
# Exchanges
+
# Exchange Author Guide
-
As we've learned on the [Stream Patterns](./stream-patterns.md) page, `urql`'s `Client` structures
+
As we've learned [on the "Architecture" page](../architecture.md) page, `urql`'s `Client` structures
its data as an event hub. We have an input stream of operations, which are instructions for the
`Client` to provide a result. These results then come from an output stream of operation results.
-
_Exchanges_ are responsible for performing the important transform from the operations (input) stream to the results stream. Exchanges are handler functions that deal with these input and
-
output streams. They're one of `urql`'s key components, and are needed to implement vital pieces of logic such as
-
caching, fetching, deduplicating requests, and more. In other words, Exchanges are handlers that
-
fulfill our GraphQL requests and can change the stream of operations or results.
+
_Exchanges_ are responsible for performing the important transform from the operations (input) stream
+
to the results stream. Exchanges are handler functions that deal with these input and
+
output streams. They're one of `urql`'s key components, and are needed to implement vital pieces of
+
logic such as caching, fetching, deduplicating requests, and more. In other words, Exchanges are
+
handlers that fulfill our GraphQL requests and can change the stream of operations or results.
-
The default set of exchanges that `@urql/core` contains and applies to a `Client` are:
-
-
- `dedupExchange`: Deduplicates pending operations (pending = waiting for a result)
-
- `cacheExchange`: The default caching logic with ["Document Caching"](../concepts/document-caching.md)
-
- `fetchExchange`: Sends an operation to the API using `fetch` and adds results to the output stream
-
-
Other available exchanges:
-
-
- [`errorExchange`](../api/core.md#errorexchange): Allows a global callback to be called when any error occurs
-
- [`ssrExchange`](../advanced/server-side-rendering.md): Allows for a server-side renderer to
-
collect results for client-side rehydration.
-
- [`retryExchange`](../api/retry-exchange.md): Allows operations to be retried
-
- [`multipartFetchExchange`](../api/multipart-fetch-exchange.md): Provides multipart file upload capability
-
- [`persistedFetchExchange`](../api/persisted-fetch-exchange.md): Provides support for Automatic
-
Persisted Queries
-
- [`authExchange`](../api/auth-exchange.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.
-
- `devtoolsExchange`: Provides the ability to use the [urql-devtools](https://github.com/FormidableLabs/urql-devtools)
-
-
It is also possible to apply custom exchanges to override the default logic.
+
In this guide we'll learn more about how exchanges work and how we can write our own exchanges.
## An Exchange Signature
···
[_Operations_](../api/core.md#operation) and returns a source of [_Operation
Results_](../api/core.md#operationresult).
-
- [Read more about streams on the "Stream Patterns" page.](../concepts/stream-patterns.md)
+
- [Read more about streams on the "Architecture" page.](../architecture.md#stream-patterns-in-urql)
- [Read more about the _Exchange_ type signature on the API docs.](../api/core.md#exchange)
## Using Exchanges
-111
docs/concepts/philosophy.md
···
-
---
-
title: Philosophy
-
order: 3
-
---
-
-
# Philosophy
-
-
`urql` is a highly customizable and flexible GraphQL client, that happens to come with some default
-
[core behavior in the core package](./core-package.md).
-
-
By default, we aim to provide features that allow you to build your app quickly with minimal
-
configuration. `urql` is designed to be a client that grows with you. As you go from building your first
-
GraphQL app to a utilising the full functionality, the tools are available to extend and customize `urql` based on
-
your needs.
-
-
In this guide, we will walk through how `urql` is set up internally and how all pieces of the puzzle
-
— the building blocks of `urql` — interact with one another.
-
-
## Hello World
-
-
When you use `urql` you will always create and set up a `Client`. There is a `createClient`
-
convenience helper to do just that.
-
-
```js
-
import { Client } from 'urql';
-
-
const client = new Client({
-
url: 'http://localhost:3000/graphql',
-
});
-
```
-
-
In `urql`, the client is the first step towards managing the complexity of GraphQL automatically.
-
-
## Using GraphQL Clients
-
-
You may have worked with a GraphQL API previously and noticed that using GraphQL in your app can be
-
as straightforward as sending a plain HTTP request with your query to fetch some data.
-
-
GraphQL also provides an opportunity to abstract away a lot of the manual work that goes with
-
sending these queries and managing the data. Ultimately, this lets you focus on building
-
your app without having to handle the technical details of state management in detail.
-
-
Specifically, `urql` simplifies three common aspects of using GraphQL:
-
-
- Sending queries and mutations and receiving results _declaratively_
-
- Abstracting _caching_ and state management internally
-
- Providing a central point of _extensibility_ and integration with your API
-
-
In the following sections we'll talk about how `urql` solves these three problems, and how this is
-
accomplished and abstracted internally.
-
-
## Declarative Queries
-
-
When you implement queries or mutations with `urql` the `Client` will internally manage the
-
lifetime and updates for these _operations_.
-
-
Such an _operation_ may be sent to your GraphQL API and you'll subsequently receive results.
-
When a _cache_ invalidates this result you may receive updated results. When your app
-
stops being interested in results for an _operation_ (e.g. React unmounts your component) then
-
the `Client` knows to _teardown_ the _operation_ and stops requesting new data or sending you
-
results.
-
-
![Operations and Results](../assets/urql-event-hub.png)
-
-
This all happens in the background, allowing you to simply declare that you'd like to have data for a given
-
query.
-
-
## Caching and State
-
-
When we use GraphQL queries and mutations declaratively with `urql`, we expect them to interact
-
and update automatically.
-
-
Furthermore, when we've already received the results from a query, we may not wish to send another request. To solve this, results can be cached in-memory and notifications can be sent to other parts of an app when the results change or
-
are invalidated by mutations/subscriptions.
-
-
GraphQL clients have access to some type information for any GraphQL API and hence can
-
cache the results of queries automatically. In `urql` the `Client` can be extended with several
-
cache implementations; all of them allow you to prevent mixing your declarative query or mutation
-
code with cache-implementation details, as they mostly happen behind the scenes.
-
-
We previously read about the default [Document Caching](../concepts/document-caching.md).
-
-
Some GraphQL clients also resort to caching data in a normalized format. This is similar to
-
[how you may store data in Redux.](https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape/)
-
Using this approach the cache uses more type information to reference parts of the GraphQL only once
-
in the cache and structures it in a graph, which leads to more shared data, and hence more shared
-
updates in your UI!
-
-
[Read more](../graphcache/normalized-caching.md) on how to add Normalized Caching to an app.
-
-
## Extensibility and Integration
-
-
With any kind of API there can be concerns outside of caching and state management. For example,
-
the global behavior or business logic of your application. For instance, you may want to add authentication, retry-logic for failed requests, or a global
-
error handler.
-
-
`urql` introduces the concept of _Exchanges_ in order to abstract the details of how the `Client` interacts with
-
your framework of choice, your app, and your GraphQL API. They are akin to
-
[middleware in Redux](https://redux.js.org/advanced/middleware) and have access to all operations
-
and all results.
-
-
Read more about [Exchanges](./exchanges.md) later on in the documentation.
-
-
All default behavior in the [core package](./core-package.md) is implemented using
-
Exchanges. This is possible as all operations and all results are treated as a stream
-
of events; we call these events "Operations".
-
-
![Operation Signature](../assets/urql-signals.png)
-
-
Thinking about GraphQL queries and results in
-
streams of operations and results allow us to implement complex behaviour in addition to allowing deep customisation over how the operations/results are handled. We'll learn more about this in the next section - [the Core Package](./core-package.md).
-120
docs/concepts/stream-patterns.md
···
-
---
-
title: Stream Patterns
-
order: 4
-
---
-
-
# Stream Patterns
-
-
As we've learned in the previous section on [philosophy](./philosophy.md), `urql`'s main way of handling GraphQL requests
-
is by abstracting them as streams of operations and results.
-
-
## Streams on the Client
-
-
The client abstracts GraphQL requests in a number of ways:
-
-
- as _Operations_
-
- descriptions of the GraphQL request
-
- the query and related variables
-
- additional information that is configured on the `Client`, such as
-
the `url` and `fetchOptions`.
-
-
![Operations stream and results stream](../assets/urql-client-architecture.png)
-
-
Internally the `Client` is an event hub. It defines a stream of operations as inputs, sends them
-
through a layer that will ultimately send GraphQL requests to an API, and then send the corresponding results
-
to another stream.
-
-
As a user working with framework code we never interact with these streams directly, but it's helpful to know that they describe
-
every interaction between the declarative queries we write and the way that `urql` fulfills them.
-
-
## Streams in JavaScript
-
-
Generally we refer to _streams_ as abstractions that allow us to program with asynchronous streams of
-
events over time. Within the JavaScript context we're thinking specifically in terms of of
-
[Observables](https://github.com/tc39/proposal-observable)
-
and [Reactive Programming with Observables.](http://reactivex.io/documentation/observable.html)
-
-
These concepts can be quite intimidating, if you're new to them, but from a high-level view what
-
we're talking about can be thought of as a "combination of Promises and Arrays".
-
Arrays because we're dealing with multiple items, and Promises because these items arrive
-
asynchronously.
-
-
Also most Observable libraries come with a toolkit of helper functions that are similar to the
-
methods on arrays, so you're likely to see `map` and `filter` — amongst other utilities — in those
-
libraries.
-
-
Read [this Gist](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) for a more in-depth
-
explanation.
-
-
## 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).
-
-
## Client Query Streams
-
-
Internally the `Client` has methods that may be used to execute queries, mutations, and
-
subscriptions. These methods typically return `Wonka` streams that, when subscribed to, will
-
emit results for a given query.
-
-
When a result can be retrieved from an in-memory cache, the stream may even emit the result
-
synchronously — rather than asynchronously.
-
-
There are three methods for each type of operation that GraphQL supports;
-
`executeQuery`, `executeMutation`, and `executeSubscription`. All these methods are
-
convenience wrappers around `executeRequestOperation` that create an operation and return a stream.
-
-
There are also convenience wrappers around the "execute" methods that are useful when using `urql`
-
in a Node.js environment. They are the `query`, `mutation`, and `subscription` methods.
-
-
```js
-
import { pipe, subscribe } from 'wonka';
-
-
const QUERY = `
-
query Test($id: ID!) {
-
getUser(id: $id) {
-
id
-
name
-
}
-
}
-
`;
-
-
const { unsubscribe } = pipe(
-
client.query(QUERY, { id: 'test' }),
-
subscribe(result => {
-
console.log(result); // { data: ... }
-
})
-
);
-
```
-
-
All methods on the `Client` internally emit an operation on an "operations stream" and the result
-
for this operation will be filtered out of all results and delivered to your stream.
-
There are several of these convenience methods in `urql` that make it easier to work with the
-
concept of GraphQL operation and result streams.
-
-
Read more about the available APIs on the `Client` in the [Core API docs](../api/core.md).
+3 -3
docs/graphcache/README.md
···
# Graphcache
-
In `urql`, caching is fully configurable via [_Exchanges_](../concepts/exchanges.md) and the default
-
`cacheExchange` in `urql` offers a ["Document Cache"](../concepts/document-caching.md), which is
+
In `urql`, caching is fully configurable via [exchanges](../architecture.md) and the default
+
`cacheExchange` in `urql` offers a ["Document Cache"](../basics/document-caching.md), which is
sufficient for sites that heavily rely and render static content. However as an app grows more
complex it's likely that the data and state that `urql` manages, will also grow more complex and
introduce interdependencies between data.
···
## Features
The following pages introduce different features in _Graphcache_ which together make it a compelling
-
alternative to the standard [document cache](../concepts/document-caching.md) that `urql` uses by
+
alternative to the standard [document cache](../basics/document-caching.md) that `urql` uses by
default.
- 🔁 [**Fully reactive, normalized caching.**](./normalized-caching.md) _Graphcache_ stores data in
+1 -1
docs/graphcache/offline.md
···
offline error — the `offlineExchange` won't deliver the error for this query to avoid it from being
surfaced to the user. This works particularly well in combination with ["Schema
Awareness"](./schema-awareness.md) which will deliver as much of a partial query result as possible.
-
In combination with the [`cache-and-network` request policy](../concepts/document-caching.md#request-policies)
+
In combination with the [`cache-and-network` request policy](../basics/document-caching.md#request-policies)
we can now ensure that we display as much data as possible when the user is offline while still
keeping the cache up-to-date when the user is online.
+24 -4
packages/site/static.config.js
···
redirect: '/docs/basics',
},
{
-
path: '/docs/basics/document-caching',
-
redirect: '/docs/concepts/document-caching',
-
},
-
{
path: '404',
template: require.resolve('./src/screens/404'),
},
···
{
path: '/docs/graphcache/under-the-hood',
redirect: '/docs/graphcache/normalized-caching',
+
},
+
{
+
path: '/docs/concepts/document-caching',
+
redirect: '/docs/basics/document-caching',
+
},
+
{
+
path: '/docs/concepts/errors',
+
redirect: '/docs/basics/errors',
+
},
+
{
+
path: '/docs/concepts',
+
redirect: '/docs/architecture',
+
},
+
{
+
path: '/docs/concepts/stream-patterns',
+
redirect: '/docs/architecture',
+
},
+
{
+
path: '/docs/concepts/philosophy',
+
redirect: '/docs/architecture',
+
},
+
{
+
path: '/docs/concepts/exchanges',
+
redirect: '/docs/advanced/authoring-exchanges',
},
],
};