Repository: FormidableLabs/urql
Pull request #605 (2020-03-12 10:57:25)

(docs) - Post-release update


<!--
  Thanks for opening a pull request! We appreciate your dedication and help!
  Before submitting your pull request, please make sure to read our CONTRIBUTING guide.

  The best contribution is always a PR! However, if you're starting to work on a large
  change, it's best to make sure to open an issue first.

  If this PR is already related to an issue, please reference it like so:
  Resolves #123
-->

PR to amend some of the initial bugs, as well as implement some of the "nice to haves" as discussed in #548.

<!--
  Roughly list the changes you've made and which packages are affected.
  Leave some notes on what may be noteworthy files you've changed.
  And lastly, please let us know if you think this is a breaking change.
-->


changeset-bot[bot]: ###  💥  No Changeset

Latest commit: 6b14f2aab02a3f6125a1deecb54c21a6ed995353

Merging this PR will not cause any packages to be released. If these changes should not cause updates to packages in this repo, this is fine 🙂

**If these changes should be published to npm, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/FormidableLabs/urql/new/docs-post-release-update?filename=.changeset/empty-beans-run.md&value=---%0A%22urql-docs%22%3A%20patch%0A---%0A%0ADocs%20site%20post-release%20update%0A)



Patch:
diff --git a/.changeset/shaggy-fans-grab.md b/.changeset/shaggy-fans-grab.md
new file mode 100644
index 00000000..909aab22
--- /dev/null
+++ b/.changeset/shaggy-fans-grab.md
@@ -0,0 +1,5 @@
+---
+'@urql/core': minor
+---
+
+Add `additionalTypenames` to the `OperationContext`, this allows the `document-cache` to invalidate efficiently when the `__typename` is unknown at the initial fetch.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9a16706d..136074bf 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -160,6 +160,30 @@ yarn
 yarn run check
 ```
 
+## When and how do I add a changeset?
+
+Our changelogs and releases are maintained using `changeset`. For each PR that actively changes the
+behaviour of one or more packages in our monorepo it allows you to log the change and classify it
+per package as either `patch`, `minor`, or `major`.
+
+You won't need to add a changeset if you're simply making "non-visible" changes to the docs or other
+files that aren't published to `npm`.
+
+If you are however making visible changes you can track a change by calling the `changeset` CLI.
+
+```sh
+# In the root of the urql repository call:
+yarn changeset
+```
+
+This will open an interactive CLI that asks you to track your changes and enter a change message.
+You should always add to the message, what you've changed (not which package if it's only for a
+single package though) and what the impact is. If it's a breaking change it should also include some
+instructions on how users should update their code.
+
+This will create a new "changeset file" in the `.changeset` folder, which you should commit and
+push, so that it's added to your PR.
+
 ## How do I release new versions of our packages?
 
 The process of releasing versions is automated using `changeset`. This moves a lot of
@@ -169,12 +193,38 @@ applied and our `CHANGELOG`s are updated.
 
 First check what changes you're about to release with `yarn changeset status`.
 
-Then the process is similar to using `yarn version` and `yarn publish`:
+Then the process is similar to using `yarn version` and `yarn publish`.
+
+First we'll run `yarn changeset version` to bump all packages' versions and update CHANGELOG files:
 
 ```sh
 # This will automatically bump versions as necessary and update CHANGELOG files:
 yarn changeset version
-# Please check all versions and CHANGELOGs manually.
+```
+
+> **Note:** This command requires you to create a [GitHub Personal Access Token](https://github.com/settings/tokens/new)
+> and have it set on the `GITHUB_TOKEN` environment variable, either globally, e.g. in your
+> `~/.profile` file, or locally in a `.env` file.
+
+Then verify that the updated `package.json` and `CHANGELOG.md` files look correct and commit the
+changes:
+
+```sh
+# Commit all updated files
+git commit -a -m 'Version Packages'
+```
+
+At this point we have a new commit with packages that have been updated and should be published.
+Please don't push this commit before publishing so that we keep `master` in a pre-release state, in
+case the publish actually fails!
+
+```
 # Then publish all new packages / versions:
 yarn changeset publish
