···7788`urql` is a highly customizable and versatile GraphQL client with which you add on features like
99normalized caching as you grow. It's built to be both easy to use for newcomers to
1010-GraphQL, as well as extensible, to grow to support dynamic single-app applications and highly
1010+GraphQL, and extensible, to grow to support dynamic single-app applications and highly
1111customized GraphQL infrastructure. In short, `urql` prioritizes usability and adaptability.
12121313As you're adopting GraphQL, `urql` becomes your primary data layer and can handle content-heavy
+2-2
docs/advanced/README.md
···2121- [**Authentication**](./authentication.md) describes how to implement authentication using the `authExchange`
2222- [**Testing**](./testing.md) covers how to test components that use `urql` particularly in React.
2323- [**Authoring Exchanges**](./authoring-exchanges.md) describes how to implement exchanges from
2424- scratch and how they work internally. This is a good basis to understanding how some of the
2424+ scratch and how they work internally. This is a good basis to understanding how some
2525 features in this section function.
2626-- [**Auto-populate Mutations**](./auto-populate-mutations.md) presents the `populateExchange` addon which can make it easier to
2626+- [**Auto-populate Mutations**](./auto-populate-mutations.md) presents the `populateExchange` addon, which can make it easier to
2727 update normalized data after mutations.
+35-24
docs/advanced/authentication.md
···12121313## Typical Authentication Flow
14141515-**Initial login** - the user opens the application and authenticates for the first time. They enter their credentials and receive an auth token.
1515+**Initial login** — the user opens the application and authenticates for the first time. They enter their credentials and receive an auth token.
1616The token is saved to storage that is persisted though sessions, e.g. `localStorage` on the web or `AsyncStorage` in React Native. The token is
1717added to each subsequent request in an auth header.
18181919-**Resume** - the user opens the application after having authenticated in the past. In this case, we should already have the token in persisted
1919+**Resume** — the user opens the application after having authenticated in the past. In this case, we should already have the token in persisted
2020storage. We fetch the token from storage and add to each request, usually as an auth header.
21212222-**Forced log out due to invalid token** - the user's session could become invalid for a variety reasons: their token expired, they requested to be
2323-signed out of all devices, or their session was invalidated remotely. In this case, we would want to also log them out in the application so they
2222+**Forced log out due to invalid token** — the user's session could become invalid for a variety reasons: their token expired, they requested to be
2323+signed out of all devices, or their session was invalidated remotely. In this case, we would want to
2424+also log them out in the application, so they
2425could have the opportunity to log in again. To do this, we want to clear any persisted storage, and redirect them to the application home or login page.
25262626-**User initiated log out** - when the user chooses to log out of the application, we usually send a logout request to the API, then clear any tokens
2727+**User initiated log out** — when the user chooses to log out of the application, we usually send a logout request to the API, then clear any tokens
2728from persisted storage, and redirect them to the application home or login page.
28292929-**Refresh (optional)** - this is not always implemented, but given that your API supports it, the user will receive both an auth token and a refresh token,
3030-where the auth token is valid for a shorter duration of time (e.g. 1 week) than the refresh token (e.g. 6 months) and the latter can be used to request a new
3030+**Refresh (optional)** — this is not always implemented; if your API supports it, the
3131+user will receive both an auth token, and a refresh token.
3232+The auth token is usually valid for a shorter duration of time (e.g. 1 week) than the refresh token
3333+(e.g. 6 months), and the latter can be used to request a new
3134auth token if the auth token has expired. The refresh logic is triggered either when the JWT is known to be invalid (e.g. by decoding it and inspecting the expiry date),
3235or when an API request returns with an unauthorized response. For graphQL APIs, it is usually an error code, instead of a 401 HTTP response, but both can be supported.
3336When the token as been successfully refreshed (this can be done as a mutation to the graphQL API or a request to a different API endpoint, depending on implementation),
···8689```
87908891We check that the `authState` doesn't already exist (this indicates that it is the first time this exchange is executed and not an auth failure) and fetch the auth state from
8989-storage. The structure of this particular`authState` is an object with keys for `token` and `refreshToken`, but this format is not required. You can
9090-use different keys or store any additional auth related information here. For example you could decode and store the token expiry date, which would save you from decoding
9191-your JWT every time you want to check whether your token is expired.
9292+storage. The structure of this particular `authState` is an object with keys for `token` and
9393+`refreshToken`, but this format is not required. We can use different keys or store any additional
9494+auth related information here. For example, we could decode and store the token expiry date, which
9595+would save us from decoding the JWT every time we want to check whether it has expired.
92969397In React Native, this is very similar, but because persisted storage in React Native is always asynchronous, so is this function:
9498···109113110114### Configuring `addAuthToOperation`
111115112112-The purpose of `addAuthToOperation` is to take apply your auth state to each request. Note that the format of the `authState` will be whatever
113113-you've returned from `getAuth` and not at all constrained by the exchange:
116116+The purpose of `addAuthToOperation` is to apply an auth state to each request. Note that the format
117117+of the `authState` will be whatever we've returned from `getAuth` and not constrained by the exchange:
114118115119```js
116120import { makeOperation } from '@urql/core';
···138142};
139143```
140144141141-First we check that we have an `authState` and a `token`. Then we apply it to the request `fetchOptions` as an `Authorization` header.
142142-The header format can vary based on the API (e.g using `Bearer ${token}` instead of just `token`) which is why it'll be up to you to add the header
143143-in the expected format for your API.
145145+First, we check that we have an `authState` and a `token`. Then we apply it to the request
146146+`fetchOptions` as an `Authorization` header. The header format can vary based on the API (e.g. using
147147+`Bearer ${token}` instead of just `token`) which is why it'll be up to us to add the header
148148+in the expected format for our API.
144149145150### Configuring `didAuthError`
146151147152This function lets the exchange know what is defined to be an API error for your API. `didAuthError` receives an `error` which is of type
148148-[`CombinedError`](../api/core.md#combinederror) and we can use the `graphQLErrors` array in `CombinedError` to determine if an auth error has occurred.
153153+[`CombinedError`](../api/core.md#combinederror), and we can use the `graphQLErrors` array in `CombinedError` to determine if an auth error has occurred.
149154150155The GraphQL error looks like something like this:
151156···165170}
166171```
167172168168-Most GraphQL APIs will communicate auth errors via the [error code extension](https://www.apollographql.com/docs/apollo-server/data/errors/#codes) which
169169-is the recommended approach. We'll be able to determine whether any of the GraphQL errors were due to an unauthorized error code, which would indicate an auth failure:
173173+Most GraphQL APIs will communicate auth errors via the [error code
174174+extension](https://www.apollographql.com/docs/apollo-server/data/errors/#codes), which
175175+is the recommended approach. We'll be able to determine whether any of the GraphQL errors were due
176176+to an unauthorized error code, which would indicate an auth failure:
170177171178```js
172179const didAuthError = ({ error }) => {
···203210204211### Configuring `getAuth` (triggered after an auth error has occurred)
205212206206-If your API doesn't support any sort of token refresh, this is where you should simply log the user out.
213213+If the API doesn't support any sort of token refresh, this is where we could simply log the user out.
207214208215```js
209216const getAuth = async ({ authState }) => {
···222229};
223230```
224231225225-Here, `logout()` is a placeholder that is called when we got an error, so that we can redirect to a login page again and clear our tokens from local storage or otherwise.
232232+Here, `logout()` is a placeholder that is called when we got an error, so that we can redirect to a
233233+login page again and clear our tokens from local storage or otherwise.
226234227227-If we had a way to refresh our token using a refresh token, we can attempt to get a new token for the user first:
235235+If we had a way to refresh our token using a refresh token, we can attempt to get a new token for the
236236+user first:
228237229238```js
230239const getAuth = async ({ authState, mutate }) => {
···342351When the application launches, the first thing we do is check whether the user has any auth tokens in persisted storage. This will tell us
343352whether to show the user the logged in or logged out view.
344353345345-The `isLoggedIn` prop should always be updated based on authentication state change e.g. set to `true` after the use has authenticated and their tokens have been
346346-added to storage, and set to `false` if the user has been logged out and their tokens have been cleared. It's important clear or add tokens to storage _before_
347347-updating the prop in order for the auth exchange to work correctly.
354354+The `isLoggedIn` prop should always be updated based on authentication state change. For instance, we may set it to
355355+`true` after the user has authenticated and their tokens have been added to storage, and set it to
356356+`false` once the user has been logged out and their tokens have been cleared. It's important to clear
357357+or add tokens to a storage _before_ updating the prop in order for the auth exchange to work
358358+correctly.
+8-7
docs/advanced/authoring-exchanges.md
···55555656**Second,** operations are checked against the cache. Depending on the `requestPolicy`,
5757cached results can be resolved from here instead, which would mean that the cache sends back the
5858-result and the operation doesn't travel any further in the chain.
5858+result, and the operation doesn't travel any further in the chain.
59596060-**Third,** operations are sent to the API and the result is turned into an `OperationResult`.
6060+**Third,** operations are sent to the API, and the result is turned into an `OperationResult`.
61616262**Lastly,** operation results then travel through the exchanges in _reverse order_, which is because
6363exchanges are a pipeline where all operations travel forward deeper into the exchange chain, and
···118118```
119119120120This exchange does nothing else than forward all operations and return all results. Hence, it's
121121-called a `noopExchange` - an exchange that doesn't do anything.
121121+called a `noopExchange` — an exchange that doesn't do anything.
122122123123### Forward and Return Composition
124124···155155### Only One Operations Stream
156156157157When writing an Exchange we have to be careful not to _split_ the stream into multiple ones by
158158-subscribing multiple times. Streams are lazy and immutable by default. Every time you use them, a new chain of streaming operators is created; since Exchanges are technically side-effects, we don't want to
159159-accidentally have multiple instances of them in parallel.
158158+subscribing multiple times. Streams are lazy and immutable by default. Every time you use them,
159159+a new chain of streaming operators is created; since Exchanges are technically side effects, we don't
160160+want to accidentally have multiple instances of them in parallel.
160161161162The `ExchangeIO` function receives an `operations$` stream. It's important to be careful to either only
162163use it once, or to _share_ its subscription.
···289290synchronous. The `fetchExchange` is asynchronous since
290291it makes a `fetch` request and waits for a server response.
291292292292-When you're adding more exchanges it's often crucial
293293-to put them in a specific order. For instance - an authentication exchange
293293+When you're adding more exchanges, it's often crucial
294294+to put them in a specific order. For instance, an authentication exchange
294295will need to go before the `fetchExchange`, a secondary cache will probably have to
295296go in front of the default cache exchange.
296297
+5-5
docs/advanced/auto-populate-mutations.md
···7788The `populateExchange` allows you to auto-populate selection sets in your mutations using the
99`@populate` directive. In combination with [Graphcache](../graphcache/README.md) this is a useful
1010-tool to update the data in your application automatically following a mutation, when your app grows
1010+tool to update the data in your application automatically following a mutation, when your app grows,
1111and it becomes harder to track all fields that have been queried before.
12121313-> **NOTE:** The `populateExchange` is currently _experimental_! Certain patterns and usage paths
1313+> **NOTE:** The `populateExchange` is _experimental_! Certain patterns and usage paths
1414> like GraphQL field arguments aren't covered yet, and the exchange hasn't been extensively used
1515> yet.
1616···48484949## Example usage
50505151-Consider the following queries which have been requested in other parts of your application:
5151+Consider the following queries, which have been requested in other parts of your application:
52525353```graphql
5454# Query 1
···92929393### Choosing when to populate
94949595-You may not want to populate your whole mutation response. In order to reduce your payload, pass populate lower in your query.
9595+You may not want to populate your whole mutation response. To reduce your payload, pass populate lower in your query.
96969797```graphql
9898mutation addTodo(id: ID!) {
···106106### Using aliases
107107108108If you find yourself using multiple queries with variables, it may be necessary to
109109-[use aliases](https://graphql.org/learn/queries/#aliases) in order to allow merging of queries.
109109+[use aliases](https://graphql.org/learn/queries/#aliases) to allow merging of queries.
110110111111> **Note:** This caveat may change in the future or this restriction may be lifted.
112112
+3-3
docs/advanced/debugging.md
···10101111## Devtools
12121313-The quickest way to debug `urql` is to use the [`urql` devtools.](https://github.com/FormidableLabs/urql-devtools/)
1313+It's easiest to debug `urql` with the [`urql` devtools.](https://github.com/FormidableLabs/urql-devtools/)
14141515It offers tools to inspect internal ["Debug Events"](#debug-events) as they happen, to explore data
1616as your app is seeing it, and to quickly trigger GraphQL queries.
···125125126126### Tips
127127128128-Lastly, in summary, here are a few tips, dos, and don'ts that are important when we're adding new
129129-Debug Events to custom exchanges.
128128+Lastly, in summary, here are a few tips, that are important when we're adding new Debug Events to
129129+custom exchanges:
130130131131- ✅ **Share internal details**: Frequent debug messages on key events inside your exchange are very
132132 useful when later inspecting them, e.g. in the `devtools`.
+3-3
docs/advanced/persistence-and-uploads.md
···2222hash and sends this hash instead of the full query. If the server has seen this GraphQL query before
2323it will recognise it by its hash and process the GraphQL API request as usual, otherwise it may
2424respond using a `PersistedQueryNotFound` error. In that case the client is supposed to instead send
2525-the full GraphQL query and the hash together, which will cause the query to be "registered" with the
2525+the full GraphQL query, and the hash together, which will cause the query to be "registered" with the
2626server.
27272828Additionally we could also decide to send these hashed queries as GET requests instead of POST
···7676### Customizing Hashing
77777878The `persistedFetchExchange` also accepts a `generateHash` option. This may be used to swap out the
7979-exchange's default method of generating SHA256 hashes. By default the exchange will use the
7979+exchange's default method of generating SHA256 hashes. By default, the exchange will use the
8080built-in [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) on the
8181browser, which has been implemented to support IE11 as well. In Node.js it'll use the [Node
8282Crypto Module](https://nodejs.org/api/crypto.html) instead.
···158158```
159159160160If you're using the `persistedFetchExchange` then put the `persistedFetchExchange` in front of the
161161-`multipartFetchExchange`, since only the latter is a full replacement for the `fetchExchange` and
161161+`multipartFetchExchange`, since only the latter is a full replacement for the `fetchExchange`, and
162162the former only handled query operations.
163163164164[Read more about `@urql/multipart-fetch-exchange` in our API
+2-2
docs/advanced/retry-operations.md
···66# Retrying Operations
7788The `retryExchange` lets us retry specific operation, by default it will
99-retry only network errors but we can specify additional options to add
99+retry only network errors, but we can specify additional options to add
1010functionality.
11111212## Installation and Setup
···55555656We have the `initialDelayMs` to specify at what interval the `retrying` should start, this means that if we specify `1000` that when our `operation` fails we'll wait 1 second and then retry it.
57575858-Next up is the `maxDelayMs`, our `retryExchange` will keep increasing the time between retries so we don't spam our server with requests it can't complete, this option ensures we don't exceed a certain threshold. This time between requests will increase with a random `back-off` factor multiplied by the `initialDelayMs`, read more about the [thundering herd problem](https://en.wikipedia.org/wiki/Thundering_herd_problem).
5858+Next up is the `maxDelayMs`, our `retryExchange` will keep increasing the time between retries, so we don't spam our server with requests it can't complete, this option ensures we don't exceed a certain threshold. This time between requests will increase with a random `back-off` factor multiplied by the `initialDelayMs`, read more about the [thundering herd problem](https://en.wikipedia.org/wiki/Thundering_herd_problem).
59596060Talking about increasing the `delay` randomly, `randomDelay` allows us to disable this. When this option is set to `false` we'll only increase the time between attempts with the `initialDelayMs`. This means if we fail the first time we'll have 1 second wait, next fail we'll have 2 seconds and so on.
6161
+8-8
docs/advanced/server-side-rendering.md
···6464`;
6565```
66666767-This will provide `__URQL_DATA__` globally which we've used in our first example to inject data into
6767+This will provide `__URQL_DATA__` globally, which we've used in our first example to inject data into
6868the `ssrExchange` on the client-side.
69697070Alternatively you can also call `restoreData` as long as this call happens synchronously before the
···8686or `renderToNodeStream`](https://reactjs.org/docs/react-dom-server.html#rendertostring).
87878888For React, `urql` has a "Suspense mode" that [allows data fetching to interrupt
8989-rendering](https://reactjs.org/docs/concurrent-mode-suspense.html). However, suspense is currently
8989+rendering](https://reactjs.org/docs/concurrent-mode-suspense.html). However, Suspense is
9090not supported by React during server-side rendering.
91919292Using [the `react-ssr-prepass` package](https://github.com/FormidableLabs/react-ssr-prepass) however,
···158158### With Preact
159159160160If you're using Preact instead of React, there's a drop-in replacement package for
161161-`react-ssr-prepass`, which is called `preact-ssr-prepass`. It only has a peer dependency on Preact
161161+`react-ssr-prepass`, which is called `preact-ssr-prepass`. It only has a peer dependency on Preact,
162162and we can install it like so:
163163164164```sh
···167167npm install --save preact-ssr-prepass preact
168168```
169169170170-All above examples for `react-ssr-prepass` will still be the exact same, except that instead of
170170+All above examples for `react-ssr-prepass` will still be the same, except that instead of
171171using the `urql` package we'll have to import from `@urql/preact`, and instead of `react-ssr-prepass`
172172we'll have to import from. `preact-ssr-prepass`.
173173···181181this integration contains convenience methods specifically for `Next.js`.
182182These will simplify the above setup for SSR.
183183184184-To setup `next-urql`, first we'll install `next-urql` with `react-is` and `urql` as
184184+To set up `next-urql`, first we'll install `next-urql` with `react-is` and `urql` as
185185peer dependencies:
186186187187```sh
···222222223223This will automatically set up server-side rendering on the page. The `withUrqlClient` higher-order
224224component function accepts the usual `Client` options as an argument. This may either just be an
225225-object or a function that receives the Next.js' `getInitialProps` context.
225225+object, or a function that receives the Next.js' `getInitialProps` context.
226226227227One added caveat is that these options may not include the `exchanges` option because `next-urql`
228228injects the `ssrExchange` automatically at the right location. If you're setting up custom exchanges
···306306307307### Resetting the client instance
308308309309-In rare scenario's you possibly will have to reset the client instance (reset all cache, ...), this is an uncommon scenario
310310-and we consider it "unsafe" so evaluate this carefully for yourself.
309309+In rare scenario's you possibly will have to reset the client instance (reset all cache, ...), this
310310+is an uncommon scenario, and we consider it "unsafe" so evaluate this carefully for yourself.
311311312312When this does seem like the appropriate solution any component wrapped with `withUrqlClient` will receive the `resetUrqlClient`
313313property, when invoked this will create a new top-level client and reset all prior operations.
+5-2
docs/advanced/subscriptions.md
···3333In the above example, we add the `subscriptionExchange` to the `Client` with the default exchanges
3434add before it. The `subscriptionExchange` is a factory that accepts additional options and returns
3535the actual `Exchange` function. It does not make any assumption over the transport protocol and
3636-scheme that is used. Instead, we need to pass a `forwardSubscription` function which is called with
3636+scheme that is used. Instead, we need to pass a `forwardSubscription` function, which is called with
3737an "enriched" _Operation_ every time the `Client` attempts to execute a GraphQL Subscription.
38383939When we define this function it must return an "Observable-like" object, which needs to follow the
···304304`Client`'s `subscription` method for one-off subscriptions. This method is similar to the ones for
305305mutations and subscriptions [that we've seen before on the "Core Package" page.](../basics/core.md)
306306307307-This method will always [returns a Wonka stream](../architecture.md#the-wonka-library) and doesn't have a `.toPromise()` shortcut method, since promises won't return the multiple values that a subscription may deliver. Let's convert the above example to one without framework code, as we may use subscriptions in a Node.js environment.
307307+This method will always [returns a Wonka stream](../architecture.md#the-wonka-library) and doesn't
308308+have a `.toPromise()` shortcut method, since promises won't return the multiple values that a
309309+subscription may deliver. Let's convert the above example to one without framework code, as we may
310310+use subscriptions in a Node.js environment.
308311309312```js
310313import { pipe, subscribe } from 'wonka';
+7-7
docs/advanced/testing.md
···23232424In the section ["Stream Patterns" on the "Architecture" page](../architecture.md) we've seen, that
2525all methods on the client operate with and return streams. These streams are created using
2626-[the Wonka library](../architecture.md#the-wonka-library) and we're able to create streams
2626+[the Wonka library](../architecture.md#the-wonka-library), and we're able to create streams
2727ourselves to mock the different states of our operations, e.g. fetching, errors, or success with data.
28282929You'll probably use one of these utility functions to create streams:
···98989999### Fetching
100100101101-Fetching states can be simulated by returning a stream which never returns. Wonka provides a utility for this, aptly called `never`.
101101+Fetching states can be simulated by returning a stream, which never returns. Wonka provides a utility for this, aptly called `never`.
102102103103-Here's a fixture which stays in the _fetching_ state.
103103+Here's a fixture, which stays in the _fetching_ state.
104104105105```tsx
106106import { Provider } from 'urql';
···120120121121### Response (success)
122122123123-Response states are simulated by providing a stream which contains a network response. For single responses, Wonka's `fromValue` function can do this for us.
123123+Response states are simulated by providing a stream, which contains a network response. For single responses, Wonka's `fromValue` function can do this for us.
124124125125**Example snapshot test of response state**
126126···185185});
186186```
187187188188-The above client we've created mocks all three operations — queries, mutations, and subscriptions — to always remain in the `fetching: true` state.
188188+The above client we've created mocks all three operations — queries, mutations and subscriptions — to always remain in the `fetching: true` state.
189189Generally when we're _hoisting_ our mocked client and reuse it across multiple tests we have to be
190190mindful not to instantiate the mocks outside of Jest's lifecycle functions (like `it`, `beforeEach`,
191191`beforeAll` and such) as it may otherwise reset our mocked functions' return values or
···197197198198If you prefer to have more control on when the new data is arriving you can use the `makeSubject` utility from Wonka. You can see more details in the next section.
199199200200-Here's an example of testing a list component which uses a subscription.
200200+Here's an example of testing a list component, which uses a subscription.
201201202202```tsx
203203import { OperationContext, makeOperation } from '@urql/core';
···236236237237Simulating multiple responses can be useful, particularly testing `useEffect` calls dependent on changing query responses.
238238239239-For this, a _subject_ is the way to go. In short, it's a stream which you can push responses to. The `makeSubject` function from Wonka is what you'll want to use for this purpose.
239239+For this, a _subject_ is the way to go. In short, it's a stream that you can push responses to. The `makeSubject` function from Wonka is what you'll want to use for this purpose.
240240241241Below is an example of simulating subsequent responses (such as a cache update/refetch) in a test.
242242
+9-9
docs/api/core.md
···5566# @urql/core
7788-The `@urql/core` package is the basis of all framework bindings. Every bindings package,
99-like [`urql` for React](./urql.md) or [`@urql/preact`](./preact.md) will reuse the core logic and
88+The `@urql/core` package is the basis of all framework bindings. Each bindings-package,
99+like [`urql` for React](./urql.md) or [`@urql/preact`](./preact.md), will reuse the core logic and
1010reexport all exports from `@urql/core`.
1111Therefore if you're not accessing utilities directly, aren't in a Node.js environment, and are using
1212framework bindings, you'll likely want to import from your framework bindings package directly.
···2727| `fetchOptions` | `RequestInit \| () => RequestInit` | Additional `fetchOptions` that `fetch` in `fetchExchange` should use to make a request |
2828| `fetch` | `typeof fetch` | An alternative implementation of `fetch` that will be used by the `fetchExchange` instead of `window.fetch` |
2929| `suspense` | `?boolean` | Activates the experimental React suspense mode, which can be used during server-side rendering to prefetch data |
3030-| `requestPolicy` | `?RequestPolicy` | Changes the default request policy that will be used. By default this will be `cache-first`. |
3030+| `requestPolicy` | `?RequestPolicy` | Changes the default request policy that will be used. By default, this will be `cache-first`. |
3131| `preferGetMethod` | `?boolean` | This is picked up by the `fetchExchange` and will force all queries (not mutations) to be sent using the HTTP GET method instead of POST. |
3232| `maskTypename` | `?boolean` | Enables the `Client` to automatically apply the `maskTypename` utility to all `data` on [`OperationResult`s](#operationresult). This makes the `__typename` properties non-enumerable. |
3333···6262This is a shorthand method for [`client.executeQuery`](#clientexecutequery), which accepts a query
6363(`DocumentNode | string`) and variables separately and creates a [`GraphQLRequest`](#graphqlrequest) [`createRequest`](#createrequest) automatically.
64646565-The returned `Source<OperationResult>` will also have an added `toPromise` method so the stream can
6565+The returned `Source<OperationResult>` will also have an added `toPromise` method, so the stream can
6666be conveniently converted to a promise.
67676868```js
···286286`true` or `false` to tell the `ssrExchange` whether to
287287write to (server-side) or read from (client-side) the cache.
288288289289-By default `isClient` defaults to `true` when the `Client.suspense`
289289+By default, `isClient` defaults to `true` when the `Client.suspense`
290290mode is disabled and to `false` when the `Client.suspense` mode
291291is enabled.
292292···307307during the server-side rendering pass, and allows you to populate
308308the cache on the client-side with the same data.
309309310310-During React rehydration this cache will be emptied and it will
310310+During React rehydration this cache will be emptied, and it will
311311become inactive and won't change the results of queries after
312312rehydration.
313313···409409### makeOperation
410410411411This utility is used to either turn a [`GraphQLRequest` object](#graphqlrequest) into a new
412412-[`Operation` object](#operation) or to copy an `Operation`. It adds the `kind` property and the
412412+[`Operation` object](#operation) or to copy an `Operation`. It adds the `kind` property, and the
413413`operationName` alias that outputs a deprecation warning.
414414415415It accepts three arguments:
···463463and marks every `__typename` property as non-enumerable.
464464465465The [`formatDocument`](#formatdocument) is often used by `urql` automatically and adds `__typename`
466466-fields to all results. However, this means that data can often not be passed back into variables or
467467-inputs on mutations, which is a common use-case. This utility hides these fields which can solves
466466+fields to all results. However, this means that data often cannot be passed back into variables or
467467+inputs on mutations, which is a common use-case. This utility hides these fields, which can solve
468468this problem.
469469470470It's used by the [`Client`](#client) when the `maskTypename` option is enabled.
+6-6
docs/api/urql.md
···2020This hook returns a tuple of the shape `[result, executeQuery]`.
21212222- The `result` is an object with the shape of an [`OperationResult`](./core.md#operationresult) with
2323- an added `fetching: boolean` property, indicating whether the query is currently being fetched.
2323+ an added `fetching: boolean` property, indicating whether the query is being fetched.
2424- The `executeQuery` function optionally accepts
2525 [`Partial<OperationContext>`](./core.md#operationcontext) and reexecutes the current query when
2626 it's called. When `pause` is set to `true` this executes the query, overriding the otherwise
···3434`[result, executeMutation]`.
35353636- The `result` is an object with the shape of an [`OperationResult`](./core.md#operationresult) with
3737- an added `fetching: boolean` property, indicating whether the mutation is currently being executed.
3737+ an added `fetching: boolean` property, indicating whether the mutation is being executed.
3838- The `executeMutation` function accepts variables and optionally
3939 [`Partial<OperationContext>`](./core.md#operationcontext) and may be used to start executing a
4040 mutation. It returns a `Promise` resolving to an [`OperationResult`](./core.md#operationresult).
···7373 it's called. When `pause` is set to `true` this starts the subscription, overriding the otherwise
7474 paused hook.
75757676-Since a subscription may proactively closed by the server, the additional `fetching: boolean`
7777-property on the `result` may update to `false` when the server ends the subscription.
7878-By default `urql` is not able to start subscriptions, since this requires some additional setup.
7676+The `fetching: boolean` property on the `result` may change to `false` when the server proactively
7777+ends the subscription. By default, `urql` is unable able to start subscriptions, since this requires
7878+some additional setup.
79798080[Read more about how to use the `useSubscription` API on the "Subscriptions"
8181page.](../advanced/subscriptions.md)
···9797This component is a wrapper around [`useMutation`](#usemutation), exposing a [render prop
9898API](https://reactjs.org/docs/render-props.html) for cases where hooks aren't desirable.
9999100100-The `Mutation` component accepts a `query` prop and a function callback must be passed to `children`
100100+The `Mutation` component accepts a `query` prop, and a function callback must be passed to `children`
101101that receives the mutation result and must return a React element. The second argument of
102102`useMutation`'s returned tuple, `executeMutation` is passed as an added property on the mutation
103103result object.
+10-10
docs/architecture.md
···35353636If `urql` was a train it would take several stops to arrive at its terminus, our API. It starts with us
3737defining queries or mutations. Any GraphQL request can be abstracted into their query documents and
3838-their variables. In `urql`, these GraphQL requests are treated as unique objects which are uniquely
3838+their variables. In `urql`, these GraphQL requests are treated as unique objects, which are uniquely
3939identified by the query document and variables (which is why a `key` is generated from the two). This
4040`key` is a hash number of the query document and variables and uniquely identifies our
4141[`GraphQLRequest`](./api/core.md#graphqlrequest).
···6666
67676868It's the `Client`s responsibility to accept an `Operation` and execute it. The bindings interally
6969-call the `client.executeQuery`, `client.executeMutation`, or `client.executeSubscription` methods
6969+call the `client.executeQuery`, `client.executeMutation`, or `client.executeSubscription` methods,
7070and we'll get a "stream" of results. This "stream" allows us to register a callback with it to
7171receive results.
72727373In the diagram we can see that each operation is a signal for our request to start at which point
7474we can expect to receive our results eventually on a callback. Once we're not interested in results
7575anymore a special "teardown" signal is issued on the `Client`. While we don't see operations outside
7676-of the `Client`, they're what travel through the "Exchanges" on the `Client`.
7676+the `Client`, they're what travel through the "Exchanges" on the `Client`.
77777878## The Client and Exchanges
79798080To reiterate, when we use `urql`'s bindings for our framework of choice, methods are called on the
8181-`Client` but we never see the operations that are created in the background from our bindings. We
8181+`Client`, but we never see the operations that are created in the background from our bindings. We
8282call a method like `client.executeQuery` (or it's called for us in the bindings), an operation is
8383issued internally when we subscribe with a callback, and later our callback is called with results.
8484···9595our perspective:
96969797- We subscribe to a "stream" and expect to get results on a callback
9898-- The `Client` issues the operation and we'll receive some results back eventually as either the
9999- cache responds (synchronously) or the request gets sent to our API.
100100-- We eventually unsubscribe and the `Client` issues a "teardown" operation with the same `key` as
9898+- The `Client` issues the operation, and we'll receive some results back eventually as either the
9999+ cache responds (synchronously), or the request gets sent to our API.
100100+- We eventually unsubscribe, and the `Client` issues a "teardown" operation with the same `key` as
101101 the original operation, which concludes our flow.
102102103103The `Client` itself doesn't actually know what to do with operations. Instead, it sends them through
···115115 unique `key`.
116116- This operation is sent into the **exchanges** and eventually ends up at the `fetchExchange`
117117 (or a similar exchange)
118118-- The operation is sent to the API and a **result** comes back which is wrapped in an `OperationResult`
118118+- The operation is sent to the API and a **result** comes back, which is wrapped in an `OperationResult`
119119- The `Client` filters the `OperationResult` by the `operation.key` and — via a callback — gives us
120120 a **stream of results**.
121121···167167But, **what are streams?**
168168169169Generally we refer to _streams_ as abstractions that allow us to program with asynchronous events
170170-over time. Within the JavaScript context we're thinking specifically in terms of of
170170+over time. Within the context of JavaScript we're specifically thinking in terms of
171171[Observables](https://github.com/tc39/proposal-observable)
172172and [Reactive Programming with Observables.](http://reactivex.io/documentation/observable.html)
173173These concepts may sound initimidating, but from a high-level view what we're talking about can be
174174thought of as a combination of promises and iterables (e.g. arrays). We're dealing with multiple
175175-events but our callback is called over time. It's like calling `forEach` on an array but expecting
175175+events, but our callback is called over time. It's like calling `forEach` on an array but expecting
176176the results to come in asynchronously.
177177178178As a user, if we're using the one framework bindings that we've seen in [the "Basics"
+17-16
docs/basics/core.md
···2222## Installation
23232424As we said above, if we are using bindings then those will already have installed `@urql/core` as
2525-they depend on it. They also all re-export all exports from `@urql/core`, so we can use those no
2626-matter which bindings we've installed. However, it's also possible to explicitly install
2525+they depend on it. They also all re-export all exports from `@urql/core`, so we can use those
2626+regardless of which bindings we've installed. However, it's also possible to explicitly install
2727`@urql/core` or use it standalone, e.g. in a Node.js environment.
28282929```sh
···111111`;
112112```
113113114114-This will all look familiar when coming from the `graphql-tag` package. The functionality is
115115-identical and the output is approximately the same. The two packages are also intercompatible.
116116-However, one small change that `@urql/core`'s implementation makes is that your fragment names don't
117117-have to be globally unique, since it's possible to create some one-off fragments every now and then.
114114+This usage will look familiar when coming from the `graphql-tag` package. The `gql` API is
115115+identical, and its output is approximately the same. The two packages are also intercompatible.
116116+However, one small change in `@urql/core`'s implementation is that your fragment names don't
117117+have to be globally unique, since it's possible to create some one-off fragments occasionally,
118118+especially for `@urql/exchange-graphcache`'s configuration.
118119It also pre-generates a "hash key" for the `DocumentNode` which is what `urql` does anyway, thus
119120avoiding some extra work compared to when the `graphql-tag` package is used with `urql`.
120121···143144At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started.
144145145146Another common option is `fetchOptions`. This option allows us to customize the options that will be
146146-passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object or
147147+passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object, or
147148a function returning an options object.
148149149150In the following example we'll add a token to each `fetch` request that our `Client` sends to our
···167168without it. However, another important option on the `Client` is the `exchanges` option.
168169169170This option passes a list of exchanges to the `Client`, which tell it how to execute our requests
170170-and how to cache data in a certain order. By default this will be populated with the list of
171171+and how to cache data in a certain order. By default, this will be populated with the list of
171172`defaultExchanges`.
172173173174```js
···186187supports by adding new exchanges to this list. On [the "Architecture" page](../architecture.md)
187188we'll also learn more about what exchanges are and why they exist.
188189189189-For now it's sufficient for us to know that our requests are executed using the logic in the
190190+For now, it's enough for us to know that our requests are executed using the logic in the
190191exchanges in order. First, the `dedupExchange` deduplicates requests if we send the same queries
191192twice, the `cacheExchange` implements the default "document caching" behaviour (as we'll learn about
192193on the ["Document Caching"](./document-caching.md) page), and lastly the `fetchExchange` is
···218219219220In the above example we're executing a query on the client, are passing some variables and are
220221calling the `toPromise()` method on the return value to execute the request immediately and get the
221221-result as a promise. This may be useful when we don't plan on cancelling queries or we don't
222222+result as a promise. This may be useful when we don't plan on cancelling queries, or we don't
222223care about future updates to this data and are just looking to query a result once.
223224224225The same can be done for mutations by calling the `client.mutation` method instead of the
225226`client.query` method.
226227227228Similarly there's a way to read data from the cache synchronously, provided that the cache has
228228-received a result for a given query before. The `Client` has a `readQuery` method which is a
229229+received a result for a given query before. The `Client` has a `readQuery` method, which is a
229230shortcut for just that.
230231231232```js
···279280```
280281281282This code example is similar to the one before. However, instead of sending a one-off query, we're
282282-subscribing to the query. Internally, this causes the `Client` to do the exact same, but the
283283+subscribing to the query. Internally, this causes the `Client` to do the same, but the
283284subscription means that our callback may be called repeatedly. We may get future results as well as
284285the first one.
285286···287288immediately if our cache already has a result for the given query. The same principle applies here!
288289Our callback will be called synchronously if the cache already has a result.
289290290290-Once we're not interested in any results anymore we need to clean up after ourselves by calling
291291+Once we're not interested in any results anymore, we need to clean up after ourselves by calling
291292`unsubscribe`. This stops the subscription and makes sure that the `Client` doesn't actively update
292293the query anymore or refetches it. We can think of this pattern as being very similar to events or
293294event hubs.
294295295295-We're using [the Wonka library for our streams](https://wonka.kitten.sh/basics/background) which
296296+We're using [the Wonka library for our streams](https://wonka.kitten.sh/basics/background), which
296297we'll learn more about [on the "Architecture" page](./architecture.md). But we can think of this as
297298React's effects being called over time, or as `window.addEventListener`.
298299···303304304305- [`CombinedError`](../api/core.md#combinederror) - our abstraction to combine one or more `GraphQLError`(s) and a `NetworkError`
305306- `makeResult` and `makeErrorResult` - utilities to create _Operation Results_
306306-- [`createRequest`](../api/core.md#createrequest) - a utility function to create a request from a query and some variables (which
307307- generates a stable _Operation Key_)
307307+- [`createRequest`](../api/core.md#createrequest) - a utility function to create a request from a
308308+ query, and some variables (which generate a stable _Operation Key_)
308309309310There are other utilities not mentioned here. Read more about the `@urql/core` API in the [API docs](../api/core.md).
310311
+6-7
docs/basics/document-caching.md
···5566# Document Caching
7788-By default `urql` uses a concept called _Document Caching_. It will avoid sending the same requests
88+By default, `urql` uses a concept called _Document Caching_. It will avoid sending the same requests
99to a GraphQL API repeatedly by caching the result of each query.
10101111This works like the cache in a browser. `urql` creates a key for each request that is sent based on
···39394040## Request Policies
41414242-The _request policy_ that is defined will alter what the default document cache does. By default the
4242+The _request policy_ that is defined will alter what the default document cache does. By default, the
4343cache will prefer cached results and will otherwise send a request, which is called `cache-first`.
4444In total there are four different policies that we can use:
4545···63636464## Document Cache Gotchas
65656666-This cache has a small trade-off! If we request a list of data and the API returns an empty list,
6767-the cache won't be able to see the `__typename` of said list and won't invalidate.
6666+This cache has a small trade-off! If we request a list of data, and the API returns an empty list,
6767+then the cache won't be able to see the `__typename` of said list and invalidate it.
68686969There are two ways to fix this issue, supplying `additionalTypenames` to the context of your query or [switch to "Normalized Caching"
7070instead](../graphcache/normalized-caching.md).
···91919292Now the cache will know when to invalidate this query even when the list is empty.
93939494-We also have the possibility to use this for `mutations`.
9595-There are moments where a mutation can cause a side-effect on your server side and it needs
9696-to invalidate an additional entity.
9494+We may also use this feature for mutations, since occasionally mutations must invalidate data that
9595+isn't directly connected to a mutation by a `__typename`.
97969897```js
9998const [result, execute] = useMutation(`mutation($name: String!) { createUser(name: $name) }`);
+1-1
docs/basics/errors.md
···1616- The `networkError` property will contain any error that stopped `urql` from making a network
1717 request.
1818- The `graphQLErrors` property may be an array that contains [normalized `GraphQLError`s as they
1919- were returned in the `errors` array from a GraphQL API.](https://graphql.org/graphql-js/error/)
1919+ were received in the `errors` array from a GraphQL API.](https://graphql.org/graphql-js/error/)
20202121Additionally, the `message` of the error will be generated and combined from the errors for
2222debugging purposes.
+16-16
docs/basics/react-preact.md
···14141515### Installation
16161717-Installing `urql` is as quick as you'd expect and you won't need any other packages to get started
1717+Installing `urql` is as quick as you'd expect, and you won't need any other packages to get started
1818with at first. We'll install the package with our package manager of choice.
19192020```sh
···5151At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started.
52525353Another common option is `fetchOptions`. This option allows us to customize the options that will be
5454-passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object or
5454+passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object, or
5555a function returning an options object.
56565757In the following example we'll add a token to each `fetch` request that our `Client` sends to our
···8989);
9090```
91919292-Now every component and element inside and under the `Provider` are able to use GraphQL queries that
9292+Now every component and element inside and under the `Provider` can use GraphQL queries that
9393will be sent to our API.
94949595## Queries
96969797Both libraries offer a `useQuery` hook and a `Query` component. The latter accepts the same
9898-parameters but we won't cover it in this guide. [Look it up in the API docs if you prefer
9898+parameters, but we won't cover it in this guide. [Look it up in the API docs if you prefer
9999render-props components.](../api/urql.md#query-component)
100100101101### Run a first query
···137137138138Here we have implemented our first GraphQL query to fetch todos. We see that `useQuery` accepts
139139options and returns a tuple. In this case we've set the `query` option to our GraphQL query. The
140140-tuple we then get in return is an array that contains a result object and a re-execute function.
140140+tuple we then get in return is an array that contains a result object, and a re-execute function.
141141142142-The result object contains several properties. The `fetching` field indicates whether we're currently
142142+The result object contains several properties. The `fetching` field indicates whether the hook is
143143loading data, `data` contains the actual `data` from the API's result, and `error` is set when either
144144the request to the API has failed or when our API result contained some `GraphQLError`s, which
145145we'll get into later on the ["Errors" page](./errors.md).
···174174`POST` request body that is sent to our GraphQL API.
175175176176Whenever the `variables` (or the `query`) option on the `useQuery` hook changes `fetching` will
177177-switch to `true` and a new request will be sent to our API, unless a result has already been cached
177177+switch to `true`, and a new request will be sent to our API, unless a result has already been cached
178178previously.
179179180180### Pausing `useQuery`
···191191192192Let's pause the query we've just
193193written to not execute when these variables are empty, to prevent `null` variables from being
194194-executed. We can do this by means of setting the `pause` option to `true`:
194194+executed. We can do this by setting the `pause` option to `true`:
195195196196```jsx
197197const Todos = ({ from, limit }) => {
···215215than just `query` and `variables`. Another option we should touch on is `requestPolicy`.
216216217217The `requestPolicy` option determines how results are retrieved from our `Client`'s cache. By
218218-default this is set to `cache-first`, which means that we prefer to get results from our cache, but
218218+default, this is set to `cache-first`, which means that we prefer to get results from our cache, but
219219are falling back to sending an API request.
220220221221Request policies aren't specific to `urql`'s React API, but are a common feature in its core. [You
···226226227227The `useQuery` hook updates and executes queries whenever its inputs, like the `query` or
228228`variables` change, but in some cases we may find that we need to programmatically trigger a new
229229-query. This is the purpose of the `reexecuteQuery` function which is the second item in the tuple
229229+query. This is the purpose of the `reexecuteQuery` function, which is the second item in the tuple
230230that `useQuery` returns.
231231232232Triggering a query programmatically may be useful in a couple of cases. It can for instance be used
233233-to refresh data that is currently being displayed. In these cases we may also override the
234234-`requestPolicy` of our query just once and set it to `network-only` to skip the cache.
233233+to refresh the hook's data. In these cases we may also override the `requestPolicy` of our query just
234234+once and set it to `network-only` to skip the cache.
235235236236```jsx
237237const Todos = ({ from, limit }) => {
···259259## Mutations
260260261261Both libraries offer a `useMutation` hook and a `Mutation` component. The latter accepts the same
262262-parameters but we won't cover it in this guide. [Look it up in the API docs if you prefer
262262+parameters, but we won't cover it in this guide. [Look it up in the API docs if you prefer
263263render-props components.](../api/urql.md#mutation-component)
264264265265### Sending a mutation
···293293### Using the mutation result
294294295295When calling our `updateTodo` function we have two ways of getting to the result as it comes back
296296-from our API. We can either use the first value of the returned tuple — our `updateTodoResult` — or
296296+from our API. We can either use the first value of the returned tuple, our `updateTodoResult`, or
297297we can use the promise that `updateTodo` returns.
298298299299```jsx
···312312```
313313314314The result is useful when your UI has to display progress on the mutation, and the returned
315315-promise is particularly useful when you're adding side-effects that run after the mutation has
315315+promise is particularly useful when you're adding side effects that run after the mutation has
316316completed.
317317318318### Handling mutation errors
···345345## Reading on
346346347347This concludes the introduction for using `urql` with React or Preact. The rest of the documentation
348348-is mostly framework-agnostic and will apply to either `urql` in general or the `@urql/core` package,
348348+is mostly framework-agnostic and will apply to either `urql` in general, or the `@urql/core` package,
349349which is the same between all framework bindings. Hence, next we may want to learn more about one of
350350the following to learn more about the internals:
351351
+8-8
docs/basics/svelte.md
···108108109109The `operationStore` function creates a [Svelte Writable store](https://svelte.dev/docs#writable).
110110You can use it to initialise a data container in `urql`. This store holds on to our query inputs,
111111-like the GraphQL query and variables, which we can change to launch new queries, and also exposes
111111+like the GraphQL query and variables, which we can change to launch new queries. It also exposes
112112the query's eventual result, which we can then observe.
113113114114### Run a first query
···213213<button on:click={nextPage}>Next page<button></button></button>
214214```
215215216216-The `operationStore` provides getters too so it's also possible for us to pass `todos` around and
216216+The `operationStore` provides getters as well, so it's also possible for us to pass `todos` around and
217217update `todos.variables` or `todos.query` directly. Both, updating `todos.variables` and
218218`$todos.variables` in a component for instance, will cause `query` to pick up the update and execute
219219our changes.
···225225started at will. Instead, the `query`'s third argument, the `context`, may have an added `pause`
226226option that can be set to `true` to temporarily _freeze_ all changes and stop requests.
227227228228-For instance we may start out with a paused store and then unpause it once a callback is invoked:
228228+For instance, we may start out with a paused store and then unpause it once a callback is invoked:
229229230230```html
231231<script>
···260260most interesting option the `context` may contain is `requestPolicy`.
261261262262The `requestPolicy` option determines how results are retrieved from our `Client`'s cache. By
263263-default this is set to `cache-first`, which means that we prefer to get results from our cache, but
263263+default, this is set to `cache-first`, which means that we prefer to get results from our cache, but
264264are falling back to sending an API request.
265265266266In total there are four different policies that we can use:
···301301...
302302```
303303304304-As we can see, the `requestPolicy` is easily changed here and we can read our `context` option back
304304+As we can see, the `requestPolicy` is easily changed, and we can read our `context` option back
305305from `todos.context`, just as we can check `todos.query` and `todos.variables`. Updating
306306`operationStore.context` can be very useful to also refetch queries, as we'll see in the next
307307section.
···313313The default caching approach in `@urql/svelte` typically takes care of updating queries on the fly
314314quite well and does so automatically. Sometimes it may be necessary though to refetch data and to
315315execute a query with a different `context`. Triggering a query programmatically may be useful in a
316316-couple of cases. It can for instance be used to refresh data that is currently being displayed.
316316+couple of cases. It can for instance be used to refresh data.
317317318318We can trigger a new query update by changing out the `context` of our `operationStore`.
319319···351351## Mutations
352352353353The `mutation` function isn't dissimilar from the `query` function but is triggered manually and
354354-can accept a [`GraphQLRequest` object](../api/core.md#graphqlrequest) too while also supporting our
354354+can accept a [`GraphQLRequest` object](../api/core.md#graphqlrequest), while also supporting our
355355trusty `operationStore`.
356356357357### Sending a mutation
···473473## Reading on
474474475475This concludes the introduction for using `urql` with Svelte. The rest of the documentation
476476-is mostly framework-agnostic and will apply to either `urql` in general or the `@urql/core` package,
476476+is mostly framework-agnostic and will apply to either `urql` in general, or the `@urql/core` package,
477477which is the same between all framework bindings. Hence, next we may want to learn more about one of
478478the following to learn more about the internals:
479479
+8-7
docs/comparison.md
···5566# Comparison
7788-> This comparison page aims to be accurate, unbiased, and up-to-date. If you see any information that
88+> This comparison page aims to be detailed, unbiased, and up-to-date. If you see any information that
99> may be inaccurate or could be improved otherwise, please feel free to suggest changes.
10101111The most common question that you may encounter with GraphQL is what client to choose when you are
1212-getting started. We aim to provide an unbiased and accurate comparison of several options on this
1212+getting started. We aim to provide an unbiased and detailed comparison of several options on this
1313page, so that you can make an **informed decision**.
14141515All options come with several drawbacks and advantages, and all of these clients have been around
1616for a while now. A little known fact is that `urql` in its current form and architecture has already
1717-existed since February of 2019, and its normalized cache has been around since September 2019.
1717+existed since February 2019, and its normalized cache has been around since September 2019.
18181919Overall, we would recommend to make your decision based on whether your required features are
2020supported, which patterns you'll use (or restrictions thereof), and you may want to look into
2121-whether all of the parts and features you're interested in are well maintained.
2121+whether all the parts and features you're interested in are well maintained.
22222323## Comparison by Features
2424···60606161Typically these are all additional addon features that you may expect from a GraphQL client, no
6262matter which framework you use it with. It's worth mentioning that all three clients support some
6363-kind of extensibility API which allows you to change when and how queries are sent to an API. These
6363+kind of extensibility API, which allows you to change when and how queries are sent to an API. These
6464are easy to use primitives particularly in Apollo, with links, and in `urql` with exchanges. The
6565major difference in `urql` is that all caching logic is abstracted in exchanges too, which makes
6666it easy to swap the caching logic or other behavior out (and hence makes `urql` slightly more
···138138`@urql/exchange-graphcache` we chose to include it as a feature since it also strengthened other
139139guarantees that the cache makes.
140140141141-Relay does in fact have similar guarantees as [`urql`'s Commutativity Guarantees](./graphcache/under-the-hood.md)
141141+Relay does in fact have similar guarantees as [`urql`'s Commutativity
142142+Guarantees](./graphcache/under-the-hood.md),
142143which are more evident when applying list updates out of order under more complex network
143144conditions.
144145···155156156157- Parts of the `graphql` package tree-shake away and may also be replaced (e.g. `parse`)
157158- All packages in `urql` reuse parts of `@urql/core` and `wonka`, which means adding all their total
158158- sizes up doesn't give you an accurate result.
159159+ sizes up doesn't give you a correct result of their total expected bundle size.
159160- These sizes may change drastically given the code you write and add yourself, but can be managed
160161 via precompilation (e.g. with `babel-plugin-graphql-tag` or GraphQL Code Generator for Apollo and
161162 `urql`)
+5-5
docs/graphcache/README.md
···5566# Graphcache
7788-In `urql`, caching is fully configurable via [exchanges](../architecture.md) and the default
88+In `urql`, caching is fully configurable via [exchanges](../architecture.md), and the default
99`cacheExchange` in `urql` offers a ["Document Cache"](../basics/document-caching.md), which is
1010-sufficient for sites that heavily rely and render static content. However as an app grows more
1010+usually enough for sites that heavily rely on static content. However as an app grows more
1111complex it's likely that the data and state that `urql` manages, will also grow more complex and
1212introduce interdependencies between data.
1313···1515how [data is often structured in
1616Redux.](https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape/)
17171818-In `urql`, normalized caching is an opt-in feature which is provided by the
1818+In `urql`, normalized caching is an opt-in feature, which is provided by the
1919`@urql/exchange-graphcache` package, _Graphcache_ for short.
20202121## Features
22222323-The following pages introduce different features in _Graphcache_ which together make it a compelling
2323+The following pages introduce different features in _Graphcache_, which together make it a compelling
2424alternative to the standard [document cache](../basics/document-caching.md) that `urql` uses by
2525default.
26262727- 🔁 [**Fully reactive, normalized caching.**](./normalized-caching.md) _Graphcache_ stores data in
2828- a normalized data structure. Query, mutation, and subscription results may update one another if
2828+ a normalized data structure. Query, mutation and subscription results may update one another if
2929 they share data, and the app will rerender or refetch data accordingly. This often allows your app
3030 to make fewer API requests, since data may already be in the cache.
3131- 💾 [**Custom cache resolvers**](./local-resolvers.md) Since all queries are fully resolved in the
+32-32
docs/graphcache/cache-updates.md
···1111that is found in a result will be stored under the entity's key.
12121313A query's result is represented as a graph, which can also be understood as a tree structure,
1414-starting from the root `Query` entity which then connects to other entities via links, which are
1414+starting from the root `Query` entity, which then connects to other entities via links, which are
1515relations stored as keys, where each entity has records that store scalar values, which are the
1616tree's leafs. On the previous page, on ["Local Resolvers"](./local-resolvers.md), we've seen how
1717resolvers can be attached to fields to manually resolve other entities (or transform record fields).
···2323[quote](./normalized-caching.md/#storing-normalized-data):
24242525> Any mutation or subscription can also be written to this data structure. Once Graphcache finds a
2626-> keyable entity in their results it's written to its relational table which may update other queries
2727-> in our application.
2626+> keyable entity in their results it's written to its relational table, which may update other
2727+> queries in our application.
28282929This means that mutations and subscriptions still write and update entities in the cache. These
3030-updates are then reflected on all queries that our app currently uses. However, there are
3131-limitations to this. While resolvers can be used to passively change data for queries, for mutations
3030+updates are then reflected on all active queries that our app uses. However, there are limitations to this.
3131+While resolvers can be used to passively change data for queries, for mutations
3232and subscriptions we sometimes have to write **updaters** to update links and relations.
3333This is often necessary when a given mutation or subscription deliver a result that is more granular
3434than the cache needs to update all affected entities.
···6262An "updater" may be attached to a `Mutation` or `Subscription` field and accepts four positional
6363arguments, which are the same as [the resolvers' arguments](./local-resolvers.md):
64646565-- `result`: The full API result that's currently being written to the cache. Typically we'd want to
6565+- `result`: The full API result that's being written to the cache. Typically we'd want to
6666 avoid coupling by only looking at the current field that the updater is attached to, but it's
6767 worth noting that we can access any part of the result.
6868- `args`: The arguments that the field has been called with, which will be replaced with an empty
···7070- `cache`: The `cache` instance, which gives us access to methods allowing us to interact with the
7171- local cache. Its full API can be found [in the API docs](../api/graphcache.md#cache). On this page
7272 we use it frequently to read from and write to the cache.
7373-- `info`: This argument shouldn't be used frequently but it contains running information about the
7373+- `info`: This argument shouldn't be used frequently, but it contains running information about the
7474 traversal of the query document. It allows us to make resolvers reusable or to retrieve
7575 information about the entire query. Its full API can be found [in the API
7676 docs](../api/graphcache.md#info).
77777878The cache updaters return value is disregarded (and typed as `void` in TypeScript), which makes any
7979-method that they call on the `cache` instance a side-effect, which may trigger additional cache
7979+method that they call on the `cache` instance a side effect, which may trigger additional cache
8080changes and updates all affected queries as we modify them.
81818282## Manually updating entities
···144144> [the `gql` tag function](../api/core.md#gql) because `writeFragment` only accepts
145145> GraphQL `DocumentNode`s as inputs, and not strings.
146146147147-### Cache Updates outside of updates
147147+### Cache Updates outside updates
148148149149-Cache updates are **not** possible outside of `updates`. If we attempt to store the `cache` in a
150150-variable and call its methods outside of any `updates` functions (or functions, like `resolvers`)
149149+Cache updates are **not** possible outside `updates`. If we attempt to store the `cache` in a
150150+variable and call its methods outside any `updates` functions (or functions, like `resolvers`)
151151then Graphcache will throw an error.
152152153153-Methods like these cannot be called outside of the `cacheExchange`'s `updates` functions, because
153153+Methods like these cannot be called outside the `cacheExchange`'s `updates` functions, because
154154all updates are isolated to be _reactive_ to mutations and subscription events. In Graphcache,
155155out-of-band updates aren't permitted because the cache attempts to only represent the server's
156156state. This limitation keeps the data of the cache true to the server data we receive from API
157157results and makes its behaviour much more predictable.
158158159159-If we still manage to call any of the cache's methods outside of its callbacks in its configuration,
159159+If we still manage to call any of the cache's methods outside its callbacks in its configuration,
160160we will receive [a "(2) Invalid Cache Call" error](./errors.md#2-invalid-cache-call).
161161162162## Updating lists or links
···202202Here we use the `cache.updateQuery` method, which is similar to the `cache.readQuery` method that
203203we've seen on the "Local Resolvers" page before](./local-resolvers.md#reading-a-query).
204204205205-This method accepts a callback which will give us the `data` of the query, as read from the locally
206206-cached data and we may return an updated version of this data. While we may want to instinctively
205205+This method accepts a callback, which will give us the `data` of the query, as read from the locally
206206+cached data, and we may return an updated version of this data. While we may want to instinctively
207207opt for immutably copying and modifying this data, we're actually allowed to mutate it directly,
208208since it's just a copy of the data that's been read by the cache.
209209210210This `data` may also be `null` if the cache doesn't actually have enough locally cached information
211211to fulfil the query. This is important because resolvers aren't actually applied to cache methods in
212212-updaters. All resolvers are ignored so it becomes impossible to accidentally commit transformed data
212212+updaters. All resolvers are ignored, so it becomes impossible to accidentally commit transformed data
213213to our cache. We could safely add a resolver for `Todo.createdAt` and wouldn't have to worry about
214214an updater accidentally writing it to the cache's internal data structure.
215215···219219the cache. However, we've used a rather simple example when we've looked at a single list on a known
220220field.
221221222222-In many schemas pagination is quite common and when we for instance delete a todo then knowing which
223223-list to update becomes unknowable. We cannot know ahead of time how many pages (and using which
224224-variables) we've already accessed. This knowledge in fact _shouldn't_ be available to Graphcache.
225225-Querying the `Client` is an entirely separate concern that's often colocated with some part of our
222222+In many schemas pagination is quite common, and when we for instance delete a todo then knowing the
223223+lists to update becomes unknowable. We cannot know ahead of time how many pages (and its variables)
224224+we've already accessed. This knowledge in fact _shouldn't_ be available to Graphcache. Querying the
225225+`Client` is an entirely separate concern that's often colocated with some part of our
226226UI code.
227227228228```graphql
···231231}
232232```
233233234234-Suppose we have the above mutation which deletes a `Todo` entity by its ID. Our app may query a list
234234+Suppose we have the above mutation, which deletes a `Todo` entity by its ID. Our app may query a list
235235of these items over many pages with separate queries being sent to our API, which makes it hard to
236236-know which fields should be checked:
236236+know the fields that should be checked:
237237238238```graphql
239239query PaginatedTodos ($skip: Int) {
···244244}
245245```
246246247247-Instead, we can **introspect an entity's fields** to find out dynamically which fields we may want
248248-to update. This is possible thanks to [the `cache.inspectFields`
249249-method](../api/graphcache.md#inspectfields). This method accepts a key or a keyable entity like the
247247+Instead, we can **introspect an entity's fields** to find the fields we may want to update
248248+dynamically. This is possible thanks to [the `cache.inspectFields`
249249+method](../api/graphcache.md#inspectfields). This method accepts a key, or a keyable entity like the
250250`cache.keyOfEntity` method that [we've seen on the "Local Resolvers"
251251page](./local-resolvers.md#resolving-by-keys) or the `cache.resolve` method's first argument.
252252···290290- `arguments`: The arguments for the given field, since each field that accepts arguments can be
291291 accessed multiple times with different arguments. In this example we're looking at
292292 `arguments.skip` to find all unique pages.
293293-- `fieldKey`: This is the field's key which can come in useful to retrieve a field using
293293+- `fieldKey`: This is the field's key, which can come in useful to retrieve a field using
294294 `cache.resolve(entityKey, fieldKey)` to prevent the arguments from having to be stringified
295295 repeatedly.
296296···326326327327We may use the cache's [`cache.invalidate` method](../api/graphcache.md#invalidate) to either
328328invalidate entire entities or individual fields. It has the same signature as [the `cache.resolve`
329329-method](../api/graphcache.md#resolve) which we've already seen [on the "Local Resolvers" page as
329329+method](../api/graphcache.md#resolve), which we've already seen [on the "Local Resolvers" page as
330330well](./local-resolvers.md#resolving-other-fields). We can simplify the previous update we've written
331331with a call to `cache.invalidate`:
332332···391391If we know what result a mutation may return, why wait for the GraphQL API to fulfill our mutations?
392392393393Additionally to the `updates` configuration we may also pass an `optimistic` option to the
394394-`cacheExchange` which is a factory function using which we can create a "virtual" result for a
394394+`cacheExchange` which is a factory function using, which we can create a "virtual" result for a
395395mutation. This temporary result can be applied immediately to the cache to give our users the
396396illusion that mutations were executed immediately, which is a great method to reduce waiting time
397397and to make our apps feel snappier.
···413413- `cache`: The `cache` instance, which gives us access to methods allowing us to interact with the
414414- local cache. Its full API can be found [in the API docs](../api/graphcache.md#cache). On this page
415415 we use it frequently to read from and write to the cache.
416416-- `info`: This argument shouldn't be used frequently but it contains running information about the
416416+- `info`: This argument shouldn't be used frequently, but it contains running information about the
417417 traversal of the query document. It allows us to make resolvers reusable or to retrieve
418418 information about the entire query. Its full API can be found [in the API
419419 docs](../api/graphcache.md#info).
420420421421The usual `parent` argument isn't present since optimistic functions don't have any server data to
422422handle or deal with and instead create this data. When a mutation is run that contains one or more
423423-optimistic mutation fields, Graphcache picks these up and generates immediate changes which it
423423+optimistic mutation fields, Graphcache picks these up and generates immediate changes, which it
424424applies to the cache. The `resolvers` functions also trigger as if the results were real server
425425results.
426426···464464Sometimes it's not possible for us to retrieve all data that an optimistic update requires to create
465465a "fake result" from the cache or from all existing variables.
466466467467-This is why Graphcache allows for a small escape hatch for these scenarios which allows us to access
468468-additional variables which we may want to pass from our UI code to the mutation. For instance, given
467467+This is why Graphcache allows for a small escape hatch for these scenarios, which allows us to access
468468+additional variables, which we may want to pass from our UI code to the mutation. For instance, given
469469a mutation like the following we may add more variables than the mutation specifies:
470470471471```graphql
+15-15
docs/graphcache/errors.md
···7788**This document lists out all errors and warnings in `@urql/exchange-graphcache`.**
991010-Any unexpected behaviour, condition, or error will be marked by an error or warning
1111-in development, which will output a helpful little message. Sometimes however, this
1212-message may not actually tell you everything about what's going on.
1010+Any unexpected behaviour or condition will be marked by an error or warning
1111+in development. This will output as a helpful little message. Sometimes, however, this
1212+message may not actually tell you about everything that's going on.
13131414This is a supporting document that explains every error and attempts to give more
1515information on how you may be able to fix some issues or avoid these errors/warnings.
···1717## (1) Invalid GraphQL document
18181919> Invalid GraphQL document: All GraphQL documents must contain an OperationDefinition
2020-> node for a query, subscription, or mutation.
2020+> node for a query, subscription or mutation.
21212222There are multiple places where you're passing in GraphQL documents, either through
2323methods on `Cache` (e.g. `cache.updateQuery`) or via `urql` using the `Client` or
2424hooks like `useQuery`.
25252626-Your queries must always contain a main operation, so either a query, mutation, or
2626+Your queries must always contain a main operation, one of: query, mutation, or
2727subscription. This error occurs when this is missing, because the `DocumentNode`
2828is maybe empty or only contains fragments.
2929···3333> operations like write or query, or as part of its resolvers, updaters,
3434> or optimistic configs.
35353636-If you're somehow accessing the `Cache` (an instance of `Store`) outside of any
3636+If you're somehow accessing the `Cache` (an instance of `Store`) outside any
3737of the usual operations then this error will be thrown.
38383939Please make sure that you're only calling methods on the `cache` as part of
4040-configs that you pass to your `cacheExchange`. Outside of these functions the cache
4040+configs that you pass to your `cacheExchange`. Outside these functions the cache
4141must not be changed.
42424343However when you're not using the `cacheExchange` and are trying to use the
···4545initialised correctly.
46464747This is a safe-guard to prevent any asynchronous work to take place, or to
4848-avoid mutating the cache outside of any normal operation.
4848+avoid mutating the cache outside any normal operation.
49495050## (3) Invalid Object type
5151···7474Check whether your schema is up-to-date or whether you're using an invalid
7575field somewhere, maybe due to a typo.
76767777-As the warning states, this won't lead any operation to abort or an error
7777+As the warning states, this won't lead any operation to abort, or an error
7878to be thrown!
79798080## (5) Invalid Abstract type
···192192193193As data is written to the cache, this warning is issued when `undefined` is encountered.
194194GraphQL results should never contain an `undefined` value, so this warning will let you
195195-know which part of your result did contain `undefined`.
195195+know the part of your result that did contain `undefined`.
196196197197## (14) Couldn't find \_\_typename when writing.
198198···215215> If this is intentional, create a `keys` config for `???` that always returns null.
216216217217This error occurs when the cache can't generate a key for an entity. The key
218218-would then effectively be `null` and the entity won't be cached by a key.
218218+would then effectively be `null`, and the entity won't be cached by a key.
219219220220Conceptually this means that an entity won't be normalised but will indeed
221221be cached by the parent's key and field, which is displayed in the first
···225225226226But if your entity at that place doesn't have any `id` fields, then you may
227227have to create a custom `keys` config. This `keys` function either needs to
228228-return a unique ID for your entity or it needs to explicitly return `null` to silence
228228+return a unique ID for your entity, or it needs to explicitly return `null` to silence
229229this warning.
230230231231## (16) Heuristic Fragment Matching
···259259`@populate` directive to fields it first checks whether the type is valid and
260260exists on the schema.
261261262262-If the field does not have sufficient type information because it doesn't exist
262262+If the field does not have enough type information because it doesn't exist
263263on the schema or does not match expectations then this warning is logged.
264264265265Check whether your schema is up-to-date or whether you're using an invalid
···303303304304## (21) Invalid mutation
305305306306-> Invalid mutation field `???` is not in the defined schema but the `updates` option is referencing it.
306306+> Invalid mutation field `???` is not in the defined schema, but the `updates` option is referencing it.
307307308308When you're passing an introspected schema to the cache exchange, it is
309309able to check whether your `opts.updates.Mutation` is valid.
···313313314314## (22) Invalid subscription
315315316316-> Invalid subscription field: `???` is not in the defined schema but the `updates` option is referencing it.
316316+> Invalid subscription field: `???` is not in the defined schema, but the `updates` option is referencing it.
317317318318When you're passing an introspected schema to the cache exchange, it is
319319able to check whether your `opts.updates.Subscription` is valid.
+9-9
docs/graphcache/local-resolvers.md
···5151- `args`: The arguments that the field is being called with, which will be replaced with an empty
5252 object if the field hasn't been called with any arguments. For example, if the field is queried as
5353 `name(capitalize: true)` then `args` would be `{ capitalize: true }`.
5454-- `cache`: Unlike in GraphQL.js this will not be the context but a `cache` instance, which gives us
5454+- `cache`: Unlike in GraphQL.js this will not be the context, but a `cache` instance, which gives us
5555 access to methods allowing us to interact with the local cache. Its full API can be found [in the
5656 API docs](../api/graphcache.md#cache).
5757-- `info`: This argument shouldn't be used frequently but it contains running information about the
5757+- `info`: This argument shouldn't be used frequently, but it contains running information about the
5858 traversal of the query document. It allows us to make resolvers reusable or to retrieve
5959 information about the entire query. Its full API can be found [in the API
6060 docs](../api/graphcache.md#info).
···9696We may also run into situations where we'd like to generalise the resolver and not make it dependent
9797on the exact field it's being attached to. In these cases, the [`info`
9898object](../api/graphcache.md#info) can be very helpful as it provides us information about the
9999-current query traversal and which part of the query document the cache is currently processing. The
100100-`info.fieldName` property is one of these properties and lets us know which field the resolver is
101101-currently operating on. Hence, we can create a reusable resolver like so:
9999+current query traversal, and the part of the query document the cache is processing. The
100100+`info.fieldName` property is one of these properties and lets us know the field that the resolver is
101101+operating on. Hence, we can create a reusable resolver like so:
102102103103```js
104104const transformToDate = (parent, _args, _cache, info) =>
···176176```
177177178178The `__typename` field is required. Graphcache will [use its keying
179179-logic](./normalized-caching.md#custom-keys-and-non-keyable-entities) and your custom `keys`
179179+logic](./normalized-caching.md#custom-keys-and-non-keyable-entities), and your custom `keys`
180180configuration to generate a key for this entity and will then be able to look this entity up in its
181181local cache. As with regular queries, the resolver is known to return a link since the `todo(id:
182182$id) { id }` will be used with a selection set, querying fields on the entity.
···215215216216## Resolving other fields
217217218218-In the above two examples we've seen how a resolver can replace Graphcache's logic which usually
218218+In the above two examples we've seen how a resolver can replace Graphcache's logic, which usually
219219reads links and records only from its locally cached data. We've seen how a field on a record can
220220use `parent[fieldName]` to access its cached record value and transform it and how a resolver for a
221221link can return a partial entity [or a key](#resolving-by-keys).
···236236237237- `entity`: This is the entity on which we'd like to access a field. We may either pass a keyable,
238238 partial entity, e.g. `{ __typename: 'Todo', id: 1 }` or a key. It takes the same inputs as [the
239239- `cache.keyOfEntity` method](../api/graphcache.md#keyofentity) which we've seen earlier in the
239239+ `cache.keyOfEntity` method](../api/graphcache.md#keyofentity), which we've seen earlier in the
240240 ["Resolving by keys" section](#resolving-by-keys). It also accepts `null` which causes it to
241241 return `null`, which is useful for chaining multiple `resolve` calls for deeply accessing a field.
242242- `fieldName`: This is the field's name we'd like to access. If we're looking for the record on
···381381382382### Reading a query
383383384384-At any point, the `cache` allows us to read entirely separate queries in our resolvers which starts
384384+At any point, the `cache` allows us to read entirely separate queries in our resolvers, which starts
385385a separate virtual operation in our resolvers. When we call `cache.readQuery` with a query and
386386variables we can execute an entirely new GraphQL query against our cached data:
387387
+3-3
docs/graphcache/offline.md
···66# Offline Support
7788_Graphcache_ allows you to build an offline-first app with built-in offline and persistence support,
99-by means of adding a `storage` interface. In combination with its [Schema
99+by adding a `storage` interface. In combination with its [Schema
1010Awareness](./schema-awareness.md) support and [Optimistic
1111Updates](./cache-updates.md#optimistic-updates) this can be used to build an application that
1212serves cached data entirely from memory when a user's device is offline and still display
···5555`offlineExchange`. The `storage` is an adapter that contains methods for storing cache data in a
5656persisted storage interface on the user's device.
57575858-By default we can use the default storage option that `@urql/exchange-graphcache` comes with. This
5858+By default, we can use the default storage option that `@urql/exchange-graphcache` comes with. This
5959default storage uses [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) to
6060persist the cache's data. We can use this default storage by importing the `makeDefaultStorage`
6161function from `@urql/exchange-graphcache/default-storage`.
···121121have different strategies for dealing with this.
122122123123[The API docs list the entire interface for the `storage` option.](../api/graphcache.md#storage-option)
124124-There we can see which methods we need to implement to implement a custom storage engine.
124124+There we can see the methods we need to implement to implement a custom storage engine.
125125126126Following is an example of the simplest possible storage engine, which uses the browser's
127127[Local Storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).
+6-6
docs/graphcache/schema-awareness.md
···7788Previously, [on the "Normalized Caching" page](./normalized-caching.md) we've seen how Graphcache
99stores normalized data in its store and how it traverses GraphQL documents to do so. What we've seen
1010-is that just using the GraphQL document for traversal and the `__typename` introspection field
1010+is that just using the GraphQL document for traversal, and the `__typename` introspection field
1111Graphcache is able to build a normalized caching structure that keeps our application up-to-date
1212across API results, allows it to store data by entities and keys, and provides us configuration
1313options to write [manual cache updates](./cache-updates.md) and [local
···4040- Fragments will be matched deterministically: A fragment can be written to be on an interface type
4141 or multiple fragments can be spread for separate union'ed types in a selection set. In many cases,
4242 if Graphcache doesn't have any schema information then it won't know what possible types a field
4343- can return and may sometimes make a best guess and [issue a
4343+ can return and may sometimes make a guess and [issue a
4444 warning](./errors.md#16-heuristic-fragment-matching). If we pass Graphcache a `schema` then it'll
4545 be able to match fragments deterministically.
4646- A schema may have non-default names for its root types; `Query`, `Mutation`, and `Subscription`.
···5151 start checking whether any of the configuration options actually don't exist, maybe because we've
5252 typo'd them. This is a small detail but can make a large different in a longer configuration.
5353- Lastly; a schema contains information on **which fields are optional or required**. When
5454- Graphcache has a schema it knows which fields can be made optional and it'll be able to generate
5454+ Graphcache has a schema it knows optional fields that may be left out, and it'll be able to generate
5555 "partial results".
56565757### Partial Results
58585959As we navigate an app that uses Graphcache we may be in states where some of our data is already
6060-cached and some isn't. Graphcache normalizes data and stores it in tables for links and records for
6060+cached while some aren't. Graphcache normalizes data and stores it in tables for links and records for
6161each entity, which means that sometimes it can maybe even execute a query against its cache that it
6262hasn't sent to the API before.
6363···7171before it sent an API result.](../assets/partial-results.png)
72727373Without a `schema` and information on which fields are optional, Graphcache will consider a "partial
7474-result" as a cache miss. If we don't have all of the information for a query then we can't execute
7474+result" as a cache miss. If we don't have all the information for a query then we can't execute
7575it against the locally cached data after all. However, an API's schema contains information on which
7676-fields are required and which fields are optional, and if our apps are typed with this schema and
7676+fields are required and optional, and if our apps are typed with this schema and
7777TypeScript, can't we then use and handle these partial results before a request is sent to the API?
78787979This is the idea behind "Schema Awareness" and "Partial Results". When Graphcache has `schema`
+1-1
docs/showcase.md
···66# Showcase
7788`urql` wouldn't be the same without our growing and loving community of users,
99-maintainers, and supporters. This page is specifically dedicated to all of you!
99+maintainers and supporters. This page is specifically dedicated to all of you!
10101111## Used by folks at
1212