1---
2title: Architecture
3order: 3
4---
5
6# Architecture
7
8`urql` is a highly customizable and flexible GraphQL client.
9As you use it in your app, it's split into three parts:
10
11- Bindings — such as for React, Preact, Vue, or Svelte — which interact with `@urql/core`'s
12 `Client`.
13- The Client — as created [with the core `@urql/core` package](./basics/core.md), which interacts with "exchanges" to execute GraphQL
14 operations, and which you can also use directly.
15- Exchanges, which provide functionality like fetching or caching to the `Client`.
16
17By default, `urql` aims to provide the minimal amount of features that allow us to build an app
18quickly. However, `urql` has also been designed to be a GraphQL Client
19that grows with our usage and demands. As we go from building our smallest or first GraphQL apps to
20utilising its full functionality, we have tools at our disposal to extend and customize `urql` to
21our liking.
22
23## Using GraphQL Clients
24
25You may have worked with a GraphQL API previously and noticed that using GraphQL in your app can be
26as straightforward as sending a plain HTTP request with your query to fetch some data.
27
28GraphQL also provides an opportunity to abstract away a lot of the manual work that goes with
29sending these queries and managing the data. Ultimately, this lets you focus on building
30your app without having to handle the technical details of state management in detail.
31
32Specifically, `urql` simplifies three common aspects of using GraphQL:
33
34- Sending queries and mutations and receiving results _declaratively_
35- Abstracting _caching_ and state management internally
36- Providing a central point of _extensibility_ and integration with your API
37
38In the following sections we'll talk about the way that `urql` solves these three problems and how the logic is abstracted away internally.
39
40## Requests and Operations on the Client
41
42If `urql` was a train it would take several stops to arrive at its terminus, our API. It starts with us
43defining queries or mutations by writing in GraphQL's query language.
44
45Any GraphQL request can be abstracted into its query documents and its variables.
46
47```js
48import { gql } from '@urql/core';
49
50const query = gql`
51 query ($name: String!) {
52 helloWorld(name: $name)
53 }
54`;
55
56const request = createRequest(query, {
57 name: 'Urkel',
58});
59```
60
61In `urql`, these GraphQL requests are treated as unique objects and each GraphQL request will have
62a `key` generated for them. This `key` is a hash of the query document and the variables you provide
63and are set on the `key` property of a [`GraphQLRequest`](./api/core.md#graphqlrequest).
64
65Whenever we decide to send our GraphQL requests to a GraphQL API we start by using `urql`'s
66[`Client`](./api/core.md#client).
67The `Client` accepts several options to configure its behaviour and the behaviour of exchanges,
68like the `fetchExchange`. For instance, we can pass it a `url` which the `fetchExchange` will
69use to make a `fetch` call to our GraphQL API.
70
71```js
72import { Client, cacheExchange, fetchExchange } from '@urql/core';
73
74const client = new Client({
75 url: 'http://localhost:3000/graphql',
76 exchanges: [cacheExchange, fetchExchange],
77});
78```
79
80Above, we're defining a `Client` that is ready to accept our requests. It will apply basic
81document caching and will send uncached requests to the `url` we pass it.
82The bindings that we've seen in [the "Basics" section](./basics/README.md), like `useQuery` for
83React for example, interact with [the `Client`](./api/core.md#client) directly and are a thin
84abstraction.
85
86Some methods can be called on it directly however, as seen [on the "Core Usage"
87page](./basics/core.md#one-off-queries-and-mutations).
88
89```js
90// Given our request and client defined above, we can call
91const subscription = client.executeQuery(request).subscribe(result => {
92 console.log(result.data);
93});
94```
95
96As we've seen, `urql` defines our query documents and variables as
97[`GraphQLRequest`s](./api/core.md#graphqlrequest). However, since we have more metadata that is
98needed, like our `url` option on the `Client`, `urql` internally creates [`Operation`s](./api/core.md#operation)
99each time a request is executed. The operations are then forwarded to the exchanges, like the
100`cacheExchange` and `fetchExchange`.
101
102An "Operation" is an extension of `GraphQLRequest`s. Not only do they carry the `query`, `variables`,
103and a `key` property, they will also identify the `kind` of operation that is executed, like
104`"query"` or `"mutation"`, and they contain the `Client`'s options on `operation.context`.
105
106
107
108This means, once we hand over a GraphQL request to the `Client`, it will create an `Operation`,
109and then hand it over to the exchanges until a result comes back.
110
111As shown in the diagram, each operation is like an event or signal for a GraphQL request to start,
112and the exchanges will eventually send back a corresponding result.
113However, because the cache can send updates to us whenever it detects a change, or you could cancel
114a GraphQL request before it finishes, a special "teardown" `Operation` also exists, which cancels
115ongoing requests.
116
117## The Client and Exchanges
118
119To reiterate, when we use `urql`'s bindings for our framework of choice, methods are called on the
120`Client`, but we never see the operations that are created in the background from our bindings. We
121call a method like `client.executeQuery` (or it's called for us in the bindings), an operation is
122issued internally when we subscribe with a callback, and later, we're given results.
123
124
125
126While we know that, for us, we're only interested in a single [`Operation`](./api/core.md#operation)
127and its [`OperationResult`s](./api/core.md#operationresult) at a time, the `Client` treats these as
128one big stream. The `Client` sees an incoming flow of all of our operations.
129
130As we've learned before, each operation carries a `key` and each result we receive carries the
131original `operation`. Because an `OperationResult` also carries an `operation` property the `Client`
132will always know which results correspond to an individual operation.
133However, internally, all of our operations are processed at the same time concurrently. However, from
134our perspective:
135
136- We subscribe to a "stream" and expect to get results on a callback
137- The `Client` issues the operation, and we'll receive some results back eventually as either the
138 cache responds (synchronously), or the request gets sent to our API.
139- We eventually unsubscribe, and the `Client` issues a "teardown" operation with the same `key` as
140 the original operation, which concludes our flow.
141
142The `Client` itself doesn't actually know what to do with operations. Instead, it sends them through
143"exchanges". Exchanges are akin to [middleware in Redux](https://redux.js.org/advanced/middleware)
144and have access to all operations and all results. Multiple exchanges are chained to process our
145operations and to execute logic on them, one of them being the `fetchExchange`, which as the name
146implies sends our requests to our API.
147
148### How operations get to exchanges
149
150We now know how we get to operations and to the `Client`:
151
152- Any bindings or calls to the `Client` create an **operation**
153- This operation identifies itself as either a `"query"`, `"mutation"` or `"subscription"` and has a
154 unique `key`.
155- This operation is sent into the **exchanges** and eventually ends up at the `fetchExchange`
156 (or a similar exchange)
157- The operation is sent to the API and a **result** comes back, which is wrapped in an `OperationResult`
158- The `Client` filters the `OperationResult` by the `operation.key` and — via a callback — gives us
159 a **stream of results**.
160
161To come back to our train analogy from earlier, an operation, like a train, travels from one end
162of the track to the terminus — our API. The results then come back on the same path as they're just
163travelling the same line in reverse.
164
165### The Exchanges
166
167By default, the `Client` doesn't do anything with GraphQL requests. It contains only the logic to
168manage and differentiate between active and inactive requests and converts them to operations.
169To actually do something with our GraphQL requests, it needs _exchanges_, which are like plugins
170that you can pass to create a pipeline of how GraphQL operations are executed.
171
172By default, you may want to add the `cacheExchange` and the `fetchExchange` from `@urql/core`:
173
174- `cacheExchange`: Caches GraphQL results with ["Document Caching"](./basics/document-caching.md)
175- `fetchExchange`: Executes GraphQL requests with a `fetch` HTTP call
176
177```js
178import { Client, cacheExchange, fetchExchange } from '@urql/core';
179
180const client = new Client({
181 url: 'http://localhost:3000/graphql',
182 exchanges: [cacheExchange, fetchExchange],
183});
184```
185
186As we can tell, exchanges define not only how GraphQL requests are executed and handled, but also
187get control over caching. Exchanges can be used to change almost any behaviour in the `Client`,
188although internally they only handle incoming & outgoing requests and incoming & outgoing results.
189
190Some more exchanges that we can use with our `Client` are:
191
192- [`mapExchange`](./api/core.md#mapexchange): Allows changing and reacting to operations, results, and errors
193- [`ssrExchange`](./advanced/server-side-rendering.md): Allows for a server-side renderer to
194 collect results for client-side rehydration.
195- [`retryExchange`](./advanced/retry-operations.md): Allows operations to be retried on errors
196- [`persistedExchange`](./advanced/persistence-and-uploads.md#automatic-persisted-queries): Provides support for Automatic
197 Persisted Queries
198- [`authExchange`](./advanced/authentication.md): Allows refresh authentication to be implemented easily.
199- [`requestPolicyExchange`](./api/request-policy-exchange.md): Automatically refreshes results given a TTL.
200- `devtoolsExchange`: Provides the ability to use the [urql-devtools](https://github.com/urql-graphql/urql-devtools)
201
202We can even swap out our [document cache](./basics/document-caching.md), which is implemented by
203`@urql/core`'s `cacheExchange`, with `urql`'s [normalized cache,
204Graphcache](./graphcache/README.md).
205
206[Read more about exchanges and how to write them from scratch on the "Authoring Exchanges"
207page.](./advanced/authoring-exchanges.md)
208
209## Stream Patterns in `urql`
210
211In the previous sections we've learned a lot about how the `Client` works, but we've always learned
212it in vague terms — for instance, we've learned that we get a "stream of results" or `urql` sees all
213operations as "one stream of operations" that it sends to the exchanges.
214But, **what are streams?**
215
216Generally we refer to _streams_ as abstractions that allow us to program with asynchronous events
217over time. Within the context of JavaScript we're specifically thinking in terms of
218[Observables](https://github.com/tc39/proposal-observable)
219and [Reactive Programming with Observables.](http://reactivex.io/documentation/observable.html)
220These concepts may sound intimidating, but from a high-level view what we're talking about can be
221thought of as a combination of promises and iterables (e.g. arrays). We're dealing with multiple
222events, but our callback is called over time. It's like calling `forEach` on an array but expecting
223the results to come in asynchronously.
224
225As a user, if we're using the one framework bindings that we've seen in [the "Basics"
226section](./basics/README.md), we may never see these streams in action or may never use them even,
227since the bindings internally use them for us. But if we [use the `Client`
228directly](./basics/core.md#one-off-queries-and-mutations) or write exchanges then we'll see streams
229and will have to deal with their API.
230
231### Stream patterns with the client
232
233When we call methods on the `Client` like [`client.executeQuery`](./api/core.md#clientexecutequery)
234or [`client.query`](./api/core.md#clientquery) then these will return a "stream" of results.
235
236It's normal for GraphQL subscriptions to deliver multiple results, however, even GraphQL queries can
237give you multiple results in `urql`. This is because operations influence one another. When a cache
238invalidates a query, this query may refetch, and a new result is delivered to your application.
239
240Multiple results mean that once you subscribe to a GraphQL query via the `Client`, you may receive
241new results in the future.
242
243```js
244import { gql } from '@urql/core';
245
246const QUERY = gql`
247 query Test($id: ID!) {
248 getUser(id: $id) {
249 id
250 name
251 }
252 }
253`;
254
255client.query(QUERY, { id: 'test' }).subscribe(result => {
256 console.log(result); // { data: ... }
257});
258```
259
260Read more about the available APIs on the `Client` in the [Core API docs](./api/core.md).
261
262Internally, these streams and all exchanges are written using a library called
263[`wonka`](https://wonka.kitten.sh/basics/background), which is a tiny Observable-like
264library. It is used to write exchanges and when we interact with the `Client` it is used internally
265as well.