+# And push the "Version Packages" commit and all tags afterwards:
+git push && git push --tags
 ```
+
+Publishing the packages will also create the tags, it won't however push them automatically. After
+you've pushed the new tags, please make sure to update releases with the new content in each
+`CHANGELOG.md`, like so: https://github.com/FormidableLabs/urql/releases/tag/%40urql%2Fexchange-graphcache%402.2.2
diff --git a/docs/README.md b/docs/README.md
index b6cda233..0fb5e133 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -7,20 +7,21 @@ order: 0
 
 `urql` is an implementation of a GraphQL client, built to be both easy to use for newcomers to
 GraphQL, as well as extensible, to grow to support dynamic single-app applications and highly
-customised GraphQL infrastructure. In short, `urql` prioritizes usability and adaptability.
+customized GraphQL infrastructure. In short, `urql` prioritizes usability and adaptability.
 
 As you're adopting GraphQL, `urql` becomes your primary data layer and can handle content-heavy
 pages through ["Document Caching"](./basics/document-caching.md) as well as dynamic and data-heavy
-pages through ["Normalized Caching"](./graphcache/normalized-caching.md).
+apps through ["Normalized Caching"](./graphcache/normalized-caching.md).
 
 ## Constituent Parts
 
-`urql` can be understood as a collection of connected parts and packages. When you [get
+`urql` can be understood as a collection of connected parts and packages. When you [first get
 started](./basics/getting-started.md) you'll only need to install a single package for your
 framework of choice. We're then able to declaratively send GraphQL requests to our API.
 
-All framework packages — like `urql` and `@urql/preact` — wrap the core package, which we can
-imagine as the brain of `urql` with most of its logic.
+All framework packages — like `urql`, `@urql/preact`, and `@urql/svelte` — wrap the [core
+package](./concepts/core-package.md), which we can imagine as the brain of `urql` with most of its
+logic.
 
 As we progress with implementing `urql` into our application, we're later able to extend it by
 adding ["addon packages", which we call _Exchanges_](./concepts/exchanges.md)
@@ -34,14 +35,14 @@ interest.
   guide](./basics/getting-started.md) and usage patterns for our framework of choice.
 - **Main Concepts** then explains more about how `urql` functions, what it's made up of, and covers
   the main aspects of the `Client` and GraphQL clients in general, on the ["Philosophy"
-  page](./concepts/philosophy.md)
+  page](./concepts/philosophy.md).
 - **Advanced** covers all more uncommon use-cases and contains guides that we won't need immediately
   when we get started with `urql`.
 - **Graphcache** documents one of the most important addons to `urql`, which adds ["Normalized
   Caching" support](./graphcache/normalized-caching.md) to the `Client` and enables more complex
   use-cases, smarter caching, and more dynamic apps to function.
-- **Showcase** aims to list some companies that use `urql`, third-party packages, and other helpful
-  resources, like tutorials or guides.
+- **Showcase** aims to list users of `urql`, third-party packages, and other helpful resources,
+  like tutorials and guides.
 - **API** contains a detailed list of all helpers, utilities, components, and other parts of each of
   `urql`'s packages, which may contain all details of each part and package.
 
diff --git a/docs/advanced/README.md b/docs/advanced/README.md
index 35350514..32231d26 100644
--- a/docs/advanced/README.md
+++ b/docs/advanced/README.md
@@ -9,9 +9,9 @@ In this chapter we'll dive into various topics of "advanced" `urql` usage. This
 catch-all chapter of various use-cases that can only be covered after [the "Concepts"
 chapter.](../concepts/README.md)
 
-- **Subscriptions** covers how to use `useSubscription` and how to set up GraphQL subscriptions with
+- [**Subscriptions**](./subscriptions.md) covers how to use `useSubscription` and how to set up GraphQL subscriptions with
   `urql`.
-- **Server-side Rendering** guides us through how to set up server-side rendering and rehydration.
-- **Auto-populate Mutations** presents the `populateExchange` addon which can make it easier to
+- [**Server-side Rendering**](./server-side-rendering.md) guides us through how to set up server-side rendering and rehydration.
+- [**Auto-populate Mutations**](./auto-populate-mutations.md) presents the `populateExchange` addon which can make it easier to
   update normalized data after mutations.
-- **Testing** covers how to test components that use `urql` particularly in React.
+- [**Testing**](./testing.md) covers how to test components that use `urql` particularly in React.
diff --git a/docs/advanced/server-side-rendering.md b/docs/advanced/server-side-rendering.md
index d10b9e1a..f6c7db89 100644
--- a/docs/advanced/server-side-rendering.md
+++ b/docs/advanced/server-side-rendering.md
@@ -12,8 +12,8 @@ through the `ssrExchange.`
 ## The SSR Exchange
 
 The `ssrExchange` has two functions. On the server-side it's able to gather all results as they're
-being fetched, which can then be serialised and sent to the client. On the client-side it's able to
-use these serialised results to rehydrate and render the application without refetching this data.
+being fetched, which can then be serialized and sent to the client. On the client-side it's able to
+use these serialized results to rehydrate and render the application without refetching this data.
 
 To start out with the `ssrExchange` we have to add the exchange to our `Client`:
 
@@ -28,7 +28,7 @@ import {
 
 const isServerSide = typeof window === 'undefined';
 
-// The `ssrExchange` must be initialised with `isClient` and `initialState`
+// The `ssrExchange` must be initialized with `isClient` and `initialState`
 const ssr = ssrExchange({
   isClient: !isServerSide,
   initialState: !isServerSide ? window.__URQL_DATA__ : undefined,
@@ -44,11 +44,11 @@ const client createClient({
 });
 ```
 
-The `ssrExchange` must be initialised with the `isClient` and `initialState` options. The `isClient`
+The `ssrExchange` must be initialized with the `isClient` and `initialState` options. The `isClient`
 option tells the exchange whether it's on the server- or client-side. In our example we use `typeof window` to determine this, but in Webpack environments you may also be able to use `process.browser`.
 
