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)

authored by kitten.sh and committed by

GitHub 699089dd 97c8be2c

+472 -423
+2 -3
docs/advanced/authentication.md
··· 51 51 in front of all `fetchExchange`s but after all other synchronous exchanges, like the `cacheExchange`. 52 52 53 53 ```js 54 - import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql'; 54 + import { Client, cacheExchange, fetchExchange } from 'urql'; 55 55 import { authExchange } from '@urql/exchange-auth'; 56 56 57 - const client = createClient({ 57 + const client = new Client({ 58 58 url: 'http://localhost:3000/graphql', 59 59 exchanges: [ 60 - dedupExchange, 61 60 cacheExchange, 62 61 authExchange(async utils => { 63 62 return {
+29 -29
docs/advanced/authoring-exchanges.md
··· 41 41 42 42 ## Using Exchanges 43 43 44 - 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` 45 - utility, which starts chaining these exchanges. 44 + The `Client` accepts an `exchanges` option that. Initially, we may choose to just 45 + set this to two very standard exchanges — `cacheExchange` and `fetchExchange`. 46 46 47 47 In essence these exchanges build a pipeline that runs in the order they're passed; _Operations_ flow 48 48 in from the start to the end, and _Results_ are returned through the chain in reverse. 49 49 50 - If we look at our list of default exchanges — `dedupExchange`, `cacheExchange`, and then 51 - `fetchExchange` — an incoming operation is treated as follows: 50 + Suppose we pass the `cacheExchange` and then the `fetchExchange` to the `exchanges`. 52 51 53 - **First,** ongoing operations are deduplicated. It wouldn't make sense to send the 54 - same operation / request twice in parallel. 55 - 56 - **Second,** operations are checked against the cache. Depending on the `requestPolicy`, 52 + **First,** operations are checked against the cache. Depending on the `requestPolicy`, 57 53 cached results can be resolved from here instead, which would mean that the cache sends back the 58 54 result, and the operation doesn't travel any further in the chain. 59 55 60 - **Third,** operations are sent to the API, and the result is turned into an `OperationResult`. 56 + **Second,** operations are sent to the API, and the result is turned into an `OperationResult`. 61 57 62 58 **Lastly,** operation results then travel through the exchanges in _reverse order_, which is because 63 59 exchanges are a pipeline where all operations travel forward deeper into the exchange chain, and ··· 65 61 result. 66 62 67 63 ```js 68 - import { createClient, dedupExchange, fetchExchange, cacheExchange } from 'urql'; 64 + import { Client, fetchExchange, cacheExchange } from 'urql'; 69 65 70 - const client = createClient({ 66 + const client = new Client({ 71 67 url: 'http://localhost:3000/graphql', 72 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 68 + exchanges: [cacheExchange, fetchExchange], 73 69 }); 74 70 ``` 75 71 76 - We can add more exchanges to this chain, for instance, we can add the `errorExchange`, which calls a 77 - global callback whenever it sees [a `CombinedError`](../basics/errors.md) on an `OperationResult`. 72 + We can add more exchanges to this chain, for instance, we can add the `mapExchange`, which can call a 73 + callback whenever it sees [a `CombinedError`](../basics/errors.md) occur on a result. 78 74 79 75 ```js 80 - import { createClient, dedupExchange, fetchExchange, cacheExchange, errorExchange } from 'urql'; 76 + import { Client, fetchExchange, cacheExchange, mapExchange } from 'urql'; 81 77 82 - const client = createClient({ 78 + const client = new Client({ 83 79 url: 'http://localhost:3000/graphql', 84 80 exchanges: [ 85 - dedupExchange, 86 81 cacheExchange, 87 - errorExchange({ 82 + mapExchange({ 88 83 onError(error) { 89 84 console.error(error); 90 85 }, ··· 123 118 ### Forward and Return Composition 124 119 125 120 When you create a `Client` and pass it an array of exchanges, `urql` composes them left-to-right. 126 - 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`. 121 + 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`. 127 122 128 123 ```js 129 - import { Client, dedupExchange, fetchExchange } from 'urql'; 124 + import { Client, cacheExchange, fetchExchange } from 'urql'; 130 125 131 126 const noopExchange = ({ client, forward }) => { 132 127 return operations$ => { 133 128 // <-- The ExchangeIO function 134 - // We receive a stream of Operations from `dedupExchange` which 129 + // We receive a stream of Operations from `cacheExchange` which 135 130 // we can modify before... 136 131 const forwardOperations$ = operations$; 137 132 ··· 141 136 const operationResult$ = forward(operations$); 142 137 143 138 // We get back `fetchExchange`'s stream of results, which we can 144 - // also change before returning, which is what `dedupExchange` 139 + // also change before returning, which is what `cacheExchange` 145 140 // will receive when calling `forward`. 146 141 return operationResult$; 147 142 }; 148 143 }; 149 144 150 145 const client = new Client({ 151 - exchanges: [dedupExchange, noopExchange, fetchExchange], 146 + exchanges: [cacheExchange, noopExchange, fetchExchange], 152 147 }); 153 148 ``` 154 149 ··· 215 210 This why **all exchanges should be ordered synchronous first and 216 211 asynchronous last**. 217 212 218 - The default order of exchanges is: 213 + What we for instance repeat as the default setup in our docs is this: 219 214 220 215 ```js 221 - import { dedupExchange, cacheExchange, fetchExchange } from 'urql'; 216 + import { Client, cacheExchange, fetchExchange } from 'urql'; 222 217 223 - [dedupExchange, cacheExchange, fetchExchange]; 218 + new Client({ 219 + // ... 220 + exchanges: [cacheExchange, fetchExchange]; 221 + }); 224 222 ``` 225 223 226 - Both the `dedupExchange` and `cacheExchange` are completely 227 - synchronous. The `fetchExchange` is asynchronous since 228 - it makes a `fetch` request and waits for a server response. 224 + The `cacheExchange` is completely synchronous. 225 + The `fetchExchange` is asynchronous since it makes a `fetch` request and waits for a server response. 226 + If we put an asynchronous exchange in front of the `cacheExchange`, that would be unexpected, and 227 + since all results would then be delayed, nothing would ever be "cached" and instead always take some 228 + amount of time to be returned. 229 229 230 230 When you're adding more exchanges, it's often crucial 231 231 to put them in a specific order. For instance, an authentication exchange
+4 -4
docs/advanced/auto-populate-mutations.md
··· 28 28 client options. 29 29 30 30 ```ts 31 - import { createClient, dedupExchange, fetchExchange } from '@urql/core'; 31 + import { Client, fetchExchange } from '@urql/core'; 32 32 import { populateExchange } from '@urql/exchange-populate'; 33 33 34 - const client = createClient({ 34 + const client = new Client({ 35 35 // ... 36 - exchanges: [dedupExchange, populateExchange({ schema }), cacheExchange, fetchExchange], 36 + exchanges: [populateExchange({ schema }), cacheExchange, fetchExchange], 37 37 }); 38 38 ``` 39 39 40 40 The `populateExchange` should be placed in front of the `cacheExchange`, especially if you're using 41 41 [Graphcache](../graphcache/README.md), since it won't understand the `@populate` directive on its 42 - own. It should also be placed after the `dedupExchange` to avoid unnecessary work. 42 + own. It should also be placed in front the `cacheExchange` to avoid unnecessary work. 43 43 44 44 Adding the `populateExchange` now enables us to use the `@populate` directive in our mutations. 45 45
+1 -1
docs/advanced/debugging.md
··· 46 46 47 47 ``` 48 48 const { unsubscribe } = client.subscribeToDebugTarget(event => { 49 - if (event.source === 'dedupExchange') 49 + if (event.source === 'cacheExchange') 50 50 return; 51 51 console.log(event); // { type, message, operation, data, source, timestamp } 52 52 });
+34 -34
docs/advanced/persistence-and-uploads.md
··· 5 5 6 6 # Persisted Queries and Uploads 7 7 8 - `urql` supports both [Automatic Persisted 9 - Queries](https://www.apollographql.com/docs/apollo-server/performance/apq/), Persisted Queries, and 10 - [File Uploads](https://www.apollographql.com/docs/apollo-server/data/file-uploads/). 11 - 12 - While File Uploads should work without any modifications, an additional exchange must be installed 13 - and added for Persisted Queries to work. 8 + `urql` supports (Automatic) Persisted Queries, and File Uploads via GraphQL 9 + Multipart requests. For persisted queries to work, some setup work is needed, 10 + while File Upload support is built into `@urql/core@4`. 14 11 15 12 ## Automatic Persisted Queries 16 13 ··· 31 28 easier for a CDN to cache, as by default most caches would not cache POST requests automatically. 32 29 33 30 In `urql`, we may use the `@urql/exchange-persisted` package's `persistedExchange` to 34 - implement Automatic Persisted Queries. This exchange works alongside the default `fetchExchange` 35 - and other exchanges by adding the `extensions.persistedQuery` parameters to a GraphQL request. 31 + enable support for Automatic Persisted Queries. This exchange works alongside other fetch or 32 + subscription exchanges by adding metadata for persisted queries to each GraphQL 33 + request by modifying the `extensions` object of operations. 36 34 37 35 ### Installation & Setup 38 36 ··· 45 43 ``` 46 44 47 45 You'll then need to add the `persistedExchange` function, that this package exposes, 48 - to your `exchanges`. 46 + to your `exchanges`, in front of exchanges that communicate with the API: 49 47 50 48 ```js 51 - import { createClient, dedupExchange, fetchExchange, cacheExchange } from 'urql'; 52 - import { persistedExchange } from '@urql/exchange-persisted-fetch'; 49 + import { Client, fetchExchange, cacheExchange } from 'urql'; 50 + import { persistedExchange } from '@urql/exchange-persisted'; 53 51 54 - const client = createClient({ 52 + const client = new Client({ 55 53 url: 'http://localhost:1234/graphql', 56 54 exchanges: [ 57 - dedupExchange, 58 55 cacheExchange, 59 56 persistedExchange({ 60 57 preferGetForPersistedQueries: true, ··· 66 63 67 64 As we can see, typically it's recommended to set `preferGetForPersistedQueries` to `true` to force 68 65 all persisted queries to use GET requests instead of POST so that CDNs can do their job. 69 - We also added the `persistedExchange` in front of the usual `fetchExchange`, since it has to 70 - update operations before they reach an exchange that talks to an API. 66 + It does so by setting the `preferGetMethod` option to `'force'` when it's 67 + updating operations. 71 68 72 - The `preferGetForPersistedQueries` is similar to the [`Client`'s 73 - `preferGetMethod`](../api/core.md#client) but only switches persisted queries to use GET requests 74 - instead. This is preferable since sometimes the GraphQL query can grow too large for a simple GET 75 - query to handle, while the `persistedExchange`'s SHA256 hashes will remain predictably small. 69 + The `fetchExchange` can see the modifications that the `persistedExchange` is 70 + making to operations, and understands to leave out the `query` from any request 71 + as needed. The same should be happening to the `subscriptionExchange`, if you're 72 + using it for queries. 76 73 77 74 ### Customizing Hashing 78 75 ··· 89 86 second argument, a GraphQL `DocumentNode` object. 90 87 91 88 ```js 92 - persistedFetchExchange({ 89 + persistedExchange({ 93 90 generateHash: (_, document) => document.documentId, 94 91 }); 95 92 ``` ··· 101 98 ```js 102 99 import sha256 from 'hash.js/lib/hash/sha/256'; 103 100 104 - persistedFetchExchange({ 105 - generateHash: async query => { 101 + persistedExchange({ 102 + async generateHash(query) { 106 103 return sha256().update(query).digest('hex'); 107 104 }, 108 105 }); ··· 115 112 116 113 ## File Uploads 117 114 118 - Many GraphQL server frameworks and APIs support the ["GraphQL Multipart Request 119 - Spec](https://github.com/jaydenseric/graphql-multipart-request-spec) to allow files to be uploaded. 120 - Often, this is defined in schemas using a `File` or `Upload` input. 121 - This allows us to pass a `File` or `Blob` directly to our GraphQL requests as variables, and the 122 - spec requires us to perform this request as a multipart upload. 115 + GraphQL server APIs commonly support the [GraphQL Multipart Request 116 + spec](https://github.com/jaydenseric/graphql-multipart-request-spec) to allow for File Uploads 117 + directly with a GraphQL API. 123 118 124 - Files are often handled in the browser via the [File API](https://developer.mozilla.org/en-US/docs/Web/API/File), 125 - 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) 119 + If a GraphQL API supports this, we can pass a [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) 120 + or a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) directly into our variables and 121 + define the corresponding scalar for our variable, which is often called `File` or `Upload`. 122 + 123 + In a browser, the `File` object may often be retrieved via a 124 + [file input](https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications), 126 125 for example. 127 126 128 - In `urql`, these are supported natively, so as long as your JS environment supports either `File` or 129 - `Blob`s, you can pass these directly to any `urql` API via your `variables`, and the default 130 - `fetchExchange` will swich to using a multipart request instead. 127 + The `@urql/core@4` package supports File Uploads natively, so we won't have to do any installation 128 + or setup work. When `urql` sees a `File` or a `Blob` anywhere in your `variables`, it switches to 129 + a `multipart/form-data` request, converts the request to a `FormData` object, according to the 130 + GraphQL Multipart Request specification, and sends it off to the API. 131 131 132 - Previously, this worked by installing the [`@urql/multipart-fetch-exchange` package](../api/multipart-fetch-exchange.md), 133 - however, this package has been deprecated and file uploads are now built into `@urql/core`. 132 + > **Note:** Previously, this worked by installing the `@urql/multipart-fetch-exchange` package. 133 + > however, this package has been deprecated and file uploads are now built into `@urql/core@4`.
+5 -7
docs/advanced/retry-operations.md
··· 22 22 You'll then need to add the `retryExchange`, exposed by this package, to your `urql` Client: 23 23 24 24 ```js 25 - import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql'; 25 + import { Client, cacheExchange, fetchExchange } from 'urql'; 26 26 import { retryExchange } from '@urql/exchange-retry'; 27 27 28 28 // None of these options have to be added, these are the default values. ··· 36 36 37 37 // Note the position of the retryExchange - it should be placed prior to the 38 38 // fetchExchange and after the cacheExchange for it to function correctly 39 - const client = createClient({ 39 + const client = new Client({ 40 40 url: 'http://localhost:1234/graphql', 41 41 exchanges: [ 42 - dedupExchange, 43 42 cacheExchange, 44 43 retryExchange(options), // Use the retryExchange factory to add a new exchange 45 44 fetchExchange, ··· 69 68 let's look at an example: 70 69 71 70 ```js 72 - import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql'; 71 + import { Client, cacheExchange, fetchExchange } from 'urql'; 73 72 import { retryExchange } from '@urql/exchange-retry'; 74 73 75 - const client = createClient({ 74 + const client = new Client({ 76 75 url: 'http://localhost:1234/graphql', 77 76 exchanges: [ 78 - dedupExchange, 79 77 cacheExchange, 80 78 retryExchange({ 81 79 retryIf: error => { 82 - return !!(error.graphQLErrors.length > 0 || error.networkError); 80 + return !!(error.graphQLErrors.length > 0 || error.networkError); 83 81 }, 84 82 }), 85 83 fetchExchange,
+6 -7
docs/advanced/server-side-rendering.md
··· 18 18 To start out with the `ssrExchange` we have to add the exchange to our `Client`: 19 19 20 20 ```js 21 - import { createClient, dedupExchange, cacheExchange, fetchExchange, ssrExchange } from '@urql/core'; 21 + import { Client, cacheExchange, fetchExchange, ssrExchange } from '@urql/core'; 22 22 23 23 const isServerSide = typeof window === 'undefined'; 24 24 ··· 28 28 initialState: !isServerSide ? window.__URQL_DATA__ : undefined, 29 29 }); 30 30 31 - const client = createClient({ 31 + const client = new Client({ 32 32 exchanges: [ 33 - dedupExchange, 34 33 cacheExchange, 35 34 ssr, // Add `ssr` in front of the `fetchExchange` 36 35 fetchExchange, ··· 113 112 import prepass from 'react-ssr-prepass'; 114 113 115 114 import { 116 - createClient, 117 - dedupExchange, 115 + Client, 118 116 cacheExchange, 119 117 fetchExchange, 120 118 ssrExchange, ··· 125 123 // ... 126 124 const ssr = ssrExchange({ isClient: false }); 127 125 128 - const client createClient({ 126 + const client new Client({ 127 + url: 'https://??', 129 128 suspense: true, // This activates urql's Suspense mode on the server-side 130 - exchanges: [dedupExchange, cacheExchange, ssr, fetchExchange] 129 + exchanges: [cacheExchange, ssr, fetchExchange] 131 130 }); 132 131 133 132 const element = (
+13 -18
docs/advanced/subscriptions.md
··· 13 13 To add support for subscriptions we need to add the `subscriptionExchange` to our `Client`. 14 14 15 15 ```js 16 - import { Client, dedupExchange, cacheExchange, fetchExchange, subscriptionExchange } from 'urql'; 16 + import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql'; 17 17 18 18 const client = new Client({ 19 19 url: 'http://localhost:3000/graphql', 20 20 exchanges: [ 21 - dedupExchange, 22 21 cacheExchange, 23 22 fetchExchange, 24 23 subscriptionExchange({ ··· 47 46 For backends supporting `graphql-ws`, we recommend using the [graphql-ws](https://github.com/enisdenjo/graphql-ws) client. 48 47 49 48 ```js 50 - import { createClient, dedupExchange, cacheExchange, fetchExchange, subscriptionExchange } from 'urql'; 49 + import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql'; 51 50 import { createClient as createWSClient } from 'graphql-ws'; 52 51 53 52 const wsClient = createWSClient({ 54 53 url: 'ws://localhost/graphql', 55 54 }); 56 55 57 - const client = createClient({ 56 + const client = new Client({ 58 57 url: '/graphql', 59 58 exchanges: [ 60 - dedupExchange, 61 59 cacheExchange, 62 60 fetchExchange, 63 61 subscriptionExchange({ ··· 85 83 > 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. 86 84 87 85 ```js 88 - import { Client, dedupExchange, cacheExchange, fetchExchange, subscriptionExchange } from 'urql'; 86 + import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql'; 89 87 import { SubscriptionClient } from 'subscriptions-transport-ws'; 90 88 91 89 const subscriptionClient = new SubscriptionClient('ws://localhost/graphql', { reconnect: true }); ··· 93 91 const client = new Client({ 94 92 url: '/graphql', 95 93 exchanges: [ 96 - dedupExchange, 97 94 cacheExchange, 98 95 fetchExchange, 99 96 subscriptionExchange({ ··· 195 192 from 196 193 text 197 194 } 198 - }` 199 - }); 195 + } 196 + `, 197 + }); 200 198 </script> 201 199 202 200 {#if !$messages.data} ··· 267 265 text 268 266 } 269 267 } 270 - ` 268 + `, 271 269 }, handleSubscription) 272 270 273 271 return { ··· 298 296 use subscriptions in a Node.js environment. 299 297 300 298 ```js 301 - import { pipe, subscribe } from 'wonka'; 299 + import { gql } from '@urql/core'; 302 300 303 - const MessageSub = ` 301 + const MessageSub = gql` 304 302 subscription MessageSub { 305 303 newMessages { 306 304 id ··· 310 308 } 311 309 `; 312 310 313 - const { unsubscribe } = pipe( 314 - client.subscription(MessageSub), 315 - subscribe(result => { 316 - console.log(result); // { data: ... } 317 - }) 318 - ); 311 + const { unsubscribe } = client.subscription(MessageSub).subscribe(result => { 312 + console.log(result); // { data: ... } 313 + }); 319 314 ```
+5 -1
docs/api/README.md
··· 11 11 Most of these packages will refer to or use utilities and types from the `@urql/core` package. [Read 12 12 more about the core package on the "Core" page.](../basics/core.md) 13 13 14 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 15 + > You can view TSDocs while using these packages in your editor, as long as it supports the 16 + > TypeScript Language Server. 17 + > We're planning to replace these API docs with a separate web app soon. 18 + 14 19 - [`@urql/core` API docs](./core.md) 15 20 - [`urql` React API docs](./urql.md) 16 21 - [`@urql/preact` Preact API docs](./preact.md) ··· 18 23 - [`@urql/exchange-graphcache` API docs](./graphcache.md) 19 24 - [`@urql/exchange-retry` API docs](./retry-exchange.md) 20 25 - [`@urql/exchange-execute` API docs](./execute-exchange.md) 21 - - [`@urql/exchange-multipart-fetch` API docs](./multipart-fetch-exchange.md) 22 26 - [`@urql/exchange-request-policy` API docs](./request-policy-exchange.md) 23 27 - [`@urql/exchange-auth` API docs](./auth-exchange.md) 24 28 - [`@urql/exchange-refocus` API docs](./refocus-exchange.md)
+5
docs/api/auth-exchange.md
··· 5 5 6 6 # Authentication Exchange 7 7 8 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9 + > You can view TSDocs while using these packages in your editor, as long as it supports the 10 + > TypeScript Language Server. 11 + > We're planning to replace these API docs with a separate web app soon. 12 + 8 13 The `@urql/exchange-auth` package contains an addon `authExchange` for `urql` that aims to make it 9 14 easy to implement complex authentication and reauthentication flows as are typically found with JWT 10 15 token based API authentication.
+6 -1
docs/api/core.md
··· 5 5 6 6 # @urql/core 7 7 8 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9 + > You can view TSDocs while using these packages in your editor, as long as it supports the 10 + > TypeScript Language Server. 11 + > We're planning to replace these API docs with a separate web app soon. 12 + 8 13 The `@urql/core` package is the basis of all framework bindings. Each bindings-package, 9 14 like [`urql` for React](./urql.md) or [`@urql/preact`](./preact.md), will reuse the core logic and 10 15 reexport all exports from `@urql/core`. ··· 22 27 23 28 | Input | Type | Description | 24 29 | ----------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 25 - | `exchanges` | `Exchange[]` | An array of `Exchange`s that the client should use | 30 + | `exchanges` | `Exchange[]` | An array of `Exchange`s that the client should use | 26 31 | `url` | `string` | The GraphQL API URL as used by `fetchExchange` | 27 32 | `fetchOptions` | `RequestInit \| () => RequestInit` | Additional `fetchOptions` that `fetch` in `fetchExchange` should use to make a request | 28 33 | `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
··· 5 5 6 6 # Execute Exchange 7 7 8 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9 + > You can view TSDocs while using these packages in your editor, as long as it supports the 10 + > TypeScript Language Server. 11 + > We're planning to replace these API docs with a separate web app soon. 12 + 8 13 The `@urql/exchange-execute` package contains an addon `executeExchange` for `urql` that may be used to 9 14 execute queries against a local schema. It is therefore a drop-in replacement for the default 10 15 _fetchExchange_ and useful for the server-side, debugging, or testing.
+5
docs/api/graphcache.md
··· 5 5 6 6 # @urql/exchange-graphcache 7 7 8 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9 + > You can view TSDocs while using these packages in your editor, as long as it supports the 10 + > TypeScript Language Server. 11 + > We're planning to replace these API docs with a separate web app soon. 12 + 8 13 The `@urql/exchange-graphcache` package contains an addon `cacheExchange` for `urql` that may be 9 14 used to replace the default [`cacheExchange`](./core.md#cacheexchange), which switches `urql` from 10 15 using ["Document Caching"](../basics/document-caching.md) to ["Normalized
-46
docs/api/multipart-fetch-exchange.md
··· 1 - --- 2 - title: '@urql/exchange-multipart-fetch' 3 - order: 7 4 - --- 5 - 6 - # Multipart Fetch Exchange 7 - 8 - > **Deprecation**: The `multipartFetchExchange` has been deprecated, and 9 - > `@urql/core` now supports GraphQL Multipart Requests natively. This won't 10 - > break the behaviour of your existing apps, however, it's recommended to remove 11 - > the `multipartFetchExchange` from your apps. 12 - 13 - The `@urql/exchange-multipart-fetch` package contains an addon `multipartFetchExchange` for `urql` 14 - that enables file uploads via `multipart/form-data` POST requests. 15 - 16 - It follows the unofficial [GraphQL Multipart Request 17 - Spec](https://github.com/jaydenseric/graphql-multipart-request-spec) which is supported by the 18 - [Apollo Sever package](https://www.apollographql.com/docs/apollo-server/data/file-uploads/). 19 - 20 - This exchange uses the same fetch logic as the [`fetchExchange`](./core.md#fetchexchange) and by reusing logic from `@urql/core/internal`. 21 - The `multipartFetchExchange` is a drop-in replacement for the default 22 - [`fetchExchange`](./core.md#fetchexchange) and will act exactly like the `fetchExchange` unless the 23 - `variables` that it receives for mutations contain any `File`s as detected by the `extract-files` package. 24 - 25 - ## Installation and Setup 26 - 27 - First install `@urql/exchange-multipart-fetch` alongside `urql`: 28 - 29 - ```sh 30 - yarn add @urql/exchange-multipart-fetch 31 - # or 32 - npm install --save @urql/exchange-multipart-fetch 33 - ``` 34 - 35 - The `multipartFetchExchange` is a drop-in replacement for the `fetchExchange`, which should be 36 - replaced in the list of `exchanges`: 37 - 38 - ```js 39 - import { createClient, dedupExchange, cacheExchange } from 'urql'; 40 - import { multipartFetchExchange } from '@urql/exchange-multipart-fetch'; 41 - 42 - const client = createClient({ 43 - url: 'http://localhost:3000/graphql', 44 - exchanges: [dedupExchange, cacheExchange, multipartFetchExchange], 45 - }); 46 - ```
+5
docs/api/preact.md
··· 5 5 6 6 # @urql/preact 7 7 8 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9 + > You can view TSDocs while using these packages in your editor, as long as it supports the 10 + > TypeScript Language Server. 11 + > We're planning to replace these API docs with a separate web app soon. 12 + 8 13 The `@urql/preact` API is the same as the React `urql` API. 9 14 Please refer to [the "urql" API docs](./urql.md) for details on the Preact API.
+5
docs/api/refocus-exchange.md
··· 5 5 6 6 # Refocus Exchange 7 7 8 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9 + > You can view TSDocs while using these packages in your editor, as long as it supports the 10 + > TypeScript Language Server. 11 + > We're planning to replace these API docs with a separate web app soon. 12 + 8 13 `@urql/exchange-refocus` is an exchange for the `urql` that tracks currently active operations and redispatches them when the 9 14 window regains focus 10 15
+5
docs/api/request-policy-exchange.md
··· 5 5 6 6 # Request Policy Exchange 7 7 8 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9 + > You can view TSDocs while using these packages in your editor, as long as it supports the 10 + > TypeScript Language Server. 11 + > We're planning to replace these API docs with a separate web app soon. 12 + 8 13 The `@urql/exchange-request-policy` package contains an addon `requestPolicyExchange` for `urql` 9 14 that may be used to upgrade [Operations' Request Policies](./core.md#requestpolicy) on a 10 15 time-to-live basis.
+5
docs/api/retry-exchange.md
··· 5 5 6 6 # Retry Exchange 7 7 8 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9 + > You can view TSDocs while using these packages in your editor, as long as it supports the 10 + > TypeScript Language Server. 11 + > We're planning to replace these API docs with a separate web app soon. 12 + 8 13 The `@urql/exchange-retry` package contains an addon `retryExchange` for `urql` that may be used to 9 14 let failed operations be retried, typically when a previous operation has failed with a network 10 15 error.
+5
docs/api/svelte.md
··· 5 5 6 6 # Svelte API 7 7 8 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9 + > You can view TSDocs while using these packages in your editor, as long as it supports the 10 + > TypeScript Language Server. 11 + > We're planning to replace these API docs with a separate web app soon. 12 + 8 13 ## queryStore 9 14 10 15 The `queryStore` factory accepts properties as inputs and returns a Svelte pausable, readable store
+12 -7
docs/api/urql.md
··· 5 5 6 6 # React API 7 7 8 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9 + > You can view TSDocs while using these packages in your editor, as long as it supports the 10 + > TypeScript Language Server. 11 + > We're planning to replace these API docs with a separate web app soon. 12 + 8 13 ## useQuery 9 14 10 15 Accepts a single required options object as an input with the following properties: ··· 14 19 | `query` | `string \| DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | 15 20 | `variables` | `?object` | The variables to be used with the GraphQL request. | 16 21 | `requestPolicy` | `?RequestPolicy` | An optional [request policy](./core.md#requestpolicy) that should be used specifying the cache strategy. | 17 - | `pause` | `?boolean` | A boolean flag instructing [execution to be paused](../basics/react-preact.md#pausing-usequery). | 22 + | `pause` | `?boolean` | A boolean flag instructing [execution to be paused](../basics/react-preact.md#pausing-usequery). | 18 23 | `context` | `?object` | Holds the contextual information for the query. | 19 24 20 25 This hook returns a tuple of the shape `[result, executeQuery]`. ··· 46 51 47 52 Accepts a single required options object as an input with the following properties: 48 53 49 - | Prop | Type | Description | 50 - | ---------------------------------------------------- | ------------------------ | ---------------------------------------------------------------------------------- | 51 - | `query` | `string \| DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | 52 - | `variables` | `?object` | The variables to be used with the GraphQL request. | 53 - | `pause` | `?boolean` | A boolean flag instructing [execution to be paused](../basics/react-preact.md#pausing-usequery). | 54 - | `context` | `?object` | Holds the contextual information for the query. | 54 + | Prop | Type | Description | 55 + | ----------- | ------------------------ | ------------------------------------------------------------------------------------------------ | 56 + | `query` | `string \| DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | 57 + | `variables` | `?object` | The variables to be used with the GraphQL request. | 58 + | `pause` | `?boolean` | A boolean flag instructing [execution to be paused](../basics/react-preact.md#pausing-usequery). | 59 + | `context` | `?object` | Holds the contextual information for the query. | 55 60 56 61 The hook optionally accepts a second argument, which may be a handler function with a type signature 57 62 of:
+5
docs/api/vue.md
··· 5 5 6 6 # Vue API 7 7 8 + > **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9 + > You can view TSDocs while using these packages in your editor, as long as it supports the 10 + > TypeScript Language Server. 11 + > We're planning to replace these API docs with a separate web app soon. 12 + 8 13 ## useQuery 9 14 10 15 Accepts a single required options object as an input with the following properties:
+110 -91
docs/architecture.md
··· 5 5 6 6 # Architecture 7 7 8 - `urql` is a highly customizable and flexible GraphQL client, that happens to come with some default 9 - [core behavior in the core package](./basics/core.md). 8 + `urql` is a highly customizable and flexible GraphQL client. 9 + As 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/code.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`. 10 16 11 17 By default, `urql` aims to provide the minimal amount of features that allow us to build an app 12 18 quickly. However, `urql` has also been designed to be a GraphQL Client ··· 34 40 ## Requests and Operations on the Client 35 41 36 42 If `urql` was a train it would take several stops to arrive at its terminus, our API. It starts with us 37 - defining queries or mutations. Any GraphQL request can be abstracted into their query documents and 38 - their variables. In `urql`, these GraphQL requests are treated as unique objects, which are uniquely 39 - identified by the query document and variables (which is why a `key` is generated from the two). This 40 - `key` is a hash number of the query document and variables and uniquely identifies our 41 - [`GraphQLRequest`](./api/core.md#graphqlrequest). 43 + defining queries or mutations by writing in GraphQL's query language. 44 + 45 + Any GraphQL request can be abstracted into its query documents and its variables. 46 + 47 + ```js 48 + import { gql } from '@urql/core'; 49 + 50 + const query = gql` 51 + query($name: String!) { 52 + helloWorld(name: $name) 53 + } 54 + `; 55 + 56 + const request = createRequest(query, { 57 + name: 'Urkel', 58 + }); 59 + ``` 60 + 61 + In `urql`, these GraphQL requests are treated as unique objects and each GraphQL request will have 62 + a `key` generated for them. This `key` is a hash of the query document and the variables you provide 63 + and are set on the `key` property of a [`GraphQLRequest`](./api/core.md#graphqlrequest). 42 64 43 - Whenever we decide to send a request to our API we start by using `urql`'s 44 - [`Client`](./api/core.md#client). It accepts several options like `url` or `requestPolicy` which are 45 - extra information on how the GraphQL requests are executed. 65 + Whenever we decide to send our GraphQL requests to a GraphQL API we start by using `urql`'s 66 + [`Client`](./api/core.md#client). 67 + The `Client` accepts several options to configure its behaviour and the behaviour of exchanges, 68 + like the `fetchExchange`. For instance, we can pass it a `url` which the `fetchExchange` will 69 + use to make a `fetch` call to our GraphQL API. 46 70 47 71 ```js 48 - import { Client, dedupExchange, cacheExchange, fetchExchange } from '@urql/core'; 72 + import { Client, cacheExchange, fetchExchange } from '@urql/core'; 49 73 50 - new Client({ 74 + const client = new Client({ 51 75 url: 'http://localhost:3000/graphql', 52 - requestPolicy: 'cache-first', 53 - exchanges: [dedupExchange, cacheExchange, fetchExchange] 76 + exchanges: [cacheExchange, fetchExchange], 54 77 }); 55 78 ``` 56 79 57 - The bindings that we've seen in [the "Basics" section](./basics/README.md) interact with [the 58 - `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" 80 + Above, we're defining a `Client` that is ready to accept our requests. It will apply basic 81 + document caching and will send uncached requests to the `url` we pass it. 82 + The bindings that we've seen in [the "Basics" section](./basics/README.md), like `useQuery` for 83 + React for example, interact with [the `Client`](./api/core.md#client) directly and are a thin 84 + abstraction. 85 + 86 + Some methods can be called on it directly however, as seen [on the "Core Usage" 59 87 page](./basics/core.md#one-off-queries-and-mutations). 60 88 61 - When we send our queries or mutations to the `Client`, internally they will be managed as 62 - [`Operation`](./api/core.md#operation)s. An "Operation" is an extension of `GraphQLRequest`s. Not 63 - only do they carry the `query`, `variables`, and a `key` property, they will also identify the 64 - `kind` of operation that is executed, like `"query"` or `"mutation"`. We can also find the 65 - `Client`'s options on `operation.context` which carries an operation's metadata. 89 + ```js 90 + // Given our request and client defined above, we can call 91 + const subscription = client.executeQuery(request).subscribe(result => { 92 + console.log(result.data); 93 + }); 94 + ``` 95 + 96 + As 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 98 + needed, like our `url` option on the `Client`, `urql` internally creates [`Operation`s](./api/core.md#operation) 99 + each time a request is executed. The operations are then forwarded to the exchanges, like the 100 + `cacheExchange` and `fetchExchange`. 101 + 102 + An "Operation" is an extension of `GraphQLRequest`s. Not only do they carry the `query`, `variables`, 103 + and 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`. 66 105 67 106 ![Operations and Results](./assets/urql-event-hub.png) 68 107 69 - It's the `Client`s responsibility to accept an `Operation` and execute it. The bindings internally 70 - call the `client.executeQuery`, `client.executeMutation`, or `client.executeSubscription` methods, 71 - and we'll get a "stream" of results. This "stream" allows us to register a callback with it to 72 - receive results. 108 + This means, once we hand over a GraphQL request to the `Client`, it will create an `Operation`, 109 + and then hand it over to the exchanges until a result comes back. 73 110 74 - In the diagram we can see that each operation is a signal for our request to start at which point 75 - we can expect to receive our results eventually on a callback. Once we're not interested in results 76 - anymore a special "teardown" signal is issued on the `Client`. While we don't see operations outside 77 - the `Client`, they're what travel through the "Exchanges" on the `Client`. 111 + As shown in the diagram, each operation is like an event or signal for a GraphQL request to start, 112 + and the exchanges will eventually send back a corresponding result. 113 + However, because the cache can send updates to us whenever it detects a change, or you could cancel 114 + a GraphQL request before it finishes, a special "teardown" `Operation` also exists, which cancels 115 + ongoing requests. 78 116 79 117 ## The Client and Exchanges 80 118 81 119 To reiterate, when we use `urql`'s bindings for our framework of choice, methods are called on the 82 120 `Client`, but we never see the operations that are created in the background from our bindings. We 83 121 call a method like `client.executeQuery` (or it's called for us in the bindings), an operation is 84 - issued internally when we subscribe with a callback, and later our callback is called with results. 122 + issued internally when we subscribe with a callback, and later, we're given results. 85 123 86 124 ![Operations stream and results stream](./assets/urql-client-architecture.png) 87 125 ··· 126 164 127 165 ### The Exchanges 128 166 129 - The default set of exchanges that `@urql/core` contains and applies to a `Client` are: 167 + By default, the `Client` doesn't do anything with GraphQL requests. It contains only the logic to 168 + manage and differentiate between active and inactive requests and converts them to operations. 169 + To actually do something with our GraphQL requests, it needs _exchanges_, which are like plugins 170 + that you can pass to create a pipeline of how GraphQL operations are executed. 171 + 172 + By default, you may want to add the `cacheExchange` and the `fetchExchange` from `@urql/core`: 130 173 131 - - `dedupExchange`: Deduplicates pending operations (pending = waiting for a result) 132 - - `cacheExchange`: The default caching logic with ["Document Caching"](./basics/document-caching.md) 133 - - `fetchExchange`: Sends an operation to the API using `fetch` and adds results to the output stream 174 + - `cacheExchange`: Caches GraphQL results with ["Document Caching"](./basics/document-caching.md) 175 + - `fetchExchange`: Executes GraphQL requests with a `fetch` HTTP call 134 176 135 - When we don't pass the `exchanges` option manually to our `Client` then these are the ones that will 136 - be applied. As we can see, an exchange exerts a lot of power over our operations and results. They 137 - determine a lot of the logic of the `Client`, taking care of things like deduplication, caching, and 138 - sending requests to our API. 177 + ```js 178 + import { Client, cacheExchange, fetchExchange } from '@urql/core'; 139 179 140 - Some of the exchanges that are available to us are: 180 + const client = new Client({ 181 + url: 'http://localhost:3000/graphql', 182 + exchanges: [cacheExchange, fetchExchange], 183 + }); 184 + ``` 141 185 142 - - [`mapExchange`](./api/core.md#mapexchange): Allows reacting to operations, results, and errors 186 + As we can tell, exchanges define not only how GraphQL requests are executed and handled, but also 187 + get control over caching. Exchanges can be used to change almost any behaviour in the `Client`, 188 + although internally they only handle incoming & outgoing requests and incoming & outgoing results. 189 + 190 + Some 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 143 193 - [`ssrExchange`](./advanced/server-side-rendering.md): Allows for a server-side renderer to 144 194 collect results for client-side rehydration. 145 - - [`retryExchange`](./advanced/retry-operations.md): Allows operations to be retried 146 - - [`multipartFetchExchange`](./advanced/persistence-and-uploads.md#file-uploads): Provides multipart file upload capability 195 + - [`retryExchange`](./advanced/retry-operations.md): Allows operations to be retried on errors 147 196 - [`persistedFetchExchange`](./advanced/persistence-and-uploads.md#automatic-persisted-queries): Provides support for Automatic 148 197 Persisted Queries 149 - - [`authExchange`](./advanced/authentication.md): Allows complex authentication flows to be implemented 150 - easily. 151 - - [`requestPolicyExchange`](./api/request-policy-exchange.md): Automatically upgrades `cache-only` and `cache-first` operations to `cache-and-network` after a given amount of time. 152 - - [`refocusExchange`](./api/refocus-exchange.md): Tracks open queries and refetches them 153 - when the window regains focus. 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. 154 200 - `devtoolsExchange`: Provides the ability to use the [urql-devtools](https://github.com/urql-graphql/urql-devtools) 155 201 156 202 We can even swap out our [document cache](./basics/document-caching.md), which is implemented by ··· 182 228 directly](./basics/core.md#one-off-queries-and-mutations) or write exchanges then we'll see streams 183 229 and will have to deal with their API. 184 230 185 - ### The Wonka library 186 - 187 - `urql` utilises the [Wonka](https://github.com/kitten/wonka) library for its streams. It has a 188 - few advantages that are specifically tailored for the `urql` library and ecosystem: 189 - 190 - - It is extremely lightweight and treeshakeable, with a size of around 3.7kB minzipped. 191 - - It's cross-platform and cross-language compatible, having been written in 192 - [Reason](https://reasonml.github.io/) and provides support for [Flow](https://flow.org/) 193 - and [TypeScript](https://www.typescriptlang.org/v2/). 194 - - It's a predictable and iterable toolchain, emitting synchronous events whenever possible. 195 - 196 - Typical usage of Wonka will involve creating a _source_ of some values and a _sink_. 197 - 198 - ```js 199 - import { fromArray, map, subscribe, pipe } from 'wonka'; 200 - 201 - const { unsubscribe } = pipe( 202 - fromArray([1, 2, 3]), 203 - map(x => x * 2), 204 - subscribe(x => { 205 - console.log(x); // 2, 4, 6 206 - }) 207 - ); 208 - ``` 209 - 210 - In Wonka, like with Observables, streams are cancellable by calling the `unsubscribe` method that a 211 - subscription returns. 212 - 213 - [Read more about Wonka in its documentation](https://wonka.kitten.sh/basics/background). 214 - 215 231 ### Stream patterns with the client 216 232 217 233 When we call methods on the `Client` like [`client.executeQuery`](./api/core.md#clientexecutequery) 218 - or [`client.query`](./api/core.md#clientquery) then these will return a Wonka stream. Those are 219 - essentially just a bunch of callbacks. 234 + or [`client.query`](./api/core.md#clientquery) then these will return a "stream" of results. 220 235 221 - We can use [`wonka`'s `subscribe`](https://wonka.kitten.sh/api/sinks#subscribe) function to start 222 - this stream. We pass this function a callback and will receive results back from the `Client`, as it 223 - starts our operation. When we unsubscribe then the `Client` will stop this operation by sending a 224 - special "teardown" operation to our exchanges. 236 + It's normal for GraphQL subscriptions to deliver multiple results, however, even GraphQL queries can 237 + give you multiple results in `urql`. This is because operations influence one another. When a cache 238 + invalidates a query, this query may refetch, and a new result is delivered to your application. 239 + 240 + Multiple results mean that once you subscribe to a GraphQL query via the `Client`, you may receive 241 + new results in the future. 225 242 226 243 ```js 227 - import { pipe, subscribe } from 'wonka'; 244 + import { gql } from '@urql/core'; 228 245 229 - const QUERY = ` 246 + const QUERY = gql` 230 247 query Test($id: ID!) { 231 248 getUser(id: $id) { 232 249 id ··· 235 252 } 236 253 `; 237 254 238 - const { unsubscribe } = pipe( 239 - client.query(QUERY, { id: 'test' }), 240 - subscribe(result => { 241 - console.log(result); // { data: ... } 242 - }) 243 - ); 255 + client.query(QUERY, { id: 'test' }).subscribe(result => { 256 + console.log(result); // { data: ... } 257 + }); 244 258 ``` 245 259 246 260 Read more about the available APIs on the `Client` in the [Core API docs](./api/core.md). 261 + 262 + Internally, 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 264 + library. It is used to write exchanges and when we interact with the `Client` it is used internally 265 + as well.
+56 -32
docs/basics/core.md
··· 27 27 `@urql/core` or use it standalone, e.g. in a Node.js environment. 28 28 29 29 ```sh 30 - yarn add @urql/core graphql 30 + yarn add @urql/core 31 31 # or 32 - npm install --save @urql/core graphql 32 + npm install --save @urql/core 33 33 ``` 34 34 35 35 Since all bindings and all exchanges depend on `@urql/core`, we may sometimes run into problems ··· 39 39 deduplicating our dependencies. 40 40 41 41 ```sh 42 + # npm 43 + npm dedupe 44 + # pnpm 45 + pnpm dedupe 46 + # yarn 42 47 npx yarn-deduplicate && yarn 43 - # or 44 - npm dedupe 45 48 ``` 46 49 47 50 ## GraphQL Tags ··· 130 133 131 134 ### Setting up the `Client` 132 135 133 - The `@urql/core` package exports a function called `createClient` which we can use to 136 + The `@urql/core` package exports a `Client` class, which we can use to 134 137 create the GraphQL client. This central `Client` manages all of our GraphQL requests and results. 135 138 136 139 ```js 137 - import { createClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/core'; 140 + import { Client, cacheExchange, fetchExchange } from '@urql/core'; 138 141 139 - const client = createClient({ 142 + const client = new Client({ 140 143 url: 'http://localhost:3000/graphql', 141 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 144 + exchanges: [cacheExchange, fetchExchange], 142 145 }); 143 146 ``` 144 147 145 - At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started. 148 + At the bare minimum we'll need to pass an API's `url`, and the `fetchExchange`, 149 + when we create a `Client` to get started. 146 150 147 151 Another common option is `fetchOptions`. This option allows us to customize the options that will be 148 152 passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object, or ··· 152 156 GraphQL API. 153 157 154 158 ```js 155 - const client = createClient({ 159 + const client = new Client({ 156 160 url: 'http://localhost:3000/graphql', 157 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 161 + exchanges: [cacheExchange, fetchExchange], 158 162 fetchOptions: () => { 159 163 const token = getToken(); 160 164 return { ··· 166 170 167 171 ### The `Client`s options 168 172 169 - As we've seen above, the most important option for the `Client` is `url`, since it won't work 170 - without it. However, another important option on the `Client` is the `exchanges` option. 173 + As we've seen above, the most important options for the `Client` are `url` and `exchanges`. 174 + The `url` option is used by the `fetchExchange` to send GraphQL requests to an API. 171 175 172 - This option passes a list of exchanges to the `Client`, which tell it how to execute our requests 173 - and how to cache data in a certain order. 176 + The `exchanges` option is of particular importance however because it tells the `Client` what to do 177 + with our GraphQL requests: 174 178 175 179 ```js 176 - import { createClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/core'; 180 + import { Client, cacheExchange, fetchExchange } from '@urql/core'; 177 181 178 - const client = createClient({ 182 + const client = new Client({ 179 183 url: 'http://localhost:3000/graphql', 180 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 184 + exchanges: [cacheExchange, fetchExchange], 181 185 }); 182 186 ``` 183 187 188 + For instance, here, the `Client`'s caching and fetching features are only available because we're 189 + passing it exchanges. In the above example, the `Client` will try to first read a GraphQL request 190 + from a local cache, and if this request isn't cached it'll make an HTTP request. 191 + The caching in `urql` is also implemented as an exchange, so for instance, the behavior described 192 + on the ["Document Caching" page](./document-caching.md) is all contained within the `cacheExchange` 193 + above. 194 + 184 195 Later, [in the "Advanced" section](../advanced/README.md) we'll see many more features that `urql` 185 196 supports by adding new exchanges to this list. On [the "Architecture" page](../architecture.md) 186 197 we'll also learn more about what exchanges are and why they exist. 187 - 188 - For now, it's enough for us to know that our requests are executed using the logic in the 189 - exchanges in order. First, the `dedupExchange` deduplicates requests if we send the same queries 190 - twice, the `cacheExchange` implements the default "document caching" behaviour (as we'll learn about 191 - on the ["Document Caching"](./document-caching.md) page), and lastly the `fetchExchange` is 192 - responsible for sending our requests to our GraphQL API. 193 198 194 199 ### One-off Queries and Mutations 195 200 ··· 220 225 result as a promise. This may be useful when we don't plan on cancelling queries, or we don't 221 226 care about future updates to this data and are just looking to query a result once. 222 227 228 + This can also be written using async/await by simply awaiting the return value of `client.query`: 229 + 230 + ```js 231 + const QUERY = ` 232 + query Test($id: ID!) { 233 + getUser(id: $id) { 234 + id 235 + name 236 + } 237 + } 238 + `; 239 + 240 + async function query() { 241 + const result = await client.query(QUERY, { id: 'test' }); 242 + console.log(result); // { data: ... } 243 + } 244 + ``` 245 + 223 246 The same can be done for mutations by calling the `client.mutation` method instead of the 224 247 `client.query` method. 248 + 249 + It's worth noting that promisifying a query result will always only give us _one_ result, because 250 + we're not calling `subscribe`. This means that we'll never see cache updates when we're asking for 251 + a single result like we do above. 252 + 253 + #### Reading only cache data 225 254 226 255 Similarly there's a way to read data from the cache synchronously, provided that the cache has 227 256 received a result for a given query before. The `Client` has a `readQuery` method, which is a ··· 258 287 somewhere else then we'll get notified of the new API result as well, as long as we're subscribed. 259 288 260 289 ```js 261 - import { pipe, subscribe } from 'wonka'; 262 - 263 290 const QUERY = ` 264 291 query Test($id: ID!) { 265 292 getUser(id: $id) { ··· 269 296 } 270 297 `; 271 298 272 - const { unsubscribe } = pipe( 273 - client.query(QUERY, { id: 'test' }), 274 - subscribe(result => { 275 - console.log(result); // { data: ... } 276 - }) 277 - ); 299 + const { unsubscribe } = client.query(QUERY, { id: 'test' }).subscribe(result => { 300 + console.log(result); // { data: ... } 301 + }); 278 302 ``` 279 303 280 304 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
··· 18 18 with at first. We'll install the package with our package manager of choice. 19 19 20 20 ```sh 21 - yarn add urql graphql 21 + yarn add urql 22 22 # or 23 - npm install --save urql graphql 23 + npm install --save urql 24 24 ``` 25 25 26 26 To use `urql` with Preact, we have to install `@urql/preact` instead of `urql` and import from ··· 37 37 38 38 ### Setting up the `Client` 39 39 40 - The `urql` and `@urql/preact` packages export a method called `createClient` which we can use to 40 + The `urql` and `@urql/preact` packages export a `Client` class, which we can use to 41 41 create the GraphQL client. This central `Client` manages all of our GraphQL requests and results. 42 42 43 43 ```js 44 - import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql'; 44 + import { Client, cacheExchange, fetchExchange } from 'urql'; 45 45 46 - const client = createClient({ 46 + const client = new Client({ 47 47 url: 'http://localhost:3000/graphql', 48 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 48 + exchanges: [cacheExchange, fetchExchange], 49 49 }); 50 50 ``` 51 51 52 - At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started. 52 + At the bare minimum we'll need to pass an API's `url` and `exchanges` when we create a `Client` 53 + to get started. 53 54 54 55 Another common option is `fetchOptions`. This option allows us to customize the options that will be 55 56 passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object, or ··· 59 60 GraphQL API. 60 61 61 62 ```js 62 - const client = createClient({ 63 + const client = new Client({ 63 64 url: 'http://localhost:3000/graphql', 64 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 65 + exchanges: [cacheExchange, fetchExchange], 65 66 fetchOptions: () => { 66 67 const token = getToken(); 67 68 return { ··· 78 79 the `Provider` export. 79 80 80 81 ```jsx 81 - import { createClient, Provider, dedupExchange, cacheExchange, fetchExchange } from 'urql'; 82 + import { Client, Provider, cacheExchange, fetchExchange } from 'urql'; 82 83 83 - const client = createClient({ 84 + const client = new Client({ 84 85 url: 'http://localhost:3000/graphql', 85 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 86 + exchanges: [cacheExchange, fetchExchange], 86 87 }); 87 88 88 89 const App = () => ( ··· 107 108 todo items. Let's dive right into it! 108 109 109 110 ```jsx 110 - import { useQuery } from 'urql'; 111 + import { gql, useQuery } from 'urql'; 111 112 112 - const TodosQuery = ` 113 + const TodosQuery = gql` 113 114 query { 114 115 todos { 115 116 id ··· 154 155 to supply variables to our query. 155 156 156 157 ```jsx 157 - const TodosListQuery = ` 158 + const TodosListQuery = gql` 158 159 query ($from: Int!, $limit: Int!) { 159 - todos (from: $from, limit: $limit) { 160 + todos(from: $from, limit: $limit) { 160 161 id 161 162 title 162 163 } ··· 198 199 199 200 ```jsx 200 201 const Todos = ({ from, limit }) => { 201 - const shouldPause = from === undefined || from === null || 202 - limit === undefined || limit === null; 202 + const shouldPause = from === undefined || from === null || limit === undefined || limit === null; 203 203 const [result, reexecuteQuery] = useQuery({ 204 204 query: TodosListQuery, 205 205 variables: { from, limit }, ··· 244 244 we may also change the `Client`'s default `requestPolicy` by passing it there. 245 245 246 246 ```js 247 - import { createClient } from 'urql'; 247 + import { Client, cacheExchange, fetchExchange } from 'urql'; 248 248 249 - const client = createClient({ 249 + const client = new Client({ 250 250 url: 'http://localhost:3000/graphql', 251 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 251 + exchanges: [cacheExchange, fetchExchange], 252 252 // every operation will by default use cache-and-network rather 253 253 // than cache-first now: 254 254 requestPolicy: 'cache-and-network',
+17 -16
docs/basics/svelte.md
··· 17 17 Installing `@urql/svelte` is quick and no other packages are immediately necessary. 18 18 19 19 ```sh 20 - yarn add @urql/svelte graphql 20 + yarn add @urql/svelte 21 21 # or 22 - npm install --save @urql/svelte graphql 22 + npm install --save @urql/svelte 23 23 ``` 24 24 25 25 Most libraries related to GraphQL also need the `graphql` package to be installed as a peer ··· 44 44 45 45 ### Setting up the `Client` 46 46 47 - The `@urql/svelte` package exports a method called `createClient` which we can use to create 47 + The `@urql/svelte` package exports a `Client` class, which we can use to create 48 48 the GraphQL client. This central `Client` manages all of our GraphQL requests and results. 49 49 50 50 ```js 51 - import { createClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/svelte'; 51 + import { Client, cacheExchange, fetchExchange } from '@urql/svelte'; 52 52 53 - const client = createClient({ 53 + const client = new Client({ 54 54 url: 'http://localhost:3000/graphql', 55 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 55 + exchanges: [cacheExchange, fetchExchange], 56 56 }); 57 57 ``` 58 58 59 - At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started. 59 + At the bare minimum we'll need to pass an API's `url` and `exchanges` 60 + when we create a `Client` to get started. 60 61 61 62 Another common option is `fetchOptions`. This option allows us to customize the options that will be 62 63 passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object or ··· 66 67 GraphQL API. 67 68 68 69 ```js 69 - const client = createClient({ 70 + const client = new Client({ 70 71 url: 'http://localhost:3000/graphql', 71 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 72 + exchanges: [cacheExchange, fetchExchange], 72 73 fetchOptions: () => { 73 74 const token = getToken(); 74 75 return { ··· 87 88 88 89 ```html 89 90 <script> 90 - import { createClient, setContextClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/svelte'; 91 + import { Client, setContextClient, cacheExchange, fetchExchange } from '@urql/svelte'; 91 92 92 - const client = createClient({ 93 + const client = new Client({ 93 94 url: 'http://localhost:3000/graphql', 94 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 95 + exchanges: [cacheExchange, fetchExchange], 95 96 }); 96 97 97 98 setContextClient(client); ··· 291 292 we may also change the `Client`'s default `requestPolicy` by passing it there. 292 293 293 294 ```js 294 - import { createClient } from '@urql/svelte'; 295 + import { Client, cacheExchange, fetchExchange } from '@urql/svelte'; 295 296 296 - const client = createClient({ 297 + const client = new Client({ 297 298 url: 'http://localhost:3000/graphql', 298 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 299 + exchanges: [cacheExchange, fetchExchange], 299 300 // every operation will by default use cache-and-network rather 300 301 // than cache-first now: 301 302 requestPolicy: 'cache-and-network', ··· 400 401 result = mutationStore({ 401 402 client, 402 403 query: gql` 403 - mutation($id: ID!, $title: String!) { 404 + mutation ($id: ID!, $title: String!) { 404 405 updateTodo(id: $id, title: $title) { 405 406 id 406 407 title
+6 -5
docs/basics/typescript-integration.md
··· 14 14 To get and running, install the following packages: 15 15 16 16 ```sh 17 - yarn add graphql 18 - yarn add -D typescript @graphql-codegen/cli @graphql-codegen/client-preset 17 + yarn add -D graphql typescript @graphql-codegen/cli @graphql-codegen/client-preset 19 18 # or 20 - npm install graphql 21 - npm install -D typescript @graphql-codegen/cli @graphql-codegen/client-preset 19 + npm install -D graphql typescript @graphql-codegen/cli @graphql-codegen/client-preset 22 20 ``` 23 21 24 22 Then, add the following script to your `package.json`: ··· 140 138 141 139 function App() { 142 140 // `data` is typed! 143 - const [{ data }] = useQuery({ query: allFilmsWithVariablesQueryDocument, variables: { first: 10 } }); 141 + const [{ data }] = useQuery({ 142 + query: allFilmsWithVariablesQueryDocument, 143 + variables: { first: 10 }, 144 + }); 144 145 return ( 145 146 <div className="App"> 146 147 {data && (
+3 -3
docs/basics/ui-patterns.md
··· 169 169 else our requests will never get upgraded. 170 170 171 171 ```js 172 - import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql'; 172 + import { Client, cacheExchange, fetchExchange } from 'urql'; 173 173 import { refocusExchange } from '@urql/exchange-refocus'; 174 174 175 - const client = createClient({ 175 + const client = new Client({ 176 176 url: 'some-url', 177 - exchanges: [dedupExchange, refocusExchange(), cacheExchange, fetchExchange], 177 + exchanges: [refocusExchange(), cacheExchange, fetchExchange], 178 178 }); 179 179 ``` 180 180
+42 -41
docs/basics/vue.md
··· 33 33 34 34 ### Setting up the `Client` 35 35 36 - The `@urql/vue` package exports a method called `createClient` which we can use to create 36 + The `@urql/vue` package exports a `Client` class, which we can use to create 37 37 the GraphQL client. This central `Client` manages all of our GraphQL requests and results. 38 38 39 39 ```js 40 - import { createClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/vue'; 40 + import { Client, cacheExchange, fetchExchange } from '@urql/vue'; 41 41 42 - const client = createClient({ 42 + const client = new Client({ 43 43 url: 'http://localhost:3000/graphql', 44 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 44 + exchanges: [cacheExchange, fetchExchange], 45 45 }); 46 46 ``` 47 47 48 - At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started. 48 + At the bare minimum we'll need to pass an API's `url` and `exchanges` when we create a `Client` 49 + to get started. 49 50 50 51 Another common option is `fetchOptions`. This option allows us to customize the options that will be 51 52 passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object or ··· 55 56 GraphQL API. 56 57 57 58 ```js 58 - const client = createClient({ 59 + const client = new Client({ 59 60 url: 'http://localhost:3000/graphql', 60 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 61 + exchanges: [cacheExchange, fetchExchange], 61 62 fetchOptions: () => { 62 63 const token = getToken(); 63 64 return { ··· 75 76 76 77 The first method is to use `@urql/vue`'s `provideClient` function. This must be called in any of 77 78 your parent components and accepts either a `Client` directly or just the options that you'd pass to 78 - `createClient`. 79 + `Client`. 79 80 80 81 ```html 81 82 <script> 82 - import { createClient, provideClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/vue'; 83 + import { Client, provideClient, cacheExchange, fetchExchange } from '@urql/vue'; 83 84 84 - const client = createClient({ 85 + const client = new Client({ 85 86 url: 'http://localhost:3000/graphql', 86 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 87 + exchanges: [cacheExchange, fetchExchange], 87 88 }); 88 89 89 90 provideClient(client); ··· 107 108 app.mount('#app'); 108 109 ``` 109 110 110 - The plugin also accepts `createClient`'s options or a `Client` as its inputs. 111 + The plugin also accepts `Client`'s options or a `Client` as its inputs. 111 112 112 113 ## Queries 113 114 ··· 134 135 </template> 135 136 136 137 <script> 137 - import { useQuery } from '@urql/vue'; 138 + import { gql, useQuery } from '@urql/vue'; 138 139 139 140 export default { 140 141 setup() { 141 142 const result = useQuery({ 142 - query: ` 143 + query: gql` 143 144 { 144 145 todos { 145 146 id ··· 184 185 </template> 185 186 186 187 <script> 187 - import { useQuery } from '@urql/vue'; 188 + import { gql, useQuery } from '@urql/vue'; 188 189 189 190 export default { 190 191 props: ['from', 'limit'], 191 192 setup({ from, limit }) { 192 193 return useQuery({ 193 - query: ` 194 + query: gql` 194 195 query ($from: Int!, $limit: Int!) { 195 196 todos(from: $from, limit: $limit) { 196 197 id ··· 221 222 </template> 222 223 223 224 <script> 224 - import { useQuery } from '@urql/vue'; 225 + import { gql, useQuery } from '@urql/vue'; 225 226 226 227 export default { 227 228 setup() { 228 229 const from = ref(0); 229 230 230 231 const result = useQuery({ 231 - query: ` 232 + query: gql` 232 233 query ($from: Int!, $limit: Int!) { 233 234 todos(from: $from, limit: $limit) { 234 235 id ··· 266 267 267 268 ```js 268 269 import { reactive } from 'vue' 269 - import { useQuery } from '@urql/vue'; 270 + import { gql, useQuery } from '@urql/vue'; 270 271 271 272 export default { 272 273 props: ['from', 'limit'], 273 274 setup({ from, limit }) { 274 275 const shouldPause = computed(() => from == null || limit == null); 275 276 return useQuery({ 276 - query: ` 277 + query: gql` 277 278 query ($from: Int!, $limit: Int!) { 278 279 todos(from: $from, limit: $limit) { 279 280 id ··· 307 308 </template> 308 309 309 310 <script> 310 - import { useQuery } from '@urql/vue'; 311 + import { gql, useQuery } from '@urql/vue'; 311 312 312 313 export default { 313 314 setup() { 314 315 return useQuery({ 315 - query: ` 316 + query: gql` 316 317 { 317 318 todos { 318 319 id ··· 364 365 we may also change the `Client`'s default `requestPolicy` by passing it there. 365 366 366 367 ```js 367 - import { createClient, dedupExchange, cacheExchange, fetchExchange } from '@urql/vue'; 368 + import { Client, cacheExchange, fetchExchange } from '@urql/vue'; 368 369 369 - const client = createClient({ 370 + const client = new Client({ 370 371 url: 'http://localhost:3000/graphql', 371 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 372 + exchanges: [cacheExchange, fetchExchange], 372 373 // every operation will by default use cache-and-network rather 373 374 // than cache-first now: 374 375 requestPolicy: 'cache-and-network', ··· 415 416 `requestPolicy` of our query just once and set it to `network-only` to skip the cache. 416 417 417 418 ```js 418 - import { useQuery } from '@urql/vue'; 419 + import { gql, useQuery } from '@urql/vue'; 419 420 420 421 export default { 421 422 setup() { 422 423 const result = useQuery({ 423 - query: ` 424 + query: gql` 424 425 { 425 426 todos { 426 427 id ··· 473 474 </template> 474 475 475 476 <script> 476 - import { useQuery } from '@urql/vue'; 477 + import { gql, useQuery } from '@urql/vue'; 477 478 478 479 export default { 479 480 async setup() { 480 481 const { data, error } = await useQuery({ 481 - query: ` 482 + query: gql` 482 483 { 483 484 todos { 484 485 id ··· 580 581 example! We'll set up a mutation that _updates_ a todo item's title. 581 582 582 583 ```js 583 - import { useMutation } from '@urql/vue'; 584 + import { gql, useMutation } from '@urql/vue'; 584 585 585 586 export default { 586 587 setup() { 587 - const { executeMutation: updateTodo } = useMutation(` 588 - mutation ($id: ID!, $title: String!) { 589 - updateTodo (id: $id, title: $title) { 588 + const { executeMutation: updateTodo } = useMutation(gql` 589 + mutation($id: ID!, $title: String!) { 590 + updateTodo(id: $id, title: $title) { 590 591 id 591 592 title 592 593 } ··· 616 617 `executeMutation` method returns when it's called: 617 618 618 619 ```js 619 - import { useMutation } from '@urql/vue'; 620 + import { gql, useMutation } from '@urql/vue'; 620 621 621 622 export default { 622 623 setup() { 623 - const updateTodoResult = useMutation(` 624 - mutation ($id: ID!, $title: String!) { 625 - updateTodo (id: $id, title: $title) { 624 + const updateTodoResult = useMutation(gql` 625 + mutation($id: ID!, $title: String!) { 626 + updateTodo(id: $id, title: $title) { 626 627 id 627 628 title 628 629 } ··· 657 658 [Read more about errors on our "Errors" page.](./errors.md) 658 659 659 660 ```js 660 - import { useMutation } from '@urql/vue'; 661 + import { gql, useMutation } from '@urql/vue'; 661 662 662 663 export default { 663 664 setup() { 664 - const updateTodoResult = useMutation(` 665 - mutation ($id: ID!, $title: String!) { 666 - updateTodo (id: $id, title: $title) { 665 + const updateTodoResult = useMutation(gql` 666 + mutation($id: ID!, $title: String!) { 667 + updateTodo(id: $id, title: $title) { 667 668 id 668 669 title 669 670 }
+46 -47
docs/comparison.md
··· 35 35 36 36 ### Core Features 37 37 38 - | | urql | Apollo | Relay | 39 - | ------------------------------------------ | ---------------------------------- | -------------------------------------------------------------------------- | ------------------------------ | 40 - | Extensible on a network level | ✅ Exchanges | ✅ Links | ✅ Network Layers | 41 - | Extensible on a cache / control flow level | ✅ Exchanges | 🛑 | 🛑 | 42 - | Base Bundle Size | **5.9kB** (7.1kB with bindings) | 32.9kB | 27.7kB (34.1kB with bindings) | 43 - | Devtools | ✅ | ✅ | ✅ | 44 - | Subscriptions | ✅ | ✅ | ✅ | 45 - | Client-side Rehydration | ✅ | ✅ | ✅ | 46 - | Polled Queries | 🔶 | ✅ | ✅ | 47 - | Lazy Queries | ✅ | ✅ | ✅ | 48 - | Stale while Revalidate / Cache and Network | ✅ | ✅ | ✅ | 49 - | Focus Refetching | ✅ `@urql/exchange-refocus` | 🛑 | 🛑 | 50 - | Stale Time Configuration | ✅ `@urql/exchange-request-policy` | ✅ | 🛑 | 51 - | Persisted Queries | ✅ `@urql/exchange-persisted` | ✅ `apollo-link-persisted-queries` | ✅ | 52 - | Batched Queries | 🛑 | ✅ `apollo-link-batch-http` | 🟡 `react-relay-network-layer` | 53 - | Live Queries | 🛑 | 🛑 | ✅ | 54 - | Defer & Stream Directives | ✅ | ✅ / 🛑 (`@defer` is supported in >=3.7.0, `@stream` is not yet supported) | 🟡 (unreleased) | 55 - | Switching to `GET` method | ✅ | ✅ | 🟡 `react-relay-network-layer` | 56 - | File Uploads | ✅ | 🟡 `apollo-upload-client` | 🛑 | 57 - | Retrying Failed Queries | ✅ `@urql/exchange-retry` | ✅ `apollo-link-retry` | ✅ `DefaultNetworkLayer` | 58 - | Easy Authentication Flows | ✅ `@urql/exchange-auth` | 🛑 (no docs for refresh-based authentication) | 🟡 `react-relay-network-layer` | 59 - | Automatic Refetch after Mutation | ✅ (with document cache) | 🛑 | ✅ | 38 + | | urql | Apollo | Relay | 39 + | ------------------------------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | 40 + | Extensible on a network level | ✅ Exchanges | ✅ Links | ✅ Network Layers | 41 + | Extensible on a cache / control flow level | ✅ Exchanges | 🛑 | 🛑 | 42 + | Base Bundle Size | **10kB** (11kB with bindings) | ~50kB (55kB with React hooks) | 45kB (66kB with bindings) | 43 + | Devtools | ✅ | ✅ | ✅ | 44 + | 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) | 45 + | Client-side Rehydration | ✅ [Docs](./advanced/server-side-rendering.md) | ✅ [Docs](https://www.apollographql.com/docs/react/performance/server-side-rendering) | 🛑 | 46 + | Polled Queries | 🔶 | ✅ | ✅ | 47 + | Lazy Queries | ✅ | ✅ | ✅ | 48 + | Stale while Revalidate / Cache and Network | ✅ | ✅ | ✅ | 49 + | Focus Refetching | ✅ `@urql/exchange-refocus` | 🛑 | 🛑 | 50 + | Stale Time Configuration | ✅ `@urql/exchange-request-policy` | ✅ | 🛑 | 51 + | Persisted Queries | ✅ `@urql/exchange-persisted` | ✅ `apollo-link-persisted-queries` | 🔶 | 52 + | Batched Queries | 🛑 | ✅ `apollo-link-batch-http` | 🟡 `react-relay-network-layer` | 53 + | Live Queries | ✅ (via Incremental Delivery) | 🛑 | ✅ | 54 + | Defer & Stream Directives | ✅ | ✅ / 🛑 (`@defer` is supported in >=3.7.0, `@stream` is not yet supported) | 🟡 (unreleased) | 55 + | Switching to `GET` method | ✅ | ✅ | 🟡 `react-relay-network-layer` | 56 + | File Uploads | ✅ | 🟡 `apollo-upload-client` | 🛑 | 57 + | Retrying Failed Queries | ✅ `@urql/exchange-retry` | ✅ `apollo-link-retry` | ✅ `DefaultNetworkLayer` | 58 + | Easy Authentication Flows | ✅ `@urql/exchange-auth` | 🛑 (no docs for refresh-based authentication) | 🟡 `react-relay-network-layer` | 59 + | Automatic Refetch after Mutation | ✅ (with document cache) | 🛑 | ✅ | 60 60 61 61 Typically these are all additional addon features that you may expect from a GraphQL client, no 62 62 matter which framework you use it with. It's worth mentioning that all three clients support some ··· 82 82 | | urql | Apollo | Relay | 83 83 | ------------------------------ | -------------- | ------------------- | ------------------ | 84 84 | React Bindings | ✅ | ✅ | ✅ | 85 - | React Concurrent Hooks Support | ✅ | 🛑 | ✅ (experimental) | 86 - | React Legacy Hooks Support | ✅ | ✅ | 🟡 `relay-hooks` | 87 - | React Suspense (Experimental) | ✅ | 🛑 | ✅ | 85 + | React Concurrent Hooks Support | ✅ | ✅ | ✅ | 86 + | React Suspense | ✅ | 🛑 | ✅ | 88 87 | Next.js Integration | ✅ `next-urql` | 🟡 | 🔶 | 89 88 | Preact Support | ✅ | 🔶 | 🔶 | 90 89 | Svelte Bindings | ✅ | 🟡 `svelte-apollo` | 🟡 `svelte-relay` | ··· 94 93 95 94 ### Caching and State 96 95 97 - | | urql | Apollo | Relay | 98 - | ------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------- | ---------------------------------------------- | 99 - | Caching Strategy | Document Caching, Normalized Caching with `@urql/exchange-graphcache` | Normalized Caching | Normalized Caching (schema restrictions apply) | 100 - | Added Bundle Size | +6.5kB (with Graphcache) | +0 (default) | +0 (default) | 101 - | Automatic Garbage Collection | ✅ | 🔶 | ✅ | 102 - | Local State Management | 🛑 | ✅ | ✅ | 103 - | Pagination Support | 🔶 | 🔶 | ✅ | 104 - | Optimistic Updates | ✅ | ✅ | ✅ | 105 - | Local Updates | ✅ | ✅ | ✅ | 106 - | Out-of-band Cache Updates | 🛑 (stays true to server data) | ✅ | ✅ | 107 - | Local Resolvers and Redirects | ✅ | ✅ | 🛑 (not needed) | 108 - | Complex Resolvers (nested non-normalized return values) | ✅ | 🛑 | 🛑 (not needed) | 109 - | Commutativity Guarantees | ✅ | 🛑 | ✅ | 110 - | Partial Results | ✅ | ✅ | 🛑 | 111 - | Safe Partial Results (schema-based) | ✅ | 🛑 | 🛑 | 112 - | Persistence Support | ✅ | ✅ `apollo-cache-persist` | 🟡 `@wora/relay-store` | 113 - | Offline Support | ✅ | 🛑 | 🟡 `@wora/relay-offline` | 96 + | | urql | Apollo | Relay | 97 + | ------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------- | ---------------------------------------------- | 98 + | Caching Strategy | Document Caching, Normalized Caching with `@urql/exchange-graphcache` | Normalized Caching | Normalized Caching (schema restrictions apply) | 99 + | Added Bundle Size | +8kB (with Graphcache) | +0 (default) | +0 (default) | 100 + | Automatic Garbage Collection | ✅ | 🔶 | ✅ | 101 + | Local State Management | 🛑 | ✅ | ✅ | 102 + | Pagination Support | 🔶 | 🔶 | ✅ | 103 + | Optimistic Updates | ✅ | ✅ | ✅ | 104 + | Local Updates | ✅ | ✅ | ✅ | 105 + | Out-of-band Cache Updates | 🛑 (stays true to server data) | ✅ | ✅ | 106 + | Local Resolvers and Redirects | ✅ | ✅ | 🛑 | 107 + | Complex Resolvers (nested non-normalized return values) | ✅ | 🛑 | 🛑 | 108 + | Commutativity Guarantees | ✅ | 🛑 | ✅ | 109 + | Partial Results | ✅ | ✅ | 🛑 | 110 + | Safe Partial Results (schema-based) | ✅ | 🔶 (experimental via `useFragment`) | 🛑 | 111 + | Persistence Support | ✅ | ✅ `apollo-cache-persist` | 🟡 `@wora/relay-store` | 112 + | Offline Support | ✅ | 🛑 | 🟡 `@wora/relay-offline` | 114 113 115 114 `urql` is the only of the three clients that doesn't pick [normalized 116 115 caching](./graphcache/normalized-caching.md) as its default caching strategy. Typically this is seen ··· 145 144 but often this comparison is hard to make. When you start comparing bundle sizes of these three 146 145 GraphQL clients you should keep in mind that: 147 146 148 - - Parts of the `graphql` package tree-shake away and may also be replaced (e.g. `parse`) 149 - - All packages in `urql` reuse parts of `@urql/core` and `wonka`, which means adding all their total 147 + - Some dependencies may be external and the above sizes listed are total minified+gzipped sizes 148 + - `@urql/core` imports from `wonka` for stream utilities and `@0no-co/graphql.web` for GraphQL query 149 + language utilities 150 + - Other GraphQL clients may import other exernal dependencies. 151 + - All `urql` packages reuse parts of `@urql/core` and `wonka`, which means adding all their total 150 152 sizes up doesn't give you a correct result of their total expected bundle size. 151 - - These sizes may change drastically given the code you write and add yourself, but can be managed 152 - via precompilation (e.g. with `babel-plugin-graphql-tag` or GraphQL Code Generator for Apollo and 153 - `urql`)
+3 -3
docs/graphcache/README.md
··· 70 70 without passing any options. 71 71 72 72 ```js 73 - import { createClient, dedupExchange, fetchExchange } from 'urql'; 73 + import { Client, fetchExchange } from 'urql'; 74 74 import { cacheExchange } from '@urql/exchange-graphcache'; 75 75 76 - const client = createClient({ 76 + const client = new Client({ 77 77 url: 'http://localhost:3000/graphql', 78 - exchanges: [dedupExchange, cacheExchange({}), fetchExchange], 78 + exchanges: [cacheExchange({}), fetchExchange], 79 79 }); 80 80 ``` 81 81
+6 -6
docs/graphcache/offline.md
··· 32 32 `offlineExchange` that's also exported by `@urql/exchange-graphcache`. 33 33 34 34 ```js 35 - import { createClient, dedupExchange, fetchExchange } from 'urql'; 35 + import { Client, fetchExchange } from 'urql'; 36 36 import { offlineExchange } from '@urql/exchange-graphcache'; 37 37 38 38 const cache = offlineExchange({ ··· 45 45 }, 46 46 }); 47 47 48 - const client = createClient({ 48 + const client = new Client({ 49 49 url: 'http://localhost:3000/graphql', 50 - exchanges: [dedupExchange, cache, fetchExchange], 50 + exchanges: [cache, fetchExchange], 51 51 }); 52 52 ``` 53 53 ··· 61 61 function from `@urql/exchange-graphcache/default-storage`. 62 62 63 63 ```js 64 - import { createClient, dedupExchange, fetchExchange } from 'urql'; 64 + import { Client, fetchExchange } from 'urql'; 65 65 import { offlineExchange } from '@urql/exchange-graphcache'; 66 66 import { makeDefaultStorage } from '@urql/exchange-graphcache/default-storage'; 67 67 ··· 81 81 }, 82 82 }); 83 83 84 - const client = createClient({ 84 + const client = new Client({ 85 85 url: 'http://localhost:3000/graphql', 86 - exchanges: [dedupExchange, cache, fetchExchange], 86 + exchanges: [cache, fetchExchange], 87 87 }); 88 88 ``` 89 89