1---
2title: Authoring Exchanges
3order: 8
4---
5
6# Exchange Author Guide
7
8As we've learned [on the "Architecture" page](../architecture.md) page, `urql`'s `Client` structures
9its data as an event hub. We have an input stream of operations, which are instructions for the
10`Client` to provide a result. These results then come from an output stream of operation results.
11
12_Exchanges_ are responsible for performing the important transform from the operations (input) stream
13to the results stream. Exchanges are handler functions that deal with these input and
14output streams. They're one of `urql`'s key components, and are needed to implement vital pieces of
15logic such as caching, fetching, deduplicating requests, and more. In other words, Exchanges are
16handlers that fulfill our GraphQL requests and can change the stream of operations or results.
17
18In this guide we'll learn more about how exchanges work and how we can write our own exchanges.
19
20## An Exchange Signature
21
22Exchanges are akin to [middleware in
23Redux](https://redux.js.org/advanced/middleware) due to the way that they apply transforms.
24
25```ts
26import { Client, Operation, OperationResult } from '@urql/core';
27
28type ExchangeInput = { forward: ExchangeIO; client: Client };
29type Exchange = (input: ExchangeInput) => ExchangeIO;
30type ExchangeIO = (ops$: Source<Operation>) => Source<OperationResult>;
31```
32
33The first parameter to an exchange is a `forward` function that refers to the next Exchange in the
34chain. The second second parameter is the `Client` being used. Exchanges always return an `ExchangeIO`
35function (this applies to the `forward` function as well), which accepts the source of
36[_Operations_](../api/core.md#operation) and returns a source of [_Operation
37Results_](../api/core.md#operationresult).
38
39- [Read more about streams on the "Architecture" page.](../architecture.md#stream-patterns-in-urql)
40- [Read more about the _Exchange_ type signature on the API docs.](../api/core.md#exchange)
41
42## Using Exchanges
43
44The `Client` accepts an `exchanges` option that. Initially, we may choose to just
45set this to two very standard exchanges — `cacheExchange` and `fetchExchange`.
46
47In essence these exchanges build a pipeline that runs in the order they're passed; _Operations_ flow
48in from the start to the end, and _Results_ are returned through the chain in reverse.
49
50Suppose we pass the `cacheExchange` and then the `fetchExchange` to the `exchanges`.
51
52**First,** operations are checked against the cache. Depending on the `requestPolicy`,
53cached results can be resolved from here instead, which would mean that the cache sends back the
54result, and the operation doesn't travel any further in the chain.
55
56**Second,** operations are sent to the API, and the result is turned into an `OperationResult`.
57
58**Lastly,** operation results then travel through the exchanges in _reverse order_, which is because
59exchanges are a pipeline where all operations travel forward deeper into the exchange chain, and
60then backwards. When these results pass through the cache then the `cacheExchange` stores the
61result.
62
63```js
64import { Client, fetchExchange, cacheExchange } from 'urql';
65
66const client = new Client({
67 url: 'http://localhost:3000/graphql',
68 exchanges: [cacheExchange, fetchExchange],
69});
70```
71
72We can add more exchanges to this chain, for instance, we can add the `mapExchange`, which can call a
73callback whenever it sees [a `CombinedError`](../basics/errors.md) occur on a result.
74
75```js
76import { Client, fetchExchange, cacheExchange, mapExchange } from 'urql';
77
78const client = new Client({
79 url: 'http://localhost:3000/graphql',
80 exchanges: [
81 cacheExchange,
82 mapExchange({
83 onError(error) {
84 console.error(error);
85 },
86 }),
87 fetchExchange,
88 ],
89});
90```
91
92This is an example for adding a synchronous exchange to the chain that only reacts to results. It
93doesn't add any special behavior for operations travelling through it. An example for an
94asynchronous exchange that looks at both operations and results [we may look at the `retryExchange`
95which retries failed operations.](../advanced/retry-operations.md)
96
97## The Rules of Exchanges
98
99Before we can start writing some exchanges, there are a couple of consistent patterns and limitations that
100must be adhered to when writing an exchange. We call these the "rules of Exchanges", which
101also come in useful when trying to learn what Exchanges actually are.
102
103For reference, this is a basic template for an exchange:
104
105```js
106const noopExchange = ({ client, forward }) => {
107 return operations$ => {
108 // <-- The ExchangeIO function
109 const operationResult$ = forward(operations$);
110 return operationResult$;
111 };
112};
113```
114
115This exchange does nothing else than forward all operations and return all results. Hence, it's
116called a `noopExchange` — an exchange that doesn't do anything.
117
118### Forward and Return Composition
119
120When you create a `Client` and pass it an array of exchanges, `urql` composes them left-to-right.
121If 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`.
122
123```js
124import { Client, cacheExchange, fetchExchange } from 'urql';
125
126const noopExchange = ({ client, forward }) => {
127 return operations$ => {
128 // <-- The ExchangeIO function
129 // We receive a stream of Operations from `cacheExchange` which
130 // we can modify before...
131 const forwardOperations$ = operations$;
132
133 // ...calling `forward` with the modified stream. The `forward`
134 // function is the next exchange's `ExchangeIO` function, in this
135 // case `fetchExchange`.
136 const operationResult$ = forward(operations$);
137
138 // We get back `fetchExchange`'s stream of results, which we can
139 // also change before returning, which is what `cacheExchange`
140 // will receive when calling `forward`.
141 return operationResult$;
142 };
143};
144
145const client = new Client({
146 exchanges: [cacheExchange, noopExchange, fetchExchange],
147});
148```
149
150### How to Avoid Accidentally Dropping Operations
151
152Typically the `operations$` stream will send you `query`, `mutation`,
153`subscription`, and `teardown`. There is no constraint for new operations
154to be added later on or a custom exchange adding new operations altogether.
155
156This means that you have to take "unknown" operations into account and
157not `filter` operations too aggressively.
158
159```js
160import { pipe, filter, merge } from 'wonka';
161
162// DON'T: drop unknown operations
163({ forward }) =>
164 operations$ => {
165 // This doesn't handle operations that aren't queries
166 const queries = pipe(
167 operations$,
168 filter(op => op.kind === 'query')
169 );
170 return forward(queries);
171 };
172
173// DO: forward operations that you don't handle
174({ forward }) =>
175 operations$ => {
176 const queries = pipe(
177 operations$,
178 filter(op => op.kind === 'query')
179 );
180 const rest = pipe(
181 operations$,
182 filter(op => op.kind !== 'query')
183 );
184 return forward(merge([queries, rest]));
185 };
186```
187
188If operations are grouped and/or filtered by what the exchange is handling, then it's also important to
189make that any streams of operations not handled by the exchange should also be forwarded.
190
191### Synchronous first, Asynchronous last
192
193By default exchanges and Wonka streams are as predictable as possible.
194Every operator in Wonka runs synchronously until asynchronicity is introduced.
195
196This may happen when using a timing utility from Wonka, like
197[`delay`](https://wonka.kitten.sh/api/operators#delay) or
198[`throttle`](https://wonka.kitten.sh/api/operators#throttle)
199This can also happen because the exchange inherently does something asynchronous, like fetching some
200data or using a promise.
201
202When writing exchanges, some will inevitably be asynchronous. For example if
203they're fetching results, performing authentication, or other tasks
204that you have to wait for.
205
206This can cause problems, because the behavior in `urql` is built
207to be _synchronous_ first. This is very helpful for suspense mode and allowing components receive cached data on their initial
208mount without rerendering.
209
210This why **all exchanges should be ordered synchronous first and
211asynchronous last**.
212
213What we for instance repeat as the default setup in our docs is this:
214
215```js
216import { Client, cacheExchange, fetchExchange } from 'urql';
217
218new Client({
219 // ...
220 exchanges: [cacheExchange, fetchExchange];
221});
222```
223
224The `cacheExchange` is completely synchronous.
225The `fetchExchange` is asynchronous since it makes a `fetch` request and waits for a server response.
226If we put an asynchronous exchange in front of the `cacheExchange`, that would be unexpected, and
227since all results would then be delayed, nothing would ever be "cached" and instead always take some
228amount of time to be returned.
229
230When you're adding more exchanges, it's often crucial
231to put them in a specific order. For instance, an authentication exchange
232will need to go before the `fetchExchange`, a secondary cache will probably have to
233go in front of the default cache exchange.
234
235To ensure the correct behavior of suspense mode and
236the initialization of our hooks, it's vital to order exchanges
237so that synchronous ones come before asynchronous ones.