-The `initialState` option should be set to the serialised data you retrieve on your server-side.
-This data may be retrieved using methods on `ssrExchange()`. You can retrive the serialised data
+The `initialState` option should be set to the serialized data you retrieve on your server-side.
+This data may be retrieved using methods on `ssrExchange()`. You can retrive the serialized data
 after server-side rendering using `ssr.extractData()`:
 
 ```js
diff --git a/docs/api/core.md b/docs/api/core.md
index 373beac3..e7205f22 100644
--- a/docs/api/core.md
+++ b/docs/api/core.md
@@ -194,16 +194,17 @@ data that can be passed from almost all API methods in `urql` that deal with
 Some of these options are set when the `Client` is initialised, so in the following list of
 properties you'll likely see some options that exist on the `Client` as well.
 
-| Prop            | Type                                  | Description                                                                                                           |
-| --------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
-| fetchOptions    | `?RequestInit \| (() => RequestInit)` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. |
-| fetch           | `typeof fetch`                        | An alternative implementation of `fetch` that will be used by the `fetchExchange` instead of `window.fetch`           |
-| requestPolicy   | `RequestPolicy`                       | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. |
-| url             | `string`                              | The GraphQL endpoint                                                                                                  |
-| pollInterval    | `?number`                             | Every `pollInterval` milliseconds the query will be refetched.                                                        |
-| meta            | `?OperationDebugMeta`                 | Metadata that is only available in development for devtools.                                                          |
-| suspense        | `?boolean`                            | Whether suspense is enabled.                                                                                          |
-| preferGetMethod | `?number`                             | Instructs the `fetchExchange` to use HTTP GET for queries.                                                            |
+| Prop                | Type                                  | Description                                                                                                           |
+| ------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
+| fetchOptions        | `?RequestInit \| (() => RequestInit)` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. |
+| fetch               | `typeof fetch`                        | An alternative implementation of `fetch` that will be used by the `fetchExchange` instead of `window.fetch`           |
+| requestPolicy       | `RequestPolicy`                       | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. |
+| url                 | `string`                              | The GraphQL endpoint                                                                                                  |
+| pollInterval        | `?number`                             | Every `pollInterval` milliseconds the query will be refetched.                                                        |
+| meta                | `?OperationDebugMeta`                 | Metadata that is only available in development for devtools.                                                          |
+| suspense            | `?boolean`                            | Whether suspense is enabled.                                                                                          |
+| preferGetMethod     | `?number`                             | Instructs the `fetchExchange` to use HTTP GET for queries.                                                            |
+| additionalTypenames | `?number`                             | Allows you to tell the operation that it depends on certain typenames (used in document-cache.)              |
 
 It also accepts additional, untyped parameters that can be used to send more
 information to custom exchanges.
diff --git a/docs/api/graphcache.md b/docs/api/graphcache.md
index 69431365..21f41ada 100644
--- a/docs/api/graphcache.md
+++ b/docs/api/graphcache.md
@@ -200,7 +200,7 @@ generated using `cache.keyOfField`.
 cache.resolveFieldByKey({ __typename: 'Query' }, cache.keyOfField('todo', { id: 1 })); // 'Todo:1'
 ```
 
-This specialised method is likely only going to be useful in combination with
+This specialized method is likely only going to be useful in combination with
 [`cache.inspectFields`](#inspectfields).
 
 ### inspectFields
diff --git a/docs/basics/README.md b/docs/basics/README.md
index 9f6067dd..cd99045a 100644
--- a/docs/basics/README.md
+++ b/docs/basics/README.md
@@ -8,12 +8,12 @@ order: 1
 In this chapter we'll explain the basics of `urql` and how to get started with using it without any
 prior knowledge. It's split into multiple pages that are best read in order.
 
-- **Getting started** covers how to install the package and set it up.
-- **Queries** explains how to write your first GraphQL queries with `urql`.
-- **Mutations** follows up with how to define mutations.
-- **Document Caching** explains the default cache mechanism of `urql`, as opposed to the [Normalized
-  Cache](../graphcache/normalized-caching.md) which is not the default.
-- **Errors** contains information on error handling in `urql`.
+- [**Getting started**](./getting-started.md) covers how to install the package for your framework of choice and set it up.
+- [**Queries**](./queries.md) explains how to write your first GraphQL queries with `urql`.
+- [**Mutations**](./mutations.md) follows up with how to define GraphQL mutations.
+- [**Document Caching**](./document-caching.md) explains the default cache mechanism of `urql`, as opposed to the opt-in
+  [Normalized Cache](../graphcache/normalized-caching.md).
+- [**Errors**](./errors.md) contains information on error handling in `urql`.
 
 After reading "Basics" you may want to [read the "Concepts" chapter of the
 documentation](../concepts/README.md) as it explains the motivation and architecture that drives
diff --git a/docs/basics/document-caching.md b/docs/basics/document-caching.md
index 21427970..f7a8debc 100644
--- a/docs/basics/document-caching.md
+++ b/docs/basics/document-caching.md
@@ -11,28 +11,31 @@ to a GraphQL API repeatedly by caching the result of each query.
 This works like the cache in a browser. `urql` creates a key for each request that is sent based on
 a query and its variables.
 
+The default _document caching_ logic is implemented in the default `cacheExchange`. We'll learn more
+about ["Exchanges" on a later page.](../concepts/exchanges.md)
+
 ## Operation Keys
 
 ![Keys for GraphQL Requests](../assets/urql-operation-keys.png)
 
-Once a result comes in it's cached indefinitely by their key. This means that each unique request
+Once a result comes in it's cached indefinitely by its key. This means that each unique request
 can have exactly one cached result.
 
-However, we also need to invalidate the cache result so new requests for the same data can be
-sent when we know that some results are outdated. A result may be outdated because a mutation has
-been executed on data that has been queried previously.
+However, we also need to invalidate the cached results so that requests are sent again and updated,
+when we know that some results are out-of-date. With document caching we assume that a result may
+be invalidated by a mutation that executes on data that has been queried previously.
 
-In GraphQL the client can request additional type information on a query by adding the `__typename`
-field. This field return the name of the type of a piece of data in a requests, and we use it
-to detect commonalities and data dependencies between queries and mutations.
+In GraphQL the client can request additional type information by adding the `__typename` field to a
+query's _selection set_. This field returns the name of the type for an object in the results, and
+we use it to detect commonalities and data dependencies between queries and mutations.
 
 ![Document Caching](../assets/urql-document-caching.png)
 
-When we send a mutation that contains types that another query's results contains as well, that
-query's result is removed from the cache.
+In short, when we send a mutation that contains types that another query's results contains as well,
+that query's result is removed from the cache.
 
 This is an aggressive form of cache invalidation. However, it works well for content-driven sites,
-although it doesn't deal with normalized data or IDs.
+while it doesn't deal with normalized data or IDs.
 
 ## Request Policies
 
@@ -50,6 +53,41 @@ docs.](../api/core.md#requestpolicy-type)
 This cache has a small trade-off! If we request a list of data and the API returns an empty list,
 the cache won't be able to see the `__typename` of said list and won't invalidate.
 
-Once you've encountered this problem you've likely hit the limits of the _Document Caching_
-approach, and you may want to [switch to "Normalized Caching"
-instead.](../graphcache/normalized-caching.md)
+There are two ways to fix this issue, supplying `additionalTypenames` to the context of your query or [switch to "Normalized Caching"
+instead](../graphcache/normalized-caching.md).
+
+### Adding typenames
+
+This will elaborate about the first fix for empty lists, the `additionalTypenames`.
+
+Example where this would occur:
+
+```js
+const query = `query { todos { id name } }`;
+const result = { todos: [] };
+```
+
+At this point we don't know what types are possible for this query, so a best practice when using
+the default cache is to add `additionalTypenames` for this query.
+
+```js
+// Keep the reference stable.
+const context = useMemo(() => ({ additionalTypenames: ['Todo'] }), []);
+const [result] = useQuery({ query, context });
+```
+
+Now the cache will know when to invalidate this query even when the list is empty.
+
+We also have the possibility to use this for `mutations`.
+There are moments where a mutation can cause a side-effect on your server side and it needs
+to invalidate an additional entity.
+
+```js
+const [result, execute] = useMutation(`mutation($name: String!) { createUser(name: $name) }`);
+
+const onClick = () => {
+  execute({ context: { additionalTypenames: ['Wallet'] } });
+};
+```
+
+Now our `mutation` knows that when it completes it has an additional type to invalidate.
diff --git a/docs/basics/errors.md b/docs/basics/errors.md
index 47e05d8e..8c2495a2 100644
--- a/docs/basics/errors.md
+++ b/docs/basics/errors.md
@@ -6,7 +6,7 @@ order: 4
 # Error handling
 
 When we use a GraphQL API there are two kinds of errors we may encounter: Network Errors and GraphQL
-Errors from the API. Since it's common to encounter either of them in development, there's a
+Errors from the API. Since it's common to encounter either of them, there's a
 [`CombinedError`](../api/core.md#combinederror-class) class that can hold and abstract either.
 
 We may encounter a `CombinedError` when using `urql` wherever an `error` may be returned, typically
diff --git a/docs/basics/getting-started.md b/docs/basics/getting-started.md
index 1432039c..d3ee1bda 100644
--- a/docs/basics/getting-started.md
+++ b/docs/basics/getting-started.md
@@ -7,14 +7,14 @@ order: 0
 
 In the ["Introduction"](./README.md) we read how `urql` consists of multiple constituent parts that
 make up a GraphQL client. Hence there are multiple packages — one for each framework we officially
-support — that you'll get started with.
+support — that we'll get started with on this page.
 
 ## React & Preact
 
-This "Getting Started" guide covers how to install and set up `urql` and the Client, for React and
-Preact. Since the `urql` and `@urql/preact` packages share most of their API one-to-one, when
-reading the documentation on React, all examples are essentially the same, except that we'll use the
-`@urql/preact` package instead of the `urql` package for Preact.
+This "Getting Started" guide covers how to install and set up `urql` and the `Client`, for React and
+Preact. Since the `urql` and `@urql/preact` packages share most of their API and are used in the
+same way, when reading the documentation on React, all examples are essentially the same, except
+that we'd want to use the `@urql/preact` package instead of the `urql` package for Preact.
 
 ### Installation
 
@@ -27,17 +27,17 @@ yarn add urql graphql
 npm install --save urql graphql
 ```
 
-To use urql with Preact, we have to install `@urql/preact` instead of `urql` and import from
+To use `urql` with Preact, we have to install `@urql/preact` instead of `urql` and import from
 that package instead. Otherwise all examples for Preact will be the same.
 
 Most libraries related to GraphQL also need the `graphql` package to be installed as a peer
-dependency, so that they can adapt to your specific versioning requirements, which is why we'll need
-to install `graphql` as well.
+dependency, so that they can adapt to your specific versioning requirements. That's why we'll need
+to install `graphql` alongside `urql`.
 
-Both the `urql` packages and `graphql` follow [semantic versioning](https://semver.org) and `urql`
-packages will define a range of compatible versions of `graphql`. Watch out for breaking changes in
-the future however, in which case your package manager may warn you about `graphql` being out of the
-defined peer dependency range.
+Both the `urql` and `graphql` packages follow [semantic versioning](https://semver.org) and all
+`urql` packages will define a range of compatible versions of `graphql`. Watch out for breaking
+changes in the future however, in which case your package manager may warn you about `graphql` being
+out of the defined peer dependency range.
 
 ### Setting up the `Client`
 
@@ -55,8 +55,8 @@ const client = createClient({
 At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started.
 
 Another common option is `fetchOptions`. This option allows us to customize the options that will be
-passed to `fetch` when a request is sent to the given API `url`. We may pass in an object or a
-function returning an object to this option.
+passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object or
+a function returning an options object.
 
 In the following example we'll add a token to each `fetch` request that our `Client` sends to our
 GraphQL API.
diff --git a/docs/basics/mutations.md b/docs/basics/mutations.md
index fd3cd20a..78c367cc 100644
--- a/docs/basics/mutations.md
+++ b/docs/basics/mutations.md
@@ -11,7 +11,7 @@ API [previously on the "Queries" page.](./queries.md)
 
 ## React & Preact
 
-This guide covers how to query data with React and Preact, which share almost the same API.
+This guide covers how to send mutations in React and Preact, which share almost the same API.
 
 Both libraries offer a `useMutation` hook and a `Mutation` component. The latter accepts the same
 parameters but we won't cover it in this guide. [Look it up in the API docs if you prefer
@@ -19,14 +19,15 @@ render-props components.](../api/urql.md#components)
 
 ### Sending a mutation
 
-Let's pick up an example in an imaginary todo items GraphQL API and dive into an example!
-We'll set up a mutation that _updates_ a todo item.
+Let's again pick up an example with an imaginary GraphQL API for todo items, and dive into an
+example! We'll set up a mutation that _updates_ a todo item's title.
 
 ```jsx
 const UpdateTodo = `
   mutation ($id: ID!, $title: String!) {
     updateTodo (id: $id, title: $title) {
       id
+      title
     }
   }
 `;
diff --git a/docs/basics/queries.md b/docs/basics/queries.md
index 4e3147b8..f9df531f 100644
--- a/docs/basics/queries.md
+++ b/docs/basics/queries.md
@@ -18,8 +18,8 @@ render-props components.](../api/urql.md#components)
 
 ### Run a first query
 
-For the following examples imagine we are querying data from a GraphQL API that contains todo items
-Let's dive right into an example!
+For the following examples, we'll imagine that we're querying data from a GraphQL API that contains
+todo items. Let's dive right into it!
 
 ```jsx
 import { useQuery } from 'urql';
@@ -53,19 +53,19 @@ const Todos = () => {
 };
 ```
 
-Here we have implemented our first GraphQL query to fetch todos. We see that using `useQuery`
-accepts options — in this case we've set `query` to our GraphQL query — and returns a tuple — an
-array that contains a result and a reexecute function.
+Here we have implemented our first GraphQL query to fetch todos. We see that `useQuery` accepts
+options and returns a tuple. In this case we've set the `query` option to our GraphQL query. The
+tuple we then get in return is an array that contains a result object and a re-execute function.
 
-The result contains several properties. The `fetching` field indicates whether we're currently loading
-data, `data` contains the actual `data` from the API's result, and `error` is set when either the
-request to the API has failed or when our result contained some `GraphQLError`s, which
+The result object contains several properties. The `fetching` field indicates whether we're currently
+loading data, `data` contains the actual `data` from the API's result, and `error` is set when either
+the request to the API has failed or when our API result contained some `GraphQLError`s, which
 we'll get into later on the ["Errors" page](./errors.md).
 
 ### Variables
 
-Typically we'll also need to pass variables to our queries, for instance What if we are dealing with
-pagination? For this purpose the `useQuery` hook also accepts a `variables` option, which we can use
+Typically we'll also need to pass variables to our queries, for instance, if we are dealing with
+pagination. For this purpose the `useQuery` hook also accepts a `variables` option, which we can use
 to supply variables to our query.
 
 ```jsx
@@ -89,24 +89,27 @@ const Todos = ({ from, limit }) => {
 ```
 
 As when we're sending GraphQL queries manually using `fetch`, the variables will be attached to the
-`POST` request that is sent to our GraphQL API.
+`POST` request body that is sent to our GraphQL API.
 
-Whenever the `variables` (or the `query`) option on
-the `useQuery` hook changes `fetching` will return to being `true` and a new request will be sent to
-our API, unless a result has already been cached previously.
+Whenever the `variables` (or the `query`) option on the `useQuery` hook changes `fetching` will
+switch to `true` and a new request will be sent to our API, unless a result has already been cached
+previously.
 
 ### Pausing `useQuery`
 
-In some cases we may want `useQuery` to execute a query when a precondition has been met. For
-instance, we may be building a form and want validation to only take place when a field has been
-filled out.
+In some cases we may want `useQuery` to execute a query when a pre-condition has been met, and not
+execute the query otherwise. For instance, we may be building a form and want a validation query to
+only take place when a field has been filled out.
+
+Since hooks in React can't just be commented out, the `useQuery` hook accepts a `pause` option that
+temporarily _freezes_ all changes and stops requests.
 
 In the previous example we've defined a query with mandatory arguments. The `$from` and `$limit`
 variables have been defined to be non-nullable `Int!` values.
 
 Let's pause the query we've just
 written to not execute when these variables are empty, to prevent `null` variables from being
-executed. We can do this by means of the `pause` option.
+executed. We can do this by means of setting the `pause` option to `true`:
 
 ```jsx
 const Todos = ({ from, limit }) => {
@@ -150,6 +153,9 @@ the background.
 For this reason there's another field on results, `result.stale`, which indicates that the cached
 result is either outdated or that another request is being sent in the background.
 
+Request policies aren't specific to `urql`'s React API, but are a common feature in its core. [You
+can learn more about request policies on the API docs.](../api/core.md#requestpolicy)
+
 ### Reexecuting Queries
 
 The `useQuery` hook updates and executes queries whenever its inputs, like the `query` or
diff --git a/docs/concepts/README.md b/docs/concepts/README.md
index a3fc8499..c4b973e7 100644
--- a/docs/concepts/README.md
+++ b/docs/concepts/README.md
@@ -10,13 +10,13 @@ inner workings of the `Client`, and how to write extensions and addons, also kno
 
 Each page goes a little further in explaning a core concept of `urql`.
 
-- **Philosophy** gives a quick overview of the different aspects of GraphQL clients and `urql` in
+- [**Philosophy**](./philosophy.md) gives a quick overview of the different aspects of GraphQL clients and `urql` in
   particular, which shines a light on why you may want to use `urql`.
-- **Stream Pattern** explains the inner working of `urql`, which is _stream-based_, also known as
+- [**Stream Pattern**](./stream-patterns.md) explains the inner working of `urql`, which is _stream-based_, also known as
   Observable patterns in JS.
-- **Core Package** defines why a shared package exists that contains the main logic of `urql`, and
+- [**Core Package**](./core-package.md) defines why a shared package exists that contains the main logic of `urql`, and
   how we can use it directly in Node.js.
-- **Exchanges** finally introduces _Exchanges_ and how to write extensions or addons and use them
+- [**Exchanges**](./exchanges.md) finally introduces _Exchanges_ and how to write extensions or addons and use them
   in `urql`.
 
 Finally, some _Exchanges_ are covered in different sections of the documentation, like
diff --git a/packages/core/src/exchanges/cache.ts b/packages/core/src/exchanges/cache.ts
index 1c67dee2..fe5f0cf3 100755
--- a/packages/core/src/exchanges/cache.ts
+++ b/packages/core/src/exchanges/cache.ts
@@ -129,8 +129,12 @@ export const afterMutation = (
   client: Client
 ) => (response: OperationResult) => {
   const pendingOperations = new Set<number>();
+  const { additionalTypenames } = response.operation.context;
 
-  collectTypesFromResponse(response.data).forEach(typeName => {
+  [
+    ...collectTypesFromResponse(response.data),
+    ...(additionalTypenames || []),
+  ].forEach(typeName => {
     const operations =
       operationCache[typeName] || (operationCache[typeName] = new Set());
     operations.forEach(key => {
@@ -154,6 +158,7 @@ const afterQuery = (
   operationCache: OperationCache
 ) => (response: OperationResult) => {
   const { operation, data, error } = response;
+  const { additionalTypenames } = operation.context;
 
   if (data === undefined || data === null) {
     return;
@@ -161,7 +166,10 @@ const afterQuery = (
 
   resultCache.set(operation.key, { operation, data, error });
 
-  collectTypesFromResponse(response.data).forEach(typeName => {
+  [
+    ...collectTypesFromResponse(response.data),
+    ...(additionalTypenames || []),
+  ].forEach(typeName => {
     const operations =
       operationCache[typeName] || (operationCache[typeName] = new Set());
     operations.add(operation.key);
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 42abefe0..8bc087f2 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -44,6 +44,7 @@ export interface OperationDebugMeta {
 /** Additional metadata passed to [exchange]{@link Exchange} functions. */
 export interface OperationContext {
   [key: string]: any;
+  additionalTypenames?: string[];
   fetchOptions?: RequestInit | (() => RequestInit);
   requestPolicy: RequestPolicy;
   url: string;
diff --git a/packages/site/src/assets/anchor.js b/packages/site/src/assets/anchor.js
new file mode 100644
index 00000000..8ba5b4f5
--- /dev/null
+++ b/packages/site/src/assets/anchor.js
@@ -0,0 +1,13 @@
+import React from 'react';
+
+const SvgAnchor = props => (
+  <svg viewBox="0 0 16 16" {...props}>
+    <path
+      fill="currentColor"
+      fillRule="evenodd"
+      d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"
+    />
+  </svg>
+);
+
+export default SvgAnchor;
diff --git a/packages/site/src/assets/anchor.svg b/packages/site/src/assets/anchor.svg
new file mode 100644
index 00000000..5f8fc157
--- /dev/null
+++ b/packages/site/src/assets/anchor.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg viewBox="0 0 16 16" version="1.1">
+  <path fill="currentColor" fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path>
+</svg>
+
diff --git a/packages/site/src/components/mdx.js b/packages/site/src/components/mdx.js
index 0ec7e1e7..91a729f8 100644
--- a/packages/site/src/components/mdx.js
+++ b/packages/site/src/components/mdx.js
@@ -7,6 +7,7 @@ import { useMarkdownPage } from 'react-static-plugin-md-pages';
 import Highlight, { Prism } from 'prism-react-renderer';
 import nightOwlLight from 'prism-react-renderer/themes/nightOwlLight';
 
+import AnchorSvg from '../assets/anchor';
 import { relative } from './sidebar';
 
 const getLanguage = className => {
@@ -223,7 +224,51 @@ const MdLink = ({ href, children }) => {
     return <Link to={relative(from, to)}>{children}</Link>;
   }
 
-  return <a href={href}>{children}</a>;
+  return (
+    <a rel="external" href={href}>
+      {children}
+    </a>
+  );
+};
+
+const HeadingText = styled.h1`
+  &:target:before {
+    content: '';
+    display: block;
+    height: 1.5em;
+    margin: -1.5em 0 0;
+  }
+`;
+
+const AnchorLink = styled.a`
+  display: inline-block;
+  color: ${p => p.theme.colors.accent};
+  padding-right: 0.5rem;
+  width: 2rem;
+
+  @media ${({ theme }) => theme.media.sm} {
+    margin-left: -2rem;
+    display: none;
+
+    ${HeadingText}:hover > & {
+      display: inline-block;
+    }
+  }
+`;
+
+const AnchorIcon = styled(AnchorSvg)`
+  height: 100%;
+`;
+
+const Header = tag => ({ id, children }) => {
+  return (
+    <HeadingText as={tag} id={id}>
+      <AnchorLink href={`#${id}`}>
+        <AnchorIcon />
+      </AnchorLink>
+      {children}
+    </HeadingText>
+  );
 };
 
 const components = {
@@ -236,6 +281,9 @@ const components = {
   th: TableHeader,
   td: TableCell,
   a: MdLink,
+  h1: HeadingText,
+  h2: Header('h2'),
+  h3: Header('h3'),
 };
 
 export const MDXComponents = ({ children }) => (
diff --git a/packages/site/src/components/navigation.js b/packages/site/src/components/navigation.js
index 66ce9743..4864bada 100644
--- a/packages/site/src/components/navigation.js
+++ b/packages/site/src/components/navigation.js
@@ -6,6 +6,10 @@ import ChevronIcon from '../assets/chevron';
 export const SidebarContainer = styled.div`
   display: ${p => (p.hidden ? 'none' : 'block')};
   position: absolute;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  min-height: 100%;
 
   @media ${({ theme }) => theme.media.sm} {
     display: block;
@@ -73,7 +77,9 @@ export const SidebarNavItem = styled(NavLink).attrs(() => ({
   }
 `;
 
-export const ChevronItem = styled(ChevronIcon)`
+export const ChevronItem = styled(ChevronIcon).attrs(() => ({
+  'aria-hidden': 'true',
+}))`
   display: inline-block;
   color: inherit;
   vertical-align: baseline;
diff --git a/packages/site/src/components/sidebar.js b/packages/site/src/components/sidebar.js
index 5605afec..56ad63eb 100644
--- a/packages/site/src/components/sidebar.js
+++ b/packages/site/src/components/sidebar.js
@@ -59,14 +59,14 @@ export const relative = (from, to) => {
   return { pathname };
 };
 
-export const SidebarStyling = ({ children, sidebarOpen }) => {
+export const SidebarStyling = ({ children, sidebarOpen, closeSidebar }) => {
   const basepath = useBasepath() || '';
   const homepage = basepath ? `/${basepath}/` : '/';
 
   return (
     <>
       <SideBarStripes />
-      <SidebarContainer hidden={!sidebarOpen}>
+      <SidebarContainer hidden={!sidebarOpen} onClick={closeSidebar}>
         <SidebarWrapper>
           <HeroLogoLink to={homepage}>
             <HeroLogo />
diff --git a/packages/site/src/screens/docs/article.js b/packages/site/src/screens/docs/article.js
index 1f7bfaa0..40fcd282 100644
--- a/packages/site/src/screens/docs/article.js
+++ b/packages/site/src/screens/docs/article.js
@@ -21,7 +21,7 @@ const Content = styled.article.attrs(() => ({
   id: 'page-content',
 }))`
   flex: 1;
-  min-height: 100vh;
+  min-height: calc(100vh - ${p => p.theme.layout.header});
   background: ${p => p.theme.colors.bg};
   padding: ${p => p.theme.spacing.md};
 
diff --git a/packages/site/src/screens/docs/header.js b/packages/site/src/screens/docs/header.js
index 3579cf1d..628f103c 100644
--- a/packages/site/src/screens/docs/header.js
+++ b/packages/site/src/screens/docs/header.js
@@ -13,6 +13,9 @@ const Fixed = styled.header`
   width: 100%;
   z-index: 1;
 
+  box-sizing: border-box;
+  height: ${p => p.theme.layout.header};
+
   background: ${p => p.theme.colors.bg};
   border-bottom: 1px solid ${p => p.theme.colors.border};
   padding: 0 ${p => p.theme.spacing.md};
@@ -21,9 +24,9 @@ const Fixed = styled.header`
 
 const Wrapper = styled.div`
   width: 100%;
+  height: 100%;
   max-width: ${p => p.theme.layout.page};
   margin: 0 auto;
-  height: ${p => p.theme.layout.header};
   padding-top: 2px;
   display: flex;
   flex-direction: row;
diff --git a/packages/site/src/screens/docs/index.js b/packages/site/src/screens/docs/index.js
index 37e98d48..fa0293b0 100644
--- a/packages/site/src/screens/docs/index.js
+++ b/packages/site/src/screens/docs/index.js
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, useCallback } from 'react';
 import styled from 'styled-components';
 
 import Article, { ArticleStyling } from './article';
@@ -38,6 +38,10 @@ const OpenCloseSidebar = styled.img.attrs(props => ({
 const Docs = ({ isLoading, children }) => {
   const [sidebarOpen, setSidebarOpen] = useState(false);
 
+  const closeSidebar = useCallback(() => {
+    setSidebarOpen(false);
+  }, [setSidebarOpen]);
+
   return (
     <>
       <Header />
@@ -49,12 +53,15 @@ const Docs = ({ isLoading, children }) => {
         {/* load just the styles if Suspense fallback in use */}
         {isLoading ? (
           <>
-            <SidebarStyling sidebarOpen={sidebarOpen} />
+            <SidebarStyling
+              sidebarOpen={sidebarOpen}
+              closeSidebar={closeSidebar}
+            />
             <ArticleStyling>{children}</ArticleStyling>
           </>
         ) : (
           <>
-            <Sidebar sidebarOpen={sidebarOpen} />
+            <Sidebar sidebarOpen={sidebarOpen} closeSidebar={closeSidebar} />
             <Article>{children}</Article>
           </>
         )}


Test patch:
diff --git a/packages/core/src/exchanges/cache.test.ts b/packages/core/src/exchanges/cache.test.ts
index 003e8fcc..1257e944 100755
--- a/packages/core/src/exchanges/cache.test.ts
+++ b/packages/core/src/exchanges/cache.test.ts
@@ -197,6 +197,37 @@ describe('on mutation', () => {
 
     expect(reexecuteOperation).toBeCalledTimes(1);
   });
+
+  it('retriggers query operation with additionalTypenames', () => {
+    const typename = 'ExampleType';
+    const resultCache = new Map([[123, queryResponse]]);
+    const operationCache = { [`${typename}-2`]: new Set([123]) };
+
+    afterMutation(
+      resultCache,
+      operationCache,
+      exchangeArgs.client
+    )({
+      ...mutationResponse,
+      operation: {
+        ...mutationResponse.operation,
+        context: {
+          ...mutationResponse.operation.context,
+          additionalTypenames: [`${typename}-2`],
+        },
+      },
+      data: {
+        todos: [
+          {
+            id: 1,
+            __typename: typename,
+          },
+        ],
+      },
+    });
+
+    expect(reexecuteOperation).toBeCalledTimes(1);
+  });
 });
 
 describe('on subscription', () => {