Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Part 1: New documentation (#553)

* add basics docs section

* Remove old docs/core content

* Remove old index README

* Add initial structure and fix some styling

* Fix relative routing edge-case in sidebar

* Add image wrapper to MDX output

* Update font-size for code blocks

* Add style for blockquotes to MDX components

* Fix <pre> being applied again in CodeHighlight

* add titles

* elaborate on graphcache in the introduction

* fix lint errors

* Update docs/basics/mutating-data.md

Co-Authored-By: Andy Richardson <andy.john.richardson@gmail.com>

* Post-meeting changes

* corrections to the basics

* Move "Architecture" to "Concepts" and write "Philosophy"

* Move "Document Caching" docs to "Basics"

* WIP: Stream Patterns page

* Finalise Stream Patterns page

* make structure for graphcache

* update api/urql (react) docs

* Add "Core Package" doc to "Concepts"

* explain normalized-caching and keys

* tidy up core and add 'refer to React' into preact API section

* make graphcache chapters

* populate the content for the populateExchange

* correct some typo's on schema-awareness

* Move over and add to old Exchanges content

* Move Subscriptions doc over from legacy docs

* start on ssr

* Add new Introduction page

* Restore /docs path prefix for all documentation

Co-authored-by: Phil Plückthun <phil@kitten.sh>
Co-authored-by: Jovi De Croock <decroockjovi@gmail.com>
Co-authored-by: Will Golledge <35961363+wgolledge@users.noreply.github.com>

+1925 -2795
+1 -3
README.md
··· 12 12 <br /> 13 13 <a href="https://circleci.com/gh/FormidableLabs/urql"> 14 14 <img alt="Test Status" src="https://circleci.com/gh/FormidableLabs/urql.svg?style=shield" /> 15 - </a> 16 - <a href="https://github.com/FormidableLabs/urql#maintenance-status"> 15 + </a> <a href="https://github.com/FormidableLabs/urql#maintenance-status"> 17 16 <img alt="Maintenance Status" src="https://img.shields.io/badge/maintenance-active-green.svg" /> 18 17 </a> 19 18 <a href="https://spectrum.chat/urql"> ··· 202 201 single stream of inputs. As responses come back from the cache or your GraphQL API one or more results are 203 202 dispatched on an output stream that correspond to the operations, which update the hooks. 204 203 205 - <img width="709" src="docs/assets/urql-event-hub.png" alt="Diagram: The 'useQuery' hook dispatches an operation on the client when it mounts or updates. When it unmounts it dispatches a 'teardown' operation that cancels the original operation. Results that come back from the client update the hook and are filtered by the operation's original key."/> 206 204 207 205 Hence the client can be seen as an event hub. Operations are sent to the client, which executes them and 208 206 sends back a result. A special teardown-event is issued when a hook unmounts or updates to a different
+39 -39
docs/README.md
··· 1 1 --- 2 - title: README 3 - order: 1 2 + title: Introduction 3 + order: 0 4 4 --- 5 5 6 - # Documentation 6 + # Introduction 7 7 8 - `urql` is a highly customizable and flexible GraphQL client, that 9 - happens to come with some default core behavior and some React 10 - component and hooks APIs. 8 + `urql` is an implementation of a GraphQL client, built to be both easy to use for newcomers to 9 + GraphQL, as well as extensible, to grow to support dynamic single-app applications and highly 10 + customised GraphQL infrastructure. In short, `urql` prioritizes usability and adaptability. 11 11 12 - You can take `urql` from writing your first GraphQL app, to 13 - building a full experience, and all the way to extending it, 14 - making it suitable for your needs, and experimenting with custom 15 - GraphQL clients! 12 + As you're adopting GraphQL, `urql` becomes your primary data layer and can handle content-heavy 13 + pages through ["Document Caching"](./basics/document-caching.md) as well as dynamic and data-heavy 14 + pages through ["Normalized Caching"](./graphcache/normalized-caching.md). 16 15 17 - ### [Getting Started](core/getting-started.md) 16 + ## Constituent Parts 18 17 19 - Let's get up and running! This section explains how to 20 - install `urql` and use its React components and hooks. 21 - This contains everything you need to know to get started 22 - and just use the default `urql`! 23 - 24 - ### [Architecture](core/architecture.md) 18 + `urql` can be understood as a collection of connected parts and packages. When you [get 19 + started](./basics/setting-up-the-client.md) you'll only need to install a single package for your 20 + framework of choice. We're then able to declaratively send GraphQL requests to our API. 25 21 26 - A quick deep dive into `urql`'s structure, how the 27 - client works, and how requests get sent through the 28 - exchange pipeline to your GraphQL API. 29 - 30 - ### [Basics](core/basics.md) 31 - 32 - Everything about the basic & default `urql` behavior 33 - that comes with explanations of the `fetchExchange`, 34 - the `cacheExchange`, and how to use _Subscriptions_. 22 + All framework packages — like `urql` and `@urql/preact` — wrap the core package, which we can 23 + imagine as the brain of `urql` with most of its logic. 35 24 36 - ### [Exchanges](core/exchanges.md) 25 + As we progress with implementing `urql` into our application, we're later able to extend it by 26 + adding ["addon packages", which we call _Exchanges_](./concepts/exchanges.md) 37 27 38 - Here's everything you need to know to customize and augment 39 - every aspect of the GraphQL client from how data is cached 40 - to how components receive their data. 28 + ## The Documentation 41 29 42 - ### [Extending & Experimenting](core/extending-and-experimenting.md) 30 + This documentation is split into groups or sections that cover different levels of usage or areas of 31 + interest. 43 32 44 - Here's everything you need to know to extend, 45 - customise and experiment with `urql`. This section 46 - shows you how to use the `Client` to write new 47 - APIs (Be it for React or not) and how to write 48 - new "Exchanges" to customise `urql`'s core behavior! 33 + - **Basics** is the section where we find the ["Getting Started" 34 + guide](./basics/setting-up-the-client.md) and usage patterns for our framework of choice. 35 + - **Main Concepts** then explains more about how `urql` functions, what it's made up of, and covers 36 + the main aspects of the `Client` and GraphQL clients in general, on the ["Philosophy" 37 + page](./concepts/philosophy.md) 38 + - **Advanced** covers all more uncommon use-cases and contains guides that we won't need immediately 39 + when we get started with `urql`. 40 + - **Graphcache** documents one of the most important addons to `urql`, which adds ["Normalized 41 + Caching" support](./graphcache/normalized-caching.md) to the `Client` and enables more complex 42 + use-cases, smarter caching, and more dynamic apps to function. 43 + - **API** contains a detailed list of all helpers, utilities, components, and other parts of each of 44 + `urql`'s packages, which may contain all details of each part and package. 49 45 50 - ### [Guides](core/guides.md) 46 + Apart from these main sections there is also some additional content that doesn't fit into any of 47 + the other sections. 51 48 52 - Here are some guides on how to make common exchanges. 49 + - **Showcase** aims to list some companies that use `urql`, third-party packages, and other helpful 50 + resources. 51 + - **Common Questions** lists frequently asked questions and problems that we may encounter 52 + infrequently (but shall answer nonetheless.) 53 53 54 - ### [API](core/api.md) 54 + We hope you grow to love `urql`!
+6
docs/advanced/README.md
··· 1 + --- 2 + title: Advanced 3 + order: 3 4 + --- 5 + 6 + # Advanced
+50
docs/advanced/server-side-rendering.md
··· 1 + --- 2 + title: Server-side Rendering 3 + order: 2 4 + --- 5 + 6 + # Server-side Rendering 7 + 8 + `urql` supports server-side rendering through the `ssrExchange`, this exchange 9 + will gather all results that are fetched during the server side render and store 10 + them. This data can then be used on the client when the application is being 11 + hydrated. 12 + 13 + ## Setting up 14 + 15 + To start out with the `ssrExchange` we have to add the exchange 16 + 17 + ```js 18 + const isClient = typeof window !== 'undefined'; 19 + const ssr = ssrExchange({ 20 + initialData: isClient ? window.URQL_DATA : undefined, 21 + // This will need to be passed explicitly to ssrExchange: 22 + isClient: !!isClient 23 + }); 24 + 25 + const client = createClient({ 26 + exchanges: [ 27 + dedupExchange, 28 + cacheExchange, 29 + ssr, 30 + fetchExchange, 31 + ] 32 + }) 33 + ``` 34 + 35 + The `ssrExchange` allows you to pass in an object with two options, one being `isClient`, 36 + this option tells the exchange you're in the browser rather than in the process of a server-side 37 + render. The other option is called `initialState`, this being a mapping of `operationKey` to `operationResult`, 38 + this could for instance be `window.__URQL_DATA__`. 39 + 40 + The returned exchange will have two methods available on it, `restoreData` and `extractData`, during your ssr 41 + we extract the gathered data and serialize it into a `<script>` tag inside head, this should bind it to a unique 42 + property on `window` for instance `window.__URQL_DATA__`. 43 + 44 + When `isClient` is true it will use the `initialState` to restore the gathered data. 45 + 46 + ## Next 47 + 48 + We have a custom integration with [`Next.js`](https://nextjs.org/), being [`next-urql`](https://github.com/FormidableLabs/next-urql) 49 + this integration contains convenience methods specifically for `Next.js`. 50 + These will simplify the above setup for SSR.
+134
docs/advanced/subscriptions.md
··· 1 + --- 2 + title: Subscriptions 3 + order: 0 4 + --- 5 + 6 + # Subscriptions 7 + 8 + One feature of `urql` that was not mentioned in the ["Basics" section](../basics/README.md) is `urql`'s 9 + APIs and ability to handle GraphQL subscriptions. 10 + 11 + ## The Subscription Exchange 12 + 13 + To add support for subscriptions we need to add the `subscriptionExchange` to our `Client`. 14 + 15 + ```js 16 + import { Client, defaultExchanges, subscriptionExchange } from 'urql'; 17 + 18 + const client = new Client({ 19 + url: '/graphql', 20 + exchanges: [ 21 + ...defaultExchanges, 22 + subscriptionExchange({ 23 + forwardSubscription, 24 + }), 25 + ], 26 + }); 27 + ``` 28 + 29 + [Read more about _Exchanges_ and how they work on the "Exchanges" page.](../concepts/exchanges.md) 30 + 31 + In the above example, we add the `subscriptionExchange` to the `Client` with the default exchanges 32 + add before it. The `subscriptionExchange` is a factory that accepts additional options and returns 33 + the actual `Exchange` function. It does not make any assumption over the transport protocol and 34 + scheme that is used. Instead we need to pass a `forwardSubscription` function which is called with 35 + an "enriched" _Operation_ every time the `Client` attempts to execute a GraphQL Subscription. 36 + 37 + When we define this function it must return an "Observable-like" object, which needs to follow the 38 + [Observable spec](https://github.com/tc39/proposal-observable), which comes down to having an 39 + object with a `.subscribe()` method accepting an observer. 40 + 41 + ## Setting up `subscriptions-transport-ws` 42 + 43 + If your GraphQL API is using [the Apollo Server](https://www.apollographql.com/docs/apollo-server/), 44 + you'll be able to use [Apollo's `subscriptions-transport-ws` 45 + package](https://github.com/apollographql/subscriptions-transport-ws). 46 + 47 + ```js 48 + import { Client, defaultExchanges, subscriptionExchange } from 'urql'; 49 + import { SubscriptionClient } from 'subscriptions-transport-ws'; 50 + 51 + const subscriptionClient = new SubscriptionClient( 52 + 'wss://localhost/graphql', 53 + { reconnect: true } 54 + ); 55 + 56 + const client = new Client({ 57 + url: '/graphql', 58 + exchanges: [ 59 + ...defaultExchanges, 60 + subscriptionExchange({ 61 + forwardSubscription(operation) { 62 + return subscriptionClient.request(operation); 63 + }, 64 + }), 65 + ], 66 + }); 67 + ``` 68 + 69 + In this example, we're creating a `SubscriptionClient`, are passing in a URL and some parameters, 70 + and are using the `SubscriptionClient`'s `request` method to create a Subscription Observable, which 71 + we return to the `subscriptionExchange` inside `forwardSubscription`. 72 + 73 + [Read more about `subscription-transport-ws` on its README.](https://github.com/apollographql/subscriptions-transport-ws/blob/master/README.md) 74 + 75 + ## React & Preact 76 + 77 + The `useSubscription` hooks comes with a similar API to `useQuery`, which [we've learned about in 78 + the "Queries" page in the "Basics" section.](../basics/querying-data.md) 79 + 80 + Its usage is extremely similar in that it accepts options, which may contain `query` and 81 + `variables`. However, it also accepts a second argument, which is a reducer function, similar to 82 + what you would pass to `Array.prootype.reduce`. 83 + 84 + It receives the previous set of data that this function has returned or `undefined`. 85 + As the second argument, it receives the event that has come in from the subscription. 86 + You can use this to accumulate the data over time, which is useful for a 87 + list for example. 88 + 89 + In the following example, we create a subscription that informs us of 90 + new messages. We will concatenate the incoming messages, so that we 91 + can display all messages that have come in over the subscription across 92 + events. 93 + 94 + ```js 95 + import React from 'react'; 96 + import { useSubscription } from 'urql'; 97 + 98 + const newMessages = ` 99 + subscription MessageSub { 100 + newMessages { 101 + id 102 + from 103 + text 104 + } 105 + } 106 + `; 107 + 108 + const handleSubscription = (messages = [], response) => { 109 + return [response.newMessages, ...messages]; 110 + }; 111 + 112 + const Messages = () => { 113 + const [res] = useSubscription({ query: newMessages }, handleSubscription); 114 + 115 + if (!res.data) { 116 + return <p>No new messages</p>; 117 + } 118 + 119 + return ( 120 + <ul> 121 + {res.data.map(message => ( 122 + <p key={message.id}> 123 + {message.from}: "{message.text}" 124 + </p> 125 + ))} 126 + </ul> 127 + ); 128 + }; 129 + ``` 130 + 131 + As we can see, the `result.data` is being updated and transformed by 132 + the `handleSubscription` function. This works over time, so as 133 + new messages come in, we will append them to the list of previous 134 + messages.
+6
docs/advanced/testing.md
··· 1 + --- 2 + title: Testing 3 + order: 3 4 + --- 5 + 6 + # Testing
+8
docs/api/README.md
··· 1 + --- 2 + title: API 3 + order: 7 4 + --- 5 + 6 + # API 7 + 8 + <!-- List of packages and subpages -->
+328
docs/api/core.md
··· 1 + --- 2 + title: @urql/core 3 + order: 0 4 + --- 5 + 6 + # @urql/core 7 + 8 + ## The Client and related types 9 + 10 + ### Client (class) 11 + 12 + The client manages all operations and ongoing requests to the exchange pipeline. 13 + It accepts a bunch of inputs when it's created 14 + 15 + | Input | Type | Description | 16 + | ------------ | ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | 17 + | url | `string` | The GraphQL API URL as used by `fetchExchange` | 18 + | fetchOptions | `RequestInit \| () => RequestInit` | Additional `fetchOptions` that `fetch` in `fetchExchange` should use to make a request | 19 + | fetch | `typeof fetch` | An alternative implementation of `fetch` that will be used by the `fetchExchange` instead of `window.fetch` | 20 + | suspense | `?boolean` | Activates the experimental React suspense mode, which can be used during server-side rendering to prefetch data | 21 + | exchanges | `Exchange[]` | An array of `Exchange`s that the client should use instead of the list of `defaultExchanges` | 22 + 23 + `urql` also exposes `createClient()` that is just a convenient alternative to calling `new Client()`. 24 + 25 + #### .executeQuery(), .executeSubscription(), and .executeMutation() 26 + 27 + These methods are used by `<Query>` & `useQuery()`, `<Subscription>` & `useSubscription()`, 28 + and `<Mutation>` & `useMutation()` respectively. 29 + 30 + They accept a `GraphQLRequest` object as their first argument and optionally 31 + a partial `OperationContext` as their second. 32 + 33 + Internally they then create an `Operation` and call `.executeRequestOperation()` with 34 + the `Operation`. This then returns a `Source<OperationResult>`, i.e. a stream of 35 + `OperationResult`s. 36 + 37 + #### .query and .mutation 38 + 39 + These two methods accept a `query`, `variables` and a `context`, these two methods 40 + are really similar to the above in the sense that they return you a `Source<OperationResult>` 41 + you can subscribe to. The difference is that this returned value has a method on it called 42 + `toPromise`, when invoked it will convert the `Source` to a one-time promise. These methods 43 + are ideal for SSR, like for example the `getInitialProps` method in [Next.js](https://nextjs.org/). 44 + 45 + #### .executeRequestOperation() 46 + 47 + This method accepts an `Operation` and handles the flow of said `Operation`. Every `Operation` 48 + that is executed must pass through this method. 49 + 50 + It creates a filtered `Source<OperationResult>` that only contains the `OperationResult`s 51 + relevant to this `Operation` by filtering by the operation `key` and track the subscriptions 52 + to this `Source`. 53 + 54 + This is important as a cache exchange can call `reexecuteOperation` to inform the 55 + client about an invalidation. Whenever an operation needs to be updated with new 56 + network data, it's important to know whether any component is still interested in 57 + this operation. 58 + 59 + To track this, this method ensures that a mapping is updated that counts up 60 + for each subscription to the `Source` and counts down for each unsubscription. 61 + 62 + The `Operation` that has been passed to this method will be dispatched 63 + when the first subscription is started. When the last subscription unsubscribes 64 + from the returned source, this method will ensure that a `teardown` operation 65 + is dispatched. 66 + 67 + > _Note:_ This does not apply to mutations, which are one-off calls and 68 + > hence aren't shared, cancelled, or tracked in the cache. 69 + 70 + The return value is the filtered `Source<OperationResult>`. 71 + 72 + #### .reexecuteOperation() 73 + 74 + This method accepts an `Operation` and will dispatch this `Operation` if there 75 + are any subscriptions from `executeRequestOperation`'s `Source<OperationResult>` 76 + to this particular `Operation`. 77 + 78 + This is called by `cacheExchange` when an `Operation`'s `OperationResult` is 79 + invalidated in the cache. 80 + 81 + #### .createRequestOperation() 82 + 83 + This is called by the `executeQuery`, `executeSubscription` and `executeMutation` 84 + methods to create `Operation`s. It accepts: 85 + 86 + - `OperationType` 87 + - `GraphQLRequest` 88 + - and; the optional partial `OperationContext` (`Partial<OperationContext>`) 89 + 90 + It returns an `Operation`. 91 + 92 + #### .dispatchOperation() 93 + 94 + This method dispatches an `Operation` to the exchange pipeline. This is only 95 + used directly by the Client and shouldn't normally be called externally, except 96 + when the tracking logic of active `Operation`s needs to be bypassed. 97 + 98 + These `Operation`s are streamed from the `operations$: Source<Operation>` stream. 99 + The results of all exchanges are similarly output to `results$: Source<OperationResult>`. 100 + 101 + ### OperationType (type) 102 + 103 + This determines what _kind of operation_ the exchanges need to perform. 104 + This is one of: 105 + 106 + - `'subscription'` 107 + - `'query'` 108 + - `'mutation'` 109 + - `'teardown'` 110 + 111 + The `'teardown'` operation is special in that it instructs exchanges to cancel 112 + any ongoing operations with the same key as the `'teardown'` operation that is 113 + received. 114 + 115 + ### RequestPolicy (type) 116 + 117 + This determines the strategy that a cache exchange should use to fulfill an operation. 118 + When you implement a custom cache exchange it's recommended that these policies are 119 + handled. 120 + 121 + - `'cache-first'` (default) 122 + - `'cache-only'` 123 + - `'network-only'` 124 + - `'cache-and-network'` 125 + 126 + ### GraphQLRequest (type) 127 + 128 + This often comes up as the **input** for every GraphQL request. 129 + It consists of `query` and optional `variables`. 130 + 131 + | Prop | Type | Description | Required | | 132 + | --------- | -------------- | --------------------------------------------------------------------------------------------------------------------- | -------- | --- | 133 + | key | `number` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. | No | 134 + | query | `DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | Yes | 135 + | variables | `object` | The variables to be used with the GraphQL request. | No | 136 + 137 + &nbsp; 138 + 139 + The `key` property is a hash of both the `query` and the `variables`, to uniquely 140 + identify the request. 141 + 142 + ### OperationContext (type) 143 + 144 + This type is used to give an operation additional metadata and information. 145 + 146 + | Prop | Type | Description | Always Present | | 147 + | --------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | -------------- | --- | 148 + | fetchOptions | `RequestInit \| (() => RequestInit)` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. | No | 149 + | requestPolicy | `RequestPolicy` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. | Yes | 150 + | url | `string` | The GraphQL endpoint | Yes | 151 + | pollInterval | `number` | Every `pollInterval` milliseconds the query will be refetched. | No | 152 + | meta | `OperationDebugMeta` | Metadata that is only available in development for devtools. | No | 153 + | suspense | `boolean` | Whether suspense is enabled. | No | 154 + | preferGetMethod | `number` | Whether to use HTTP GET for queries. | No | 155 + 156 + &nbsp; 157 + 158 + It contains a lot of the above mentioned Client options and also `requestPolicy`. 159 + It accepts additional, untyped parameters that can be used to send more 160 + information to custom exchanges. 161 + 162 + ### Operation (type) 163 + 164 + The input for every exchange that informs GraphQL requests. 165 + It contains all properties in the [GraphQLRequest](#graphqlrequest-type) type, as well as the additional properties below. 166 + 167 + | Prop | Type | Description | Required | | 168 + | ------------- | ------------------ | --------------------------------------------- | -------- | --- | 169 + | operationName | `OperationType` | The type of GraphQL operation being executed. | Yes | 170 + | context | `OperationContext` | Additional metadata passed to exchange. | Yes | 171 + 172 + ### OperationResult (type) 173 + 174 + The result of every GraphQL request, i.e. an `Operation`. 175 + It's very similar to what comes back from a typical GraphQL API, but 176 + slightly enriched. 177 + 178 + | Prop | Type | Description | Always Present | 179 + | ---------- | --------------------- | ----------------------------------------------------- | -------------- | 180 + | operation | `Operation` | The operation that this is a result for | Yes | 181 + | data | Generic | Data returned by the specified query | No | 182 + | error | `CombinedError` | The query error | No | 183 + | extensions | `Record<string, any>` | Extensions that the GraphQL server may have returned. | No | 184 + 185 + ### CombinedError (class) 186 + 187 + | Input | Type | Description | Required | 188 + | ------------- | ------------------------------- | --------------------------------------------------------------------------------- | -------- | 189 + | networkError | `Error` | An unexpected error that might've occured when trying to send the GraphQL request | No | 190 + | graphQLErrors | `Array<string \| GraphQLError>` | GraphQL Errors (if any) that were returned by the GraphQL API | No | 191 + | response | `any` | The raw response object (if any) from the `fetch` call | No | 192 + 193 + These are both inputs and properties on the `CombinedError`. Additionally it exposes a default `message` 194 + that combines all errors it has received. 195 + 196 + This is on every `OperationResult` that has one or more errors and groups the usual `errors` property 197 + that a GraphQL result might have normally. 198 + 199 + ## Exchanges and their utilities 200 + 201 + ### ExchangeInput (type) 202 + 203 + | Input | Type | Description | Required | 204 + | ------- | ------------ | ----------------------------------------------------------------------------------------------------------------------- | -------- | 205 + | forward | `ExchangeIO` | The unction responsible for receiving an observable operation and returning a result | Yes | 206 + | client | `Client` | The URQL application-wide client library. Each execute method starts a GraphQL request and returns a stream of results. | Yes | 207 + 208 + ### ExchangeIO (type) 209 + 210 + A function that receives a stream of operations and must return a stream 211 + of results. 212 + 213 + ```js 214 + type ExchangeIO = (Source<Operation>) => Source<OperationResult>; 215 + ``` 216 + 217 + ### Exchange (type) 218 + 219 + Similar to `redux-observable`'s epics, kind of related to Apollo's links, 220 + also somehow similar to Express' middleware. 221 + 222 + ```js 223 + type Exchange = ExchangeInput => ExchangeIO; 224 + ``` 225 + 226 + This works since every exchange receives `forward` with the `ExchangeInput`. 227 + Exchanges can therefore be chained. They can alter and filter `Operation`s 228 + that go into the next exchange, and they can alter, filter, or return 229 + `OperationResult`s that are returned. 230 + 231 + ### composeExchanges (function) 232 + 233 + This utility accepts multiple exchanges and composes them into a single one. 234 + It chains them in the order that they're given, left to right. 235 + 236 + ```js 237 + function composeExchanges(Exchange[]): Exchange; 238 + ``` 239 + 240 + This can be used to combine some exchanges and is also used by `Client` 241 + to handle the `exchanges` input. 242 + 243 + ### cacheExchange (Exchange) 244 + 245 + The `cacheExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#cacheexchange). 246 + It's of type `Exchange`. 247 + 248 + ### subscriptionExchange (Exchange factory) 249 + 250 + The `subscriptionExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#subscriptions). 251 + It's of type `Options => Exchange`. 252 + 253 + It accepts a single input: `{ forwardSubscription }`. This is a function that 254 + receives an enriched operation and must return an Observable-like object that 255 + streams `GraphQLResult`s with `data` and `errors`. 256 + 257 + ### ssrExchange (Exchange factory) 258 + 259 + The `ssrExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#server-side-rendering). 260 + It's of type `Options => Exchange`. 261 + 262 + It accepts two inputs, `initialState` which is completely 263 + optional and populates the server-side rendered data with 264 + a rehydrated cache, and `isClient` which can be set to 265 + `true` or `false` to tell the `ssrExchange` whether to 266 + write to (server-side) or read from (client-side) the cache. 267 + 268 + By default `isClient` defaults to `true` when the `Client.suspense` 269 + mode is disabled and to `false` when the `Client.suspense` mode 270 + is enabled. 271 + 272 + This can be used to extract data that has been queried on 273 + the server-side, which is also described in the Basics section, 274 + and is also used on the client-side to restore server-side 275 + rendered data. 276 + 277 + When called, this function creates an `Exchange`, which also has 278 + two methods on it: 279 + 280 + - `.restoreData(data)` which can be used to inject data, typically 281 + on the client-side. 282 + - `.extractData()` which is typically used on the server-side to 283 + extract the server-side rendered data. 284 + 285 + Basically, the `ssrExchange` is a small cache that collects data 286 + during the server-side rendering pass, and allows you to populate 287 + the cache on the client-side with the same data. 288 + 289 + During React rehydration this cache will be emptied and it will 290 + become inactive and won't change the results of queries after 291 + rehydration. 292 + 293 + It needs to be used _after_ other caching Exchanges like the 294 + `cacheExchange`, but before any _asynchronous_ Exchange like 295 + the `fetchExchange`. 296 + 297 + ### debugExchange (Exchange) 298 + 299 + An exchange that writes incoming `Operation`s to `console.log` and 300 + writes completed `OperationResult`s to `console.log`. 301 + 302 + ### dedupExchange (Exchange) 303 + 304 + An exchange that keeps track of ongoing `Operation`s that haven't returned had 305 + a corresponding `OperationResult` yet. Any duplicate `Operation` that it 306 + receives is filtered out if the same `Operation` has already been received 307 + and is still waiting for a result. 308 + 309 + ### fallbackExchangeIO (ExchangeIO) 310 + 311 + This is an `ExchangeIO` function that the `Client` adds on after all 312 + exchanges. This function is responsible from filtering `teardown` operations 313 + out of the output and also warns you of unhandled `operationName`s which 314 + can occur when a subscription is used without adding a `subscriptionExchange`. 315 + 316 + ### fetchExchange (Exchange) 317 + 318 + The `fetchExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#fetchexchange). 319 + It's of type `Exchange`. 320 + 321 + ### defaultExchanges (Exchange[]) 322 + 323 + An array of the default exchanges that the `Client` uses when it wasn't passed 324 + an `exchanges` option. 325 + 326 + ```js 327 + const defaultExchanges = [dedupExchange, cacheExchange, fetchExchange]; 328 + ```
+7
docs/api/graphcache.md
··· 1 + --- 2 + title: @urql/exchange-graphcache 3 + order: 3 4 + --- 5 + 6 + # @urql/exchange-graphcache 7 +
+8
docs/api/preact.md
··· 1 + --- 2 + title: @urql/preact 3 + order: 2 4 + --- 5 + 6 + # @urql/preact 7 + 8 + Please refer to [the React API section](/api/urql) for details of the Preact API.
+177
docs/api/urql.md
··· 1 + --- 2 + title: urql (React) 3 + order: 1 4 + --- 5 + 6 + # React API 7 + 8 + ## Hooks 9 + 10 + ### useQuery 11 + 12 + #### useQuery Parameters 13 + Accepts a single required `options` object as an input with the following properties: 14 + 15 + | Prop | Type | Description | Required | | 16 + | ------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------- | -------- | 17 + | query | `string \| DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | Yes | 18 + | variables | `object` | The variables to be used with the GraphQL request. | No | 19 + | requestPolicy | `RequestPolicy` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. | No | 20 + | pause | `boolean` | A boolean flag instructing `Query` to pause execution of the subsequent query operation. | No | 21 + | pollInterval | `number` | Every `pollInterval` milliseconds the query will be refetched. | No | 22 + | context | `object` | Holds the contextual information for the query. | No | 23 + 24 + #### useQuery Returned Data 25 + 26 + A tuple is returned with item one being the current query's state object and item two being an `executeQuery` function. 27 + 28 + The shape of the current state is an [OperationResult Type](/api/core#operationresult-type) 29 + 30 + &nbsp; 31 + 32 + The `executeQuery` function optionally accepts a partial `OperationContext`. 33 + 34 + [More information on how to use this hook can be found in the Basics section.](/basics/querying-data#queries) 35 + 36 + ### useMutation 37 + 38 + #### useMutation Parameters 39 + 40 + Accepts a single `query` argument of type `string`. 41 + 42 + #### useMutation Returned Data 43 + 44 + A tuple is returned with item one being the current query's state object and item two being an `executeQuery` function. 45 + 46 + The shape of the current state is an [OperationResult Type](/api/core#operationresult-type) 47 + &nbsp; 48 + 49 + The `executeQuery` function optionally accepts a partial `OperationContext`. 50 + 51 + [More information on how to use this hook can be found in the Basics section.](/basics/mutating-data#mutations) 52 + 53 + ### useSubscription 54 + 55 + #### useSubscription Parameters 56 + Accepts an `options` object as the required first parameter, and a second optional parameter that is the subscription's handler function. 57 + 58 + The `options` object's property breakdown: 59 + 60 + | Prop | Type | Description | Required | | 61 + | --------- | ------------------------ | ---------------------------------------------------------------------------------- | -------- | 62 + | query | `string \| DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | Yes | 63 + | variables | `object` | The variables to be used with the GraphQL request. | No | 64 + | context | `object` | Holds the contextual information for the query. | No | 65 + 66 + &nbsp; 67 + 68 + The subscription handler's type signature: 69 + 70 + ```js 71 + type SubscriptionHandler<T, R> = (prev: R | undefined, data: T) => R; 72 + ``` 73 + 74 + This means that the subscription handler receives the previous data or undefined 75 + and the current, incoming subscription event data. 76 + 77 + #### useSubscription Returned Data 78 + 79 + The shape of the current state is an [OperationResult Type](/api/core#operationresult-type) without the first `operation` prop. 80 + 81 + More information can be found in the [Subscriptions](/advanced/subscriptions) section. 82 + 83 + ## Components 84 + 85 + ### Query 86 + 87 + #### Props 88 + 89 + | Prop | Type | Description | Required | 90 + | ------------- | -------------------------- | ----------------------------------------------------------------------------------------------------- | -------- | 91 + | query | `string` | The GraphQL request's query | Yes | 92 + | variables | `object` | The GraphQL request's variables | Yes | 93 + | context | `?object` | The GraphQL request's context | No | 94 + | requestPolicy | `?RequestPolicy` | An optional request policy that should be used | No | 95 + | pause | `?boolean` | A boolean flag instructing `Query` to pause execution of the subsequent query operation | No | 96 + | pollInterval | `?number` | Every `pollInterval` milliseconds the query will be refetched | No | 97 + | children | `RenderProps => ReactNode` | A function that follows the typical render props pattern. The shape of the render props is as follows | N/A | 98 + 99 + #### Render Props 100 + 101 + | Prop | Type | Description | 102 + | ------------ | ----------------------------------- | ------------------------------------------------------------------------------------------------------- | 103 + | fetching | `boolean` | Whether the `Query` is currently waiting for a GraphQL result | 104 + | data | `?any` | The GraphQL request's result | 105 + | error | `?CombinedError` | The `CombinedError` containing any errors that might've occured | 106 + | extensions | `?Record<string, any>` | Optional extensions that the GraphQL server may have returned. | 107 + | executeQuery | `Partial<OperationContext> => void` | A function that can force the operation to be sent again with the given context (Useful for refetching) | 108 + 109 + &nbsp; 110 + 111 + [More information on how to use this hook can be found in the Basics section.](/basics/querying-data#queries) 112 + 113 + ### Mutation 114 + 115 + #### Props 116 + 117 + | Prop | Type | Description | 118 + | -------- | -------------------------- | ----------------------------------------------------------------------------------------------------- | 119 + | query | `string` | The GraphQL request's query | 120 + | children | `RenderProps => ReactNode` | A function that follows the typical render props pattern. The shape of the render props is as follows | 121 + 122 + #### Render Props 123 + 124 + | Prop | Type | Description | 125 + | --------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- | 126 + | fetching | `boolean` | Whether the `Mutation` is currently waiting for a GraphQL result | 127 + | data | `?any` | The GraphQL request's result | 128 + | error | `?CombinedError` | The `CombinedError` containing any errors that might've occured | 129 + | extensions | `?Record<string, any>` | Optional extensions that the GraphQL server may have returned. | 130 + | executeMutation | `(variables: object, context?: Partial<OperationContext>) => void` | A function that accepts variables and starts the mutation | 131 + 132 + [More information on how to use this hook can be found in the Basics section.](/basics/mutating-data#mutations) 133 + 134 + ### Subscription 135 + 136 + [More information on how to use this component can be found in the Basics section.](https://formidable.com/open-source/urql/docs/basics#subscriptions) 137 + 138 + #### Props 139 + 140 + | Prop | Type | Description | 141 + | --------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | 142 + | query | `string` | The GraphQL subscription's query | 143 + | variables | `object` | The GraphQL subscriptions' variables | 144 + | context | `?Partial<OperationContext>` | The GraphQL subscriptions' context | 145 + | handler | `undefined \| (prev: R \| undefined, data: T) => R` | The handler that should combine/update the subscription's data with incoming data | 146 + | children | `RenderProps => ReactNode` | A function that follows the typical render props pattern. The shape of the render props is as follows | 147 + 148 + #### Render Props 149 + 150 + | Prop | Type | Description | 151 + | ---------- | ---------------------- | --------------------------------------------------------------- | 152 + | fetching | `boolean` | Whether the `Subscription` is currently ongoing | 153 + | data | `?any` | The GraphQL subscription's data | 154 + | error | `?CombinedError` | The `CombinedError` containing any errors that might've occured | 155 + | extensions | `?Record<string, any>` | Optional extensions that the GraphQL server may have returned. | 156 + 157 + More information can be found in the [Subscriptions](/advanced/subscriptions) section. 158 + 159 + ### Context 160 + 161 + `urql` comes with the two context components `Consumer` and `Provider` as returned 162 + by React's `createContext` utility. It also exports the `Context` itself which can 163 + be used in combination with the `useContext` hook. 164 + 165 + E.g. 166 + 167 + ```js 168 + <App> 169 + <UrqlProvider> 170 + <UrqlConsumer> 171 + {urqlData => ( 172 + <MyComponent data={urqlData} /> 173 + )} 174 + </UrqlConsumer> 175 + </UrqlProvider> 176 + </App> 177 + ```
+12
docs/basics/README.md
··· 1 + --- 2 + title: Basics 3 + order: 1 4 + --- 5 + 6 + # Basics 7 + 8 + In this chapter we'll explain the basics of `urql`, you'll learn 9 + [how to set up your client](./setting-up-the-client.md). You'll 10 + see how you [query data](./querying-data.md) and find out how we 11 + can alter that data by [mutating it](./mutating-data.md). 12 +
+44
docs/basics/document-caching.md
··· 1 + --- 2 + title: Document Caching 3 + order: 3 4 + --- 5 + 6 + # Document Caching 7 + 8 + By default `urql` uses a concept called _Document Caching_. It will avoid sending the same requests 9 + to a GraphQL API repeatedly by caching the result of each query. 10 + 11 + This works like the cache in a browser. `urql` creates a key for each request that is sent based on 12 + a query and its variables. 13 + 14 + ## Operation Keys 15 + 16 + ![Keys for GraphQL Requests](../assets/urql-operation-keys.png) 17 + 18 + Once a result comes in it's cached indefinitely by their key. This means that each unique request 19 + can have exactly one cached result. 20 + 21 + However, we also need to invalidate the cache result so new requests for the same data can be 22 + sent when we know that some results are outdated. A result may be outdated because a mutation has 23 + been executed on data that has been queried previously. 24 + 25 + In GraphQL the client can request additional type information on a query by adding the `__typename` 26 + field. This field return the name of the type of a piece of data in a requests, and we use it 27 + to detect commonalities and data dependencies between queries and mutations. 28 + 29 + ![Document Caching](../assets/urql-document-caching.png) 30 + 31 + When we send a mutation that contains types that another query's results contains as well, that 32 + query's result is removed from the cache. 33 + 34 + This is an aggressive form of cache invalidation. However, it works well for content-driven sites, 35 + although it doesn't deal with normalized data or IDs. 36 + 37 + ## Document Cache Gotchas 38 + 39 + This cache has a small trade-off! If we request a list of data and the API returns an empty list, 40 + the cache won't be able to see the `__typename` of said list and won't invalidate. 41 + 42 + Once you've encountered this problem you've likely hit the limits of the _Document Caching_ 43 + approach, and you may want to [switch to "Normalized Caching" 44 + instead.](../graphcache/normalized-caching.md)
+61
docs/basics/mutating-data.md
··· 1 + --- 2 + title: Mutations 3 + order: 2 4 + --- 5 + 6 + # Mutations 7 + 8 + Now that we know how to query our data we'll also need to know 9 + how to mutate that data. 10 + We'll see how we can dispatch mutations to our back-end and view 11 + the result of these mutations. 12 + 13 + ## React/Preact 14 + 15 + `urql` exposes the `useMutation` hook and the `Mutation` component to send out mutations. 16 + 17 + ### Sending a mutation 18 + 19 + Let's set up a mutation allowing us to change the name of our todo. 20 + 21 + ```jsx 22 + const Todo = ({ id, title }) => { 23 + const [updateTodoResult, updateTodo] = useMutation(` 24 + mutation ($id: ID!, $title: String!) { 25 + updateTodo (id: $id, title: $title) { 26 + id 27 + } 28 + } 29 + `); 30 + } 31 + ``` 32 + 33 + Similar to the `useQuery` output, `useMutation` returns a tuple. The first item in the tuple being our `result` 34 + containing: `fetching`, `error`, and `data`. At this point in time, no mutation has been performed. 35 + To mutate the data we first have to invoke the second item in the tuple - the function here named `updateTodo`. 36 + 37 + ### Using the mutation result 38 + 39 + When calling this `updateTodo` function we have two ways of getting the response from the server, 40 + we can get it from `updateTodoResult` or we can await the promise returned from our mutation trigger function. 41 + 42 + ```jsx 43 + const Todo = ({ id, title }) => { 44 + const [updateTodoResult, updateTodo] = useMutation(` 45 + mutation ($id: ID!, $title: String!) { 46 + updateTodo (id: $id, title: $title) { 47 + id 48 + } 49 + } 50 + `); 51 + 52 + const submit = (newTitle) => { 53 + updateTodo({ variables: { id, title: newTitle } }).then((data) => { 54 + // this data variable will be the same as updateTodoResult.data 55 + }); 56 + } 57 + } 58 + ``` 59 + 60 + This means that we can react to a completed todo in the body of the `.then` or 61 + with a `useEffect`.
+107
docs/basics/querying-data.md
··· 1 + --- 2 + title: Queries 3 + order: 1 4 + --- 5 + 6 + # Queries 7 + 8 + Let's get to querying our data! This section will teach us how we can 9 + retrieve our data from the server with the help of `urql`. 10 + 11 + ## React/Preact 12 + 13 + Let's get to querying our first piece of data, we offer both a 14 + `render-props` component named `Query` and a hook named `useQuery` as 15 + a means to query data. 16 + 17 + The examples will show the hooks-version but it will be the same for the component. 18 + 19 + ### Run your first query 20 + 21 + For the following examples imagine we are querying a server offering us todo's, let's 22 + dive right into it! 23 + 24 + ```jsx 25 + const Todos = () => { 26 + const [{ data, fetching, error }, reexecuteFetch] = useQuery({ 27 + query: ` 28 + query { 29 + todos { 30 + id 31 + title 32 + } 33 + } 34 + `, 35 + }); 36 + 37 + if (fetching) return <p>Loading...</p> 38 + if (error) return <p>Oh no... {error.message}</p> 39 + 40 + return ( 41 + <ul> 42 + {data.todos.map(todo => ( 43 + <li key={todo.id}> 44 + {todo.title} 45 + </li> 46 + ))} 47 + </ul> 48 + ); 49 + } 50 + ``` 51 + 52 + ### Variables 53 + 54 + We have fetched our first set of todos. We can see the `useQuery` hook returns a tuple, 55 + the first being the result indicating whether it's fetching, it has errored and the result. 56 + The second can be used to refetch the query forcefully. 57 + 58 + What if we are dealing with pagination? We'd need a way to pass that right? 59 + We have the `variables` property to supply the variables to our query. 60 + 61 + ```jsx 62 + const Todos = ({ from, limit }) => { 63 + const [{ data, fetching, error }, reexecuteFetch] = useQuery({ 64 + query: ` 65 + query ($from: Int!, $limit: Int!) { 66 + todos (from: $from, limit: $limit) { 67 + id 68 + title 69 + } 70 + } 71 + `, 72 + variables: { from, limit }, 73 + }); 74 + ... 75 + } 76 + ``` 77 + 78 + ### Skipping queries 79 + 80 + As you can see we are enforcing `from` and `limit` as mandatory (notice the "!" after the `Int` type) 81 + this means that if we don't supply them this query will fail, we need some way of pausing this query 82 + when we don't have these. We can do exactly this by means of the `skip` property. 83 + 84 + ```jsx 85 + const Todos = ({ from, limit }) => { 86 + const [{ data, fetching, error }, reexecuteFetch] = useQuery({ 87 + ... 88 + skip: (!from || !limit) 89 + }); 90 + ... 91 + } 92 + ``` 93 + 94 + Now whenever one of these two mandatory variables isn't supplied the query won't be executed. 95 + 96 + ### Request policy 97 + 98 + We're almost there, there's one last thing we should touch on and that's the `requestPolicy`, 99 + this property tells the client how you want to get the result for your query, there are four values: 100 + 101 + - `cache-first` (default), this means we want to first look in our cache and see if the result is there, if not 102 + we fetch it and update our cache with the result. 103 + - `cache-and-network`, here we'll go to the cache and see if there's a result if there's not we fetch it, if there 104 + is a result we return it to the `query` and dispatch another operation to refresh this data. 105 + - `network-only`, this policy bypasses the cache and will just query your server. 106 + - `cache-only`, here we'll look for your data in the cache, if it's there you'll get the data returned, if not 107 + there will be a `null` return.
+81
docs/basics/setting-up-the-client.md
··· 1 + --- 2 + title: Getting started 3 + order: 0 4 + --- 5 + 6 + # Getting started 7 + 8 + ## React/Preact 9 + 10 + ### Installation 11 + 12 + Installing `urql` is as quick as you'd expect. Firstly, install it 13 + with your package manager of choice, Note: this installation is specific for React: 14 + 15 + ```sh 16 + yarn add urql graphql 17 + # or 18 + npm install --save urql graphql 19 + ``` 20 + 21 + To use urql with Preact, you have to install `@urql/preact` instead of urql and import from 22 + that package instead. 23 + 24 + > _Note:_ Most libraries related to GraphQL specify `graphql` as their peer 25 + > dependency so that they can adapt to your specific versioning 26 + > requirements. 27 + > The library is updated frequently and remains very backwards compatible, 28 + > but make sure it will work with other GraphQL tooling you might have installed. 29 + 30 + ### Setting up the client 31 + 32 + The package will export a method called `createClient` we can use this to create the 33 + client that will be used to dispatch our queries, mutations, etc. 34 + ```js 35 + import { createClient } from 'urql'; 36 + 37 + const client = createClient({ 38 + url: 'http://localhost:3000/graphql', 39 + }); 40 + ``` 41 + 42 + This is the bare minimum you need to get started with your client. 43 + 44 + One option you will most likely need in most applications is the `fetchOptions`, 45 + this option allows you to customize the `fetch` request sent to the given url. 46 + 47 + This is a function or an object, in the following example we tell our client a token 48 + should be added whenever it's present. 49 + 50 + ```js 51 + const client = createClient({ 52 + url: 'http://localhost:3000/graphql', 53 + fetchOptions: () => { 54 + const token = getToken(); 55 + return { 56 + headers: { authorization: token ? `Bearer ${token}` : '' }, 57 + }; 58 + }, 59 + }); 60 + ``` 61 + 62 + ### Providing the client 63 + 64 + To make use of this client in (P)React we will have to provide the client through 65 + the context API. This is done with the help of the `Provider` export. 66 + 67 + ```jsx 68 + import { createClient, Provider } from 'urql'; 69 + 70 + const client = createClient({ 71 + url: 'http://localhost:3000/graphql', 72 + }); 73 + 74 + const App = () => ( 75 + <Provider value={client}> 76 + <YourRoutes /> 77 + </Provider> 78 + ); 79 + ``` 80 + 81 + Now all the children of `<App />` will have access to the client we declared.
+6
docs/common-questions.md
··· 1 + --- 2 + title: Common Questions 3 + order: 6 4 + --- 5 + 6 + # Common Questions
+6
docs/concepts/README.md
··· 1 + --- 2 + title: Main Concepts 3 + order: 2 4 + --- 5 + 6 + # Main Concepts
+108
docs/concepts/core-package.md
··· 1 + --- 2 + title: Core Package 3 + order: 2 4 + --- 5 + 6 + # The Core Package 7 + 8 + Previously, [the "Philosophy" page](./philosophy.md) explained how `urql` solves different aspects 9 + of having a GraphQL client handle declarative querying and being a central point of extensibiliy. 10 + 11 + By extension there are three parts of `urql` you'll come in contact with when you add it to your 12 + app: 13 + 14 + <!-- TODO: Add more package links --> 15 + 16 + - the framework integration that allows you to declaratively write queries and mutations in your 17 + preferred framework, which are currently the `urql` or `@urql/preact` packages 18 + - the `Client` that manages the operation lifecycle and results 19 + - and; exchanges that may either be some default exchanges or some from external packages 20 + 21 + On this page we'll learn about the latter two points, which is shared logic that isn't specific to 22 + any framework, like React code or Preact code. 23 + 24 + [We'll learn more about _Exchanges_ on the next page.](./exchanges.md) 25 + 26 + ## Contents of Core 27 + 28 + The `@urql/core` package contains `urql`'s `Client`, some common utilities, and some default 29 + _Exchanges_. These are the shared, default parts of `urql` that we will be using no matter which 30 + framework we're interacting with. 31 + 32 + Therefore those are also the parts of `urql` that contain its most important logic — like the 33 + `Client` — and the package that we need to know about if we're either integrating `urql` with a new 34 + framework, or if we're using the "raw" `Client` in Node.js. 35 + 36 + ## Usage with Node.js 37 + 38 + The largest part of `urql` itself and the core package is the aforementioned `Client`. It's often 39 + used directly if you're just using `urql` in Node.js without any other integration. 40 + 41 + [We've previously seen how we can use the `Client`'s stream methods directly, in "Stream 42 + Patterns".](./stream-patterns.md) However, the `Client` also has plenty of convenience methods that 43 + make interacting with the `Client` directly a lot easier. 44 + 45 + ### One-off Queries and Mutations 46 + 47 + When you're using `urql` to send one-off queries or mutations — rather than in full framework code, 48 + where updates are important — it's common to convert the streams that we get to promises. The 49 + `client.query` and `client.mutation` methods have a shortcut to do just that. 50 + 51 + ```js 52 + const QUERY = ` 53 + query Test($id: ID!) { 54 + getUser(id: $id) { 55 + id 56 + name 57 + } 58 + } 59 + `; 60 + 61 + client.query(QUERY, { id: 'test' }) 62 + .toPromise() 63 + .then(result => { 64 + console.log(result); // { data: ... } 65 + }); 66 + ``` 67 + 68 + This may be useful when we don't plan on cancelling queries or we don't care about future updates to 69 + this data and are just looking to query a result once. 70 + 71 + Similarly there's a way to read data from the cache synchronously, provided that the cache has 72 + received a result for a given query before. The `Client` has a `readQuery` method which is a 73 + shortcut for just that. 74 + 75 + ```js 76 + const QUERY = ` 77 + query Test($id: ID!) { 78 + getUser(id: $id) { 79 + id 80 + name 81 + } 82 + } 83 + `; 84 + 85 + const result = client.readQuery(QUERY, { id: 'test' }); 86 + 87 + result; // null or { data: ... } 88 + ``` 89 + 90 + Since the streams in `urql` operate synchronously, internally this method subscribes to 91 + `client.executeQuery` but unsubscribes immediately. If a result is available in the cache it will be 92 + resolved synchronosuly before we immediately unsubscribe, otherwise this will cause no request to be 93 + sent to the GraphQL API. 94 + 95 + ## Common Utilities in Core 96 + 97 + The `@urql/core` package contains other utilities that are shared between multiple addon packages. 98 + This is a short but non-exhaustive list. It contains, 99 + 100 + <!-- TODO: Add links to other docs pages where appropriate --> 101 + 102 + - `CombinedError`, our abstraction to combine `GraphQLError`s and a `NetworkError` 103 + - `makeResult` and `makeErrorResult`, utilities to create _Operation Results_ 104 + - `createRequest`, a utility function to create a request from a query and some variables (which 105 + generates a stable _Operation Key_) 106 + 107 + There are more utilities. [Read more about the `@urql/core` API in the API docs for 108 + it.](../api/core.md)
+266
docs/concepts/exchanges.md
··· 1 + --- 2 + title: Exchanges 3 + order: 3 4 + --- 5 + 6 + # Exchanges 7 + 8 + As we've learned [on the "Stream Patterns" page](./stream-patterns.md), `urql`'s `Client` structures 9 + its data as an event hub. We have an input stream of operations, which are instructions for the 10 + `Client` to provide a result. These results come from an output stream of operation results. 11 + 12 + The important part how we get from the operations stream to the results stream, which is the 13 + responsibility of _Exchanges_. _Exchanges_ are handler functions that deal with these input and 14 + output streams. They're the main construct of `urql` to implement every piece of logic, like 15 + caching, fetching, deduplicating requests, and more. In other words, _Exchanges_ are handlers that 16 + fulfill our GraphQL requests and can change the stream of operations or results. 17 + 18 + The default exchanges that `@urql/core` contains and applies by default to a `Client` without custom 19 + exchanges are the: 20 + 21 + - `dedupExchange`: Deduplicates pending operations (pending = waiting for a result) 22 + - `cacheExchange`: The default caching logic with ["Document Caching"](../basics/document-caching.md) 23 + - `fetchExchange`: Sends an operation to the API using `fetch` and adds results to the output stream 24 + 25 + ## An Exchange Signature 26 + 27 + Because of how _Exchanges_ work they're akin to [middleware in 28 + Redux](https://redux.js.org/advanced/middleware). 29 + 30 + ```ts 31 + import { Client, Operation, OperationResult } from '@urql/core'; 32 + 33 + type ExchangeInput = { forward: ExchangeIO, client: Client }; 34 + type Exchange = (input: ExchangeInput) => ExchangeIO; 35 + type ExchangeIO = (ops$: Source<Operation>) => Source<OperationResult>; 36 + ``` 37 + 38 + Their signature is a function that receives a `forward` function, which is the next _Exchange_ in a 39 + chain of them and returns the `ExchangeIO` function, which accepts the source of _Operations_ and 40 + returns a source of _Operation Results_: 41 + 42 + ## Using Exchanges 43 + 44 + The `Client` accepts an `exchanges` option which is by default the list of default exchanges, as 45 + discussed above. When we pass a custom list of exchanges the `Client` uses the `composeExchanges` 46 + utiliy, which starts chaining these exchanges. 47 + 48 + In essence these exchanges build a pipeline that runs in the order they're passed; _Operations_ flow 49 + in from the start to the end, and _Results_ come back in reverse, through this chain. 50 + 51 + If we look at our list of default exchanges — `dedupExchange`, `cacheExchange`, and then 52 + `fetchExchange` — an incoming operation is treated as follows: 53 + 54 + **First,** ongoing operations are deduplicated. It wouldn't make sense to send the 55 + same operation / request twice at the same time. 56 + 57 + **Second,** operations are checked against the cache. Depending on the `requestPolicy` 58 + cached results can be resolved instead and results from network requests are cached. 59 + 60 + **Third,** operations are sent to the API and the result is normalized. The result then travels 61 + backwards through the returned stream of results. 62 + 63 + ## The Rules of Exchanges 64 + 65 + Before we can start writing some exchanges, there are a couple of patterns and limitations that 66 + always remain the same when writing an exchange. We call these the "rules of _Exchanges_", which 67 + also come in useful when trying to learn what _Exchanges_ actually are. 68 + 69 + For reference, this is a basic template for an exchange: 70 + 71 + ```js 72 + const noopExchange = ({ client, forward }) => { 73 + return operation$ => { // <-- The ExchangeIO function 74 + const operationResult$ = forward(operations$); 75 + return operationResult$; 76 + }; 77 + }; 78 + ``` 79 + 80 + This exchange does nothing else than forward all operations and return all results. Hence, it's 81 + called a `noopExchange`, an exchange that doesn't do anything. 82 + 83 + ### Forward and Return Composition 84 + 85 + When you create a `Client` and pass it an array of exchanges, `urql` composes them left-to-right. 86 + If we look at our previous `noopExchange` example in context, we can track what it does if it sat 87 + in-between the `dedupExchange` and the `fetchExchange`. 88 + 89 + ```js 90 + import { Client, dedupExchange, fetchExchange } from 'urql'; 91 + 92 + const noopExchange = ({ client, forward }) => { 93 + return operation$ => { // <-- The ExchangeIO function 94 + // We receive a stream of Operations from `dedupExchange` which 95 + // we can modify before... 96 + const forwardOperations$ = operations$; 97 + 98 + // ...calling `forward` with the modified stream. The `forward` 99 + // function is the next exchange's `ExchangeIO` function, in this 100 + // case `fetchExchange`. 101 + const operationResult$ = forward(operations$); 102 + 103 + // We get back `fetchExchange`'s stream of results, which we can 104 + // also change before returning, which is what `dedupExchange` 105 + // will receive when calling `forward`. 106 + return operationResult$; 107 + }; 108 + }; 109 + 110 + const client = new Client({ 111 + exchanges: [dedupExchange, noopExchange, fetchExchange], 112 + }); 113 + ``` 114 + 115 + ### One operations stream only 116 + 117 + When writing an _Exchange_ we have to be careful not to "split" the stream into multiple ones by 118 + subscribing multiple times. Streams are lazy and immutable by default. Every time you use them, you 119 + create a new chain of streaming operators, but since _Exchanges_ are side-effects, we don't want to 120 + accidentally have multiple instances of them in parallel. 121 + 122 + Your `ExchangeIO` function receives an `operations$` stream, and you must be careful to either only 123 + use it once, or to _share_ its subscription. 124 + 125 + ```js 126 + import { pipe, filter, merge, share } from 'wonka'; 127 + 128 + // DON'T: split use operations$ twice 129 + ({ forward }) => operations$ => { // <-- The ExchangeIO function (inline) 130 + const queries = pipe( 131 + operations$, 132 + filter(op => op.operationName === 'query') 133 + ); 134 + const others = pipe( 135 + operations$, 136 + filter(op => op.operationName !== 'query') 137 + ); 138 + return forward(merge([queries, others])); 139 + }; 140 + 141 + // DO: share operations$ if you have to use it twice 142 + ({ forward }) => operations$ => { // <-- The ExchangeIO function (inline) 143 + const shared = pipe( 144 + operations$, 145 + share 146 + ); 147 + const queries = pipe( 148 + shared, 149 + filter(op => op.operationName === 'query') 150 + ); 151 + const others = pipe( 152 + shared, 153 + filter(op => op.operationName !== 'query') 154 + ); 155 + return forward(merge([queries, others])); 156 + }; 157 + 158 + // DO: use operations$ only once alternatively 159 + ({ forward }) => operations$ => // <-- The ExchangeIO function (inline) 160 + pipe( 161 + operations$, 162 + map(op => { 163 + if (op.operationName === 'query') { 164 + /* ... */ 165 + } else { 166 + /* ... */ 167 + } 168 + }), 169 + forward 170 + ); 171 + ``` 172 + 173 + So if you see the `operations$` stream twice in your exchange code, make sure to 174 + use Wonka's [`share`](https://wonka.kitten.sh/api/operators#share) operator, to share the underlying 175 + subscription between all your streams. 176 + 177 + ### Don't accidentally drop operations 178 + 179 + Typically the `operations$` stream will send you `query`, `mutation`, 180 + `subscription`, and `teardown`. There is no constraint for new operations 181 + to be added later on or a custom exchange adding new operations altogether. 182 + 183 + This means that you have to take "unknown" operations into account and 184 + not `filter` operations too aggressively. 185 + 186 + ```js 187 + import { pipe, filter, merge, share } from 'wonka'; 188 + 189 + // DON'T: drop unknown operations 190 + ({ forward }) => operations$ => { 191 + // This doesn't handle operations that aren't queries 192 + const queries = pipe( 193 + operations$, 194 + filter(op => op.operationName === 'query') 195 + ); 196 + return forward(queries); 197 + }; 198 + 199 + // DO: forward operations that you don't handle 200 + ({ forward }) => operations$ => { 201 + const shared = pipe( 202 + operations$, 203 + share 204 + ); 205 + const queries = pipe( 206 + shared, 207 + filter(op => op.operationName === 'query') 208 + ); 209 + const rest = pipe( 210 + shared, 211 + filter(op => op.operationName !== 'query') 212 + ); 213 + return forward(merge([queries, rest])); 214 + }; 215 + ``` 216 + 217 + If you group and or filter operations by what your exchange is handling, 218 + also make sure that you have a stream of operations that it's not handling, 219 + which you should also forward. 220 + 221 + ### Synchronous first, Asynchronous last 222 + 223 + By default exchanges and Wonka streams are as predictable as possible. 224 + Every operator in Wonka runs synchronously until you actually introduce 225 + asynchronicity. 226 + 227 + This may happen when you use a timing utility from Wonka, like 228 + [`delay`](https://wonka.kitten.sh/api/operators#delay) or 229 + [`throttle`](https://wonka.kitten.sh/api/operators#throttle) 230 + Or this could happen because your exchange inherently does something asynchronous, like fetching some 231 + data or use a promise. 232 + 233 + When you write exchanges, some will inevitably be asynchronous, if 234 + they're fetching results, performing authentication, or other tasks 235 + that you have to wait for. 236 + 237 + This can cause problems, because the behavior in `urql` is built 238 + to be _synchronous_ first. This helps us build our suspense mode, 239 + and it helps your components receive cached data on their initial 240 + mount without rerendering. 241 + 242 + This why **all exchanges should be ordered synchronous first and 243 + asynchronous last**. 244 + 245 + The default order of exchanges is: 246 + 247 + ```js 248 + import { dedupExchange, cacheExchange, fetchExchange } from 'wonka'; 249 + 250 + // Also exported as `defaultExchanges`: 251 + [dedupExchange, cacheExchange, fetchExchange]; 252 + ``` 253 + 254 + Both the `dedupExchange` and `cacheExchange` are completely 255 + synchronous and only the `fetchExchange` is asynchronous since 256 + it makes a `fetch` request and waits for a server response. 257 + 258 + When you're adding more exchanges you obviously have a reason 259 + to put them in a specific order. For instance, an authentication exchange 260 + needs to go before the `fetchExchange`. And a secondary cache would 261 + maybe go in front of the default cache exchange. 262 + 263 + But to ensure the correct behavior of suspense mode and 264 + the initialization of our hooks, it's vital to order your exchanges 265 + so that synchronous exchanges come first and asynchronous ones 266 + come last.
+118
docs/concepts/philosophy.md
··· 1 + --- 2 + title: Philosophy 3 + order: 1 4 + --- 5 + 6 + # Philosophy 7 + 8 + `urql` is a highly customizable and flexible GraphQL client, that happens to come with some default 9 + [core behavior in the core package](./core-package.md). 10 + 11 + By default, we aim to provide features that allow you to build your app quickly with minimal 12 + configuration. `urql` is a client that grows with you. As you go from building your first 13 + GraphQL app to a full experience, we give you he tools to extend and customize `urql` based on 14 + your needs. 15 + 16 + In this guide, we will walkthrough how `urql` is set up internally and how all pieces of the puzzle 17 + — the building blocks of `urql` — interact with one another. 18 + 19 + ## Hello World 20 + 21 + [We previously read about how to set up a `Client` in "Getting 22 + Started".](../basics/setting-up-the-client.md) 23 + 24 + When you use `urql` you will always create and set up a `Client` for which a `createClient` 25 + convenience helper exists. 26 + 27 + ```js 28 + import { Client } from 'urql'; 29 + 30 + const client = new Client({ 31 + url: 'http://localhost:3000/graphql', 32 + }); 33 + ``` 34 + 35 + In `urql`, the client is the first step towards manging the complexity of GraphQL automatically. 36 + 37 + ## Using GraphQL Clients 38 + 39 + You may have worked on a GraphQL API previously and noticed that using GraphQL in your app can be 40 + as straightforward as sending a plain HTTP request with your query to fetch some data. 41 + 42 + But GraphQL provides an opportunity to abstract away a lot of the manual work that goes with 43 + sending these queries and managing the data. This ultimately lets you focus on building 44 + your app without handling the technical details of state management in detail. 45 + 46 + Specifically `urql` simplifies three common aspects of using GraphQL easily: 47 + 48 + - Sending queries and mutations and receiving results _declaratively_ 49 + - Abstracting _caching_ and state management internally 50 + - Providing a central point of _extensibility_ and integration with your API 51 + 52 + In the following sections we'll talk about how `urql` solves these three problems, and how this is 53 + accomplished and abstracted internally. 54 + 55 + ## Declarative Queries 56 + 57 + When you implement queries or mutations with `urql` the `Client` will internally manage the 58 + lifetime and updates for these _operations_. 59 + 60 + Such an _operation_ may be sent to your GraphQL API and you'll subsequently receive results. 61 + When a _cache_ invalidates this result you may receive updated results. When your app 62 + stops being interested in results for an _operation_ (e.g. React unmounts your component) then 63 + the `Client` knows to _teardown_ the _operation_ and stops requesting new data or sending you 64 + results. 65 + 66 + ![Operations and Results](../assets/urql-event-hub.png) 67 + 68 + This all happens in the background while you just declare that you'd like to have data for a given 69 + query. 70 + 71 + ## Caching and State 72 + 73 + When we use GraphQL queries and mutations declaratively with `urql`, we expect them to interact 74 + and update automaticaly. 75 + 76 + Furthermore, we don't wish to send more requests for a query, when we've done so before. We'd like 77 + to instead cache results in-memory and notify other parts of an app when these results chane or 78 + are invalidated by mutations or subscriptions. 79 + 80 + GraphQL clients have access to some amount of type information for any GraphQL API and can hence 81 + cache the results of queries automatically. In `urql` the `Client` can be extended with several 82 + cache implementations, but all of them mean that you'll never mix your declarative query or mutation 83 + code with cache-implementation details, which mostly happen behind the scenes. 84 + 85 + [We previously read about the default "Document Caching".](../basics/document-caching.md) 86 + 87 + Some GraphQL clients also resort to caching data in a normalized format. This is similar to 88 + [how you may store data in Redux.](https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape/) 89 + Using this approach the cache uses more type information to reference parts of the GraphQL only once 90 + in the cache and structures it in a graph, which leads to more shared data, and hence more shared 91 + updates in your UI! 92 + 93 + [Read more about how to add "Normalized Caching" to an app.](../graphcache/normalized-caching.md) 94 + 95 + ## Extensibility and Integration 96 + 97 + With any kind of API come other concerns apart from caching and state mangagement that concern 98 + the global behavior or business logic of your application. 99 + 100 + For instance, you may want to add authentication, retry-logic for failed requests, or a global 101 + error handler. 102 + 103 + `urql` provides a concept of _Exchanges_ to abstract the details of how the `Client` interacts with 104 + your framework of choice, your app, and your GraphQL API. They are akin to 105 + [middleware in Redux](https://redux.js.org/advanced/middleware) and have access to all operations 106 + and all results. 107 + 108 + [Read more about _Exchanges_ in a later page of the documentation.](./exchanges.md) 109 + 110 + All default behavior in the [core package](./core-package.md) is implemented using 111 + _Exchanges_, which is possible because all operations and all results are treated as a stream 112 + of events. We call these events "Operations". 113 + 114 + ![Operation Signature](../assets/urql-signals.png) 115 + 116 + From our perspective, thinking about your GraphQL queries and results in terms of 117 + streams of operations and results allows us to implement any given complex behaviour, 118 + which we'll learn more about in the next section.
+116
docs/concepts/stream-patterns.md
··· 1 + --- 2 + title: Stream Patterns 3 + order: 1 4 + --- 5 + 6 + # Stream Patterns 7 + 8 + As we've learned [on the last page](./philosophy.md), `urql`'s main way of handling GraphQL requests 9 + is by abstracting them as streams of operations and results 10 + 11 + ## Streams on the Client 12 + 13 + Mainly, the client abstracts GraphQL requests as _Operations_, descriptions of the GraphQL request, 14 + its query and variables, and also additional information that is configured on the `Client`, like 15 + the `url` and `fetchOptions`. 16 + 17 + ![Operations stream and results stream](../assets/urql-client-architecture.png) 18 + 19 + Internally the `Client` is an event hub. It defines a stream of operations as inputs, sends them 20 + through a layer that will ultimately send GraphQL requests to an API, and then sends the results 21 + onto another stream. 22 + 23 + As a user, in framework code, we never interact with these streams directly, but they describe 24 + every interaction between the declarative queries we write and how `urql` fulfills them. 25 + 26 + ## Streams in JavaScript 27 + 28 + Generally we refer to _streams_ as abstractions that allow us to program with asynchronous streams of 29 + events over time, but more specifically in JavaScript, we're thinking specifically of 30 + [Observables](https://github.com/tc39/proposal-observable) 31 + and [Reactive Programming with Observables.](http://reactivex.io/documentation/observable.html) 32 + 33 + These concepts can be quite intimidating, if you're new to them, but from a high-level view what 34 + we're talking about can be thought of as a "combination of Promises and Arrays". 35 + Arrays because we're dealing with multiple items, and Promises because these items arrive 36 + asynchronously. 37 + 38 + Also most Observable libraries come with a toolkit of helper functions that are similar to the 39 + methods on arrays, so you're likely to see `map` and `filter` — amongst other utlities — in those 40 + libraries. 41 + 42 + [Read this Gist for a more in-depth 43 + explanation.](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) 44 + 45 + ## The Wonka library 46 + 47 + `urql` utilises the [Wonka](https://github.com/kitten/wonka) library for its streams. It has a 48 + couple of advantages that are specifically tailored for the `urql` library and ecosystem. 49 + 50 + - It is extremely lightweight and treeshakeable, weighing around 3.7kB minzipped. 51 + - It's cross-platform and cross-language compatible, having been written in 52 + [Reason](https://reasonml.github.io/) and providing support for [Flow](https://flow.org/) 53 + and [TypeScript](typescriptlang.org/). 54 + - It's predictable and also an iterable toolchain, emitting synchronous events whenever possible. 55 + 56 + Typical usage of Wonka will involve creating a _source_ of some values and a _sink_. 57 + 58 + ```js 59 + import { fromArray, map, subscribe, pipe } from 'wonka'; 60 + 61 + const { unsubscribe } = pipe( 62 + fromArray([1, 2, 3]), 63 + map(x => x * 2), 64 + subscribe(x => { 65 + console.log(x); // 1, 2, 3 66 + }) 67 + ); 68 + ``` 69 + 70 + In Wonka, like with Observables, streams are cancellable by calling `unsubscribe` that a 71 + subscription returns. 72 + 73 + [Read more about Wonka in its documentation.](https://wonka.kitten.sh/basics/background) 74 + 75 + ## The Client's query streams 76 + 77 + Internally the `Client` has methods that may be used to execute queries, mutations, and 78 + subscriptions. These methods typically return `Wonka` streams that when subscribed to will 79 + emit results for a given query. 80 + 81 + When a result can be retrieved from an in-memory cache, the stream may even emit the result 82 + synchronously — rather than asynchronously. 83 + 84 + There are three methods for each different type of operation that GraphQL supports, there's an 85 + `executeQuery`, `executeMutation`, and `executeSubscription` method. All these methods are 86 + convenience wrappers around `executeRequestOperation` that create an operation and return a stream. 87 + 88 + There are also convenience wrappers around the "execute" methods that are useful when using `urql` 89 + in a Node.js environment. Those are `query`, `mutation`, and `subscription`. 90 + 91 + ```js 92 + import { pipe, subscribe } from 'wonka'; 93 + 94 + const QUERY = ` 95 + query Test($id: ID!) { 96 + getUser(id: $id) { 97 + id 98 + name 99 + } 100 + } 101 + `; 102 + 103 + const { unsubscribe } = pipe( 104 + client.query(QUERY, { id: 'test' }), 105 + subscribe(result => { 106 + console.log(result); // { data: ... } 107 + }) 108 + ); 109 + ``` 110 + 111 + All methods on the `Client` internally emit an operation on an "operations stream" and the result 112 + for this operation will be filtered out of all results and delivered to your stream. 113 + There are several of these convenience methods in `urql` that make it easier to work with the 114 + concept of GraphQL operation and result streams. 115 + 116 + [Read more about the available APIs on the `Client` in the Core API docs.](../api/core.md)
-511
docs/core/api.md
··· 1 - --- 2 - title: API 3 - order: 6 4 - --- 5 - 6 - # API 7 - 8 - ## React components and hooks 9 - 10 - ### useQuery (hook) 11 - 12 - Accepts a single options object as input: 13 - 14 - ```js 15 - interface UseQueryArgs { 16 - query: string; 17 - variables?: any; 18 - requestPolicy?: RequestPolicy; 19 - pause?: boolean; 20 - pollInterval?: number; 21 - context?: Partial<OperationContext>; 22 - } 23 - ``` 24 - 25 - And returns a tuple of the current query's state and 26 - an `executeQuery` function. 27 - 28 - The current state's shape is: 29 - 30 - ```js 31 - interface UseQueryState<T> { 32 - fetching: boolean; 33 - data?: T; 34 - error?: CombinedError; 35 - extensions?: Record<string, any>; 36 - } 37 - ``` 38 - 39 - And the `executeQuery` function optionally 40 - accepts a partial `OperationContext`. 41 - 42 - [More information on how to use this hook can be found in the Getting Started section.](https://formidable.com/open-source/urql/docs/getting-started#writing-queries) 43 - 44 - ### useMutation (hook) 45 - 46 - Accepts a single `query` argument of type `string`. And returns the 47 - current mutation's state and an `executeMutation` function in a tuple. The 48 - mutation is not started unless `executeMutation` has been called. 49 - 50 - The use of the state is optional as `executeMutation` returns promise 51 - resolving to the `OperationResult` itself. 52 - 53 - The current mutation state's shape is: 54 - 55 - ```js 56 - interface UseMutationState<T> { 57 - fetching: boolean; 58 - data?: T; 59 - error?: CombinedError; 60 - extensions?: Record<string, any>; 61 - } 62 - ``` 63 - 64 - The `executeMutation` function accepts the `variables` of type `object` and 65 - an optional `context` variable of type `Partial<OperationContext>`. 66 - 67 - [More information on how to use this hook can be found in the Getting Started section.](https://formidable.com/open-source/urql/docs/getting-started#writing-mutations) 68 - 69 - ### useSubscription (hook) 70 - 71 - Accepts an options argument as its first input, and a second optional argument that is 72 - the subscription handler function. 73 - 74 - The options argument shape is: 75 - 76 - ```js 77 - interface UseSubscriptionArgs { 78 - query: string; 79 - variables?: any; 80 - context?: Partial<OperationContext>; 81 - } 82 - ``` 83 - 84 - And the handler has the signature: 85 - 86 - ```js 87 - type SubscriptionHandler<T, R> = (prev: R | undefined, data: T) => R; 88 - ``` 89 - 90 - Meaning that the subscription handler receives the previous data or undefined 91 - and the current, incoming subscription event data. 92 - 93 - The hook returns a tuple of only its state: 94 - 95 - ```js 96 - interface UseSubscriptionState<T> { 97 - data?: T; 98 - error?: CombinedError; 99 - extensions?: Record<string, any>; 100 - } 101 - ``` 102 - 103 - [More information on how to use this hook can be found in the Basics section.](https://formidable.com/open-source/urql/docs/basics#subscriptions) 104 - 105 - ### Query (component) 106 - 107 - [More information on how to use this component can be found in the Getting Started section.](https://formidable.com/open-source/urql/docs/getting-started#writing-queries) 108 - 109 - #### Props 110 - 111 - | Prop | Type | Description | 112 - | ------------- | -------------------------- | ----------------------------------------------------------------------------------------------------- | 113 - | query | `string` | The GraphQL request's query | 114 - | variables | `object` | The GraphQL request's variables | 115 - | context | `?object` | The GraphQL request's context | 116 - | requestPolicy | `?RequestPolicy` | An optional request policy that should be used | 117 - | pause | `?boolean` | A boolean flag instructing `Query` to pause execution of the subsequent query operation | 118 - | pollInterval | `?number` | Every `pollInterval` milliseconds the query will be refetched | 119 - | children | `RenderProps => ReactNode` | A function that follows the typical render props pattern. The shape of the render props is as follows | 120 - 121 - #### Render Props 122 - 123 - | Prop | Type | Description | 124 - | ------------ | ----------------------------------- | ------------------------------------------------------------------------------------------------------- | 125 - | fetching | `boolean` | Whether the `Query` is currently waiting for a GraphQL result | 126 - | data | `?any` | The GraphQL request's result | 127 - | error | `?CombinedError` | The `CombinedError` containing any errors that might've occured | 128 - | extensions | `?Record<string, any>` | Optional extensions that the GraphQL server may have returned. | 129 - | executeQuery | `Partial<OperationContext> => void` | A function that can force the operation to be sent again with the given context (Useful for refetching) | 130 - 131 - ### Mutation (component) 132 - 133 - [More information on how to use this component can be found in the Getting Started section.](https://formidable.com/open-source/urql/docs/getting-started#writing-mutations) 134 - 135 - #### Props 136 - 137 - | Prop | Type | Description | 138 - | -------- | -------------------------- | ----------------------------------------------------------------------------------------------------- | 139 - | query | `string` | The GraphQL request's query | 140 - | children | `RenderProps => ReactNode` | A function that follows the typical render props pattern. The shape of the render props is as follows | 141 - 142 - #### Render Props 143 - 144 - | Prop | Type | Description | 145 - | --------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- | 146 - | fetching | `boolean` | Whether the `Mutation` is currently waiting for a GraphQL result | 147 - | data | `?any` | The GraphQL request's result | 148 - | error | `?CombinedError` | The `CombinedError` containing any errors that might've occured | 149 - | extensions | `?Record<string, any>` | Optional extensions that the GraphQL server may have returned. | 150 - | executeMutation | `(variables: object, context?: Partial<OperationContext>) => void` | A function that accepts variables and starts the mutation | 151 - 152 - ### Subscription (component) 153 - 154 - [More information on how to use this component can be found in the Basics section.](https://formidable.com/open-source/urql/docs/basics#subscriptions) 155 - 156 - #### Props 157 - 158 - | Prop | Type | Description | 159 - | --------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | 160 - | query | `string` | The GraphQL subscription's query | 161 - | variables | `object` | The GraphQL subscriptions' variables | 162 - | context | `?Partial<OperationContext>` | The GraphQL subscriptions' context | 163 - | handler | `undefined \| (prev: R \| undefined, data: T) => R` | The handler that should combine/update the subscription's data with incoming data | 164 - | children | `RenderProps => ReactNode` | A function that follows the typical render props pattern. The shape of the render props is as follows | 165 - 166 - #### Render Props 167 - 168 - | Prop | Type | Description | 169 - | ---------- | ---------------------- | --------------------------------------------------------------- | 170 - | fetching | `boolean` | Whether the `Subscription` is currently ongoing | 171 - | data | `?any` | The GraphQL subscription's data | 172 - | error | `?CombinedError` | The `CombinedError` containing any errors that might've occured | 173 - | extensions | `?Record<string, any>` | Optional extensions that the GraphQL server may have returned. | 174 - 175 - ### Context components 176 - 177 - `urql` comes with the two context components `Consumer` and `Provider` as returned 178 - by React's `createContext` utility. It also exports the `Context` itself which can 179 - be used in combination with the `useContext` hook. 180 - 181 - ## The Client and related types 182 - 183 - ### Client (class) 184 - 185 - The client manages all operations and ongoing requests to the exchange pipeline. 186 - It accepts a bunch of inputs when it's created 187 - 188 - | Input | Type | Description | 189 - | ------------ | ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | 190 - | url | `string` | The GraphQL API URL as used by `fetchExchange` | 191 - | fetchOptions | `RequestInit \| () => RequestInit` | Additional `fetchOptions` that `fetch` in `fetchExchange` should use to make a request | 192 - | fetch | `typeof fetch` | An alternative implementation of `fetch` that will be used by the `fetchExchange` instead of `window.fetch` | 193 - | suspense | `?boolean` | Activates the experimental React suspense mode, which can be used during server-side rendering to prefetch data | 194 - | exchanges | `Exchange[]` | An array of `Exchange`s that the client should use instead of the list of `defaultExchanges` | 195 - 196 - `urql` also exposes `createClient()` that is just a convenient alternative to calling `new Client()`. 197 - 198 - #### .executeQuery(), .executeSubscription(), and .executeMutation() 199 - 200 - These methods are used by `<Query>` & `useQuery()`, `<Subscription>` & `useSubscription()`, 201 - and `<Mutation>` & `useMutation()` respectively. 202 - 203 - They accept a `GraphQLRequest` object as their first argument and optionally 204 - a partial `OperationContext` as their second. 205 - 206 - Internally they then create an `Operation` and call `.executeRequestOperation()` with 207 - the `Operation`. This then returns a `Source<OperationResult>`, i.e. a stream of 208 - `OperationResult`s. 209 - 210 - #### .query and .mutation 211 - 212 - These two methods accept a `query`, `variables` and a `context`, these two methods 213 - are really similar to the above in the sense that they return you a `Source<OperationResult>` 214 - you can subscribe to. The difference is that this returned value has a method on it called 215 - `toPromise`, when invoked it will convert the `Source` to a one-time promise. These methods 216 - are ideal for SSR, like for example the `getInitialProps` method in [Next.js](https://nextjs.org/). 217 - 218 - #### .executeRequestOperation() 219 - 220 - This method accepts an `Operation` and handles the flow of said `Operation`. Every `Operation` 221 - that is executed must pass through this method. 222 - 223 - It creates a filtered `Source<OperationResult>` that only contains the `OperationResult`s 224 - relevant to this `Operation` by filtering by the operation `key` and track the subscriptions 225 - to this `Source`. 226 - 227 - This is important as a cache exchange can call `reexecuteOperation` to inform the 228 - client about an invalidation. Whenever an operation needs to be updated with new 229 - network data, it's important to know whether any component is still interested in 230 - this operation. 231 - 232 - To track this, this method ensures that a mapping is updated that counts up 233 - for each subscription to the `Source` and counts down for each unsubscription. 234 - 235 - The `Operation` that has been passed to this method will be dispatched 236 - when the first subscription is started. When the last subscription unsubscribes 237 - from the returned source, this method will ensure that a `teardown` operation 238 - is dispatched. 239 - 240 - > _Note:_ This does not apply to mutations, which are one-off calls and 241 - > hence aren't shared, cancelled, or tracked in the cache. 242 - 243 - The return value is the filtered `Source<OperationResult>`. 244 - 245 - #### .reexecuteOperation() 246 - 247 - This method accepts an `Operation` and will dispatch this `Operation` if there 248 - are any subscriptions from `executeRequestOperation`'s `Source<OperationResult>` 249 - to this particular `Operation`. 250 - 251 - This is called by `cacheExchange` when an `Operation`'s `OperationResult` is 252 - invalidated in the cache. 253 - 254 - #### .createRequestOperation() 255 - 256 - This is called by the `executeQuery`, `executeSubscription` and `executeMutation` 257 - methods to create `Operation`s. It accepts: 258 - 259 - - `OperationType` 260 - - `GraphQLRequest` 261 - - and; the optional partial `OperationContext` (`Partial<OperationContext>`) 262 - 263 - It returns an `Operation`. 264 - 265 - #### .dispatchOperation() 266 - 267 - This method dispatches an `Operation` to the exchange pipeline. This is only 268 - used directly by the Client and shouldn't normally be called externally, except 269 - when the tracking logic of active `Operation`s needs to be bypassed. 270 - 271 - These `Operation`s are streamed from the `operations$: Source<Operation>` stream. 272 - The results of all exchanges are similarly output to `results$: Source<OperationResult>`. 273 - 274 - ### OperationType (type) 275 - 276 - This determines what _kind of operation_ the exchanges need to perform. 277 - This can either be: 278 - 279 - - `'subscription'` 280 - - `'query'` 281 - - `'mutation'` 282 - - or; `'teardown'` 283 - 284 - The `'teardown'` operation is special in that it instructs exchanges to cancel 285 - any ongoing operations with the same key as the `'teardown'` operation that is 286 - received. 287 - 288 - ### RequestPolicy (type) 289 - 290 - This determines the strategy that a cache exchange should use to fulfill an operation. 291 - When you implement a custom cache exchange it's recommended that these policies are 292 - handled. 293 - 294 - - `'cache-first'` (default) 295 - - `'cache-only'` 296 - - `'network-only'` 297 - - `'cache-and-network'` 298 - 299 - ### GraphQLRequest (type) 300 - 301 - This often comes up as the **input** for every GraphQL request. 302 - It consists of `query` and optional `variables`. 303 - 304 - ```js 305 - type Operation = { 306 - query: string | DocumentNode, 307 - variables?: object, 308 - key: number, 309 - }; 310 - ``` 311 - 312 - As can be seen it also carries a `key` property. This property 313 - is a hash of both the `query` and the `variables`, to uniquely 314 - identify the request. 315 - 316 - ### OperationContext (type) 317 - 318 - This type is used to give an operation additional metadata and information. 319 - 320 - ```js 321 - type OperationContext = { 322 - fetchOptions?: RequestInit, 323 - requestPolicy: RequestPolicy, 324 - url: string, 325 - [key: string]: any, 326 - }; 327 - ``` 328 - 329 - It contains a lot of the above mentioned Client options and also `requestPolicy`. 330 - It accepts additional, untyped parameters that can be used to send more 331 - information to custom exchanges. 332 - 333 - ### Operation (type) 334 - 335 - The input for every exchange that informs GraphQL requests. 336 - it's essentially an extension of the `GraphQLRequest`. 337 - 338 - ```js 339 - type Operation = { 340 - query: DocumentNode, 341 - variables?: object, 342 - key: number, 343 - operationName: OperationType, 344 - context: OperationContext, 345 - }; 346 - ``` 347 - 348 - The `key` value is a "hash" of `query` and `variables` or another string that 349 - unique identifies the combination of the two. 350 - 351 - ### OperationResult (type) 352 - 353 - The result of every GraphQL request, i.e. an `Operation`. 354 - It's very similar to what comes back from a typical GraphQL API, but 355 - slightly enriched. 356 - 357 - ```js 358 - type OperationResult = { 359 - operation: Operation, // The operation that this result is a response for 360 - data?: any, 361 - error?: CombinedError, 362 - extensions?: Record<string, any>, 363 - }; 364 - ``` 365 - 366 - ### CombinedError (class) 367 - 368 - | Input | Type | Description | 369 - | ------------- | -------------------------------- | --------------------------------------------------------------------------------- | 370 - | networkError | `?Error` | An unexpected error that might've occured when trying to send the GraphQL request | 371 - | graphQLErrors | `?Array<string \| GraphQLError>` | GraphQL Errors (if any) that were returned by the GraphQL API | 372 - | response | `?any` | The raw response object (if any) from the `fetch` call | 373 - 374 - These are both inputs and properties on the `CombinedError`. Additionally it exposes a default `message` 375 - that combines all errors it has received. 376 - 377 - This is on every `OperationResult` that has one or more errors and groups the usual `errors` property 378 - that a GraphQL result might have normally. 379 - 380 - ## Exchanges and their utilities 381 - 382 - ### ExchangeInput (type) 383 - 384 - ```js 385 - type ExchangeInput = { 386 - forward: ExchangeIO, 387 - client: Client, 388 - }; 389 - ``` 390 - 391 - ### ExchangeIO (type) 392 - 393 - A function that receives a stream of operations and must return a stream 394 - of results. 395 - 396 - ```js 397 - type ExchangeIO = (Source<Operation>) => Source<OperationResult>; 398 - ``` 399 - 400 - ### Exchange (type) 401 - 402 - Similar to `redux-observable`'s epics, kind of related to Apollo's links, 403 - also somehow similar to Express' middleware. 404 - 405 - ```js 406 - type Exchange = ExchangeInput => ExchangeIO; 407 - ``` 408 - 409 - This works since every exchange receives `forward` with the `ExchangeInput`. 410 - Exchanges can therefore be chained. They can alter and filter `Operation`s 411 - that go into the next exchange, and they can alter, filter, or return 412 - `OperationResult`s that are returned. 413 - 414 - ### composeExchanges (function) 415 - 416 - This utility accepts multiple exchanges and composes them into a single one. 417 - It chains them in the order that they're given, left to right. 418 - 419 - ```js 420 - function composeExchanges(Exchange[]): Exchange; 421 - ``` 422 - 423 - This can be used to combine some exchanges and is also used by `Client` 424 - to handle the `exchanges` input. 425 - 426 - ### cacheExchange (Exchange) 427 - 428 - The `cacheExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#cacheexchange). 429 - It's of type `Exchange`. 430 - 431 - ### subscriptionExchange (Exchange factory) 432 - 433 - The `subscriptionExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#subscriptions). 434 - It's of type `Options => Exchange`. 435 - 436 - It accepts a single input: `{ forwardSubscription }`. This is a function that 437 - receives an enriched operation and must return an Observable-like object that 438 - streams `GraphQLResult`s with `data` and `errors`. 439 - 440 - ### ssrExchange (Exchange factory) 441 - 442 - The `ssrExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#server-side-rendering). 443 - It's of type `Options => Exchange`. 444 - 445 - It accepts two inputs, `initialState` which is completely 446 - optional and populates the server-side rendered data with 447 - a rehydrated cache, and `isClient` which can be set to 448 - `true` or `false` to tell the `ssrExchange` whether to 449 - write to (server-side) or read from (client-side) the cache. 450 - 451 - By default `isClient` defaults to `true` when the `Client.suspense` 452 - mode is disabled and to `false` when the `Client.suspense` mode 453 - is enabled. 454 - 455 - This can be used to extract data that has been queried on 456 - the server-side, which is also described in the Basics section, 457 - and is also used on the client-side to restore server-side 458 - rendered data. 459 - 460 - When called, this function creates an `Exchange`, which also has 461 - two methods on it: 462 - 463 - - `.restoreData(data)` which can be used to inject data, typically 464 - on the client-side. 465 - - `.extractData()` which is typically used on the server-side to 466 - extract the server-side rendered data. 467 - 468 - Basically, the `ssrExchange` is a small cache that collects data 469 - during the server-side rendering pass, and allows you to populate 470 - the cache on the client-side with the same data. 471 - 472 - During React rehydration this cache will be emptied and it will 473 - become inactive and won't change the results of queries after 474 - rehydration. 475 - 476 - It needs to be used _after_ other caching Exchanges like the 477 - `cacheExchange`, but before any _asynchronous_ Exchange like 478 - the `fetchExchange`. 479 - 480 - ### debugExchange (Exchange) 481 - 482 - An exchange that writes incoming `Operation`s to `console.log` and 483 - writes completed `OperationResult`s to `console.log`. 484 - 485 - ### dedupExchange (Exchange) 486 - 487 - An exchange that keeps track of ongoing `Operation`s that haven't returned had 488 - a corresponding `OperationResult` yet. Any duplicate `Operation` that it 489 - receives is filtered out if the same `Operation` has already been received 490 - and is still waiting for a result. 491 - 492 - ### fallbackExchangeIO (ExchangeIO) 493 - 494 - This is an `ExchangeIO` function that the `Client` adds on after all 495 - exchanges. This function is responsible from filtering `teardown` operations 496 - out of the output and also warns you of unhandled `operationName`s which 497 - can occur when a subscription is used without adding a `subscriptionExchange`. 498 - 499 - ### fetchExchange (Exchange) 500 - 501 - The `fetchExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#fetchexchange). 502 - It's of type `Exchange`. 503 - 504 - ### defaultExchanges (Exchange[]) 505 - 506 - An array of the default exchanges that the `Client` uses when it wasn't passed 507 - an `exchanges` option. 508 - 509 - ```js 510 - const defaultExchanges = [dedupExchange, cacheExchange, fetchExchange]; 511 - ```
-194
docs/core/architecture.md
··· 1 - --- 2 - title: Architecture 3 - order: 1 4 - --- 5 - 6 - # Architecture 7 - 8 - Much of `urql` is about being flexible and customizable. 9 - To this extent a large chunk of this document is dedicated to 10 - how `urql` works and how to adapt it to different use cases. 11 - 12 - If you wish to use `urql` without any customizations, this 13 - document is entirely optional for you. But it's still worth 14 - the read. Promised. 15 - 16 - `urql`'s core parts are separated into three concepts: 17 - **operations & results**, **the client**, and **exchanges**. 18 - 19 - ## Requests 20 - 21 - Every GraphQL Request starts as a collection of just a 22 - query and variables, which are supposed to be sent to 23 - a GraphQL API (presumably). Those are objects that 24 - tie the two together. 25 - 26 - ```js 27 - type GraphQLRequest = { 28 - key: number, 29 - query: string | DocumentNode, 30 - variables?: object, 31 - }; 32 - ``` 33 - 34 - The `key` property will become a unique identifier of the GraphQL Request. 35 - It's a hash of the exact `query` and `variables` combination, i.e. a 36 - unique string for this request. It's used to keep track of 37 - what request the client is dealing with at any given time. 38 - 39 - To begin sending a GraphQL request the **client** has 40 - [three main methods](https://github.com/FormidableLabs/urql/blob/master/src/client.ts) 41 - that are responsible to provide this **input**. 42 - 43 - - `executeQuery` 44 - - `executeSubscription` 45 - - `executeMutation` 46 - 47 - All of these can be called with a `GraphQLRequest` as 48 - the first argument and optionally accept some additional 49 - "context" information. The method that was called determined 50 - the operation that is then sent. 51 - 52 - > _Note:_ In GraphQL and in `urql` the term "query" can be ambiguous. 53 - > It is used to refer to a string composed in the 54 - > [query language](https://facebook.github.io/graphql/June2018/#sec-Overview) 55 - > but also one of the 56 - > [three basic operations](https://facebook.github.io/graphql/June2018/#sec-Language.Operations). 57 - 58 - ## Operations 59 - 60 - The client will enrich every request with meta information, 61 - the result of which is called an **operation**. You can think 62 - of them as the actual input of the eventual GraphQL request. 63 - 64 - All bits that are added to a request to form an **operation** 65 - are there to inform what should happen to it. It determines 66 - how the network request should be sent and how the cache 67 - should behave. 68 - 69 - The shape of an operation extends the shape of a GraphQL Request: 70 - 71 - ```js 72 - type Operation = { 73 - // GraphQLRequest: 74 - key: number, 75 - query: DocumentNode, 76 - variables?: object 77 - 78 - // The rest: 79 - operationName: OperationType, 80 - context: OperationContext 81 - } 82 - ``` 83 - 84 - The `OperationType` here is simply one of the three basic 85 - GraphQL operations: `'subscription'`, `'query'`, or `'mutation'`. 86 - Additionally there's an internal operation type called `'teardown'` 87 - which is used to **cancel** all ongoing work for a previous operation 88 - and free its resources. 89 - 90 - If we call `executeQuery({ query: '{ content }' })`, the client 91 - will internally dispatch the following operation: 92 - 93 - ```js 94 - const exampleOperation = { 95 - query: '{ content }', 96 - variables: undefined, 97 - 98 - key: /* KEY */, 99 - operationName: 'query', 100 - context: {}, 101 - }; 102 - ``` 103 - 104 - The `context` contains some more information and can be extended with 105 - the second argument to `executeQuery`. By default it contains: 106 - 107 - - `fetchOptions` for the `fetch` call's options 108 - - `url` for the `fetch` call's API endpoint 109 - - `requestPolicy` to determine the cache's behavior 110 - 111 - The `executeQuery` call will return a [Wonka](https://github.com/kitten/wonka) 112 - stream. This is just an observable (not following the Observable spec) 113 - that sends back the GraphQL request's result (an **"Operation Result"**). 114 - When all consumers unsubscribe from this stream however, it'll terminate 115 - any ongoing requests for this operation and free resources. 116 - 117 - This is done by sending the exact same operation as above (`exampleOperation`) 118 - but with the `'teardown'` operation name: 119 - 120 - ```js 121 - const teardownOperation = { 122 - ...exampleOperation, 123 - operationName: 'teardown', 124 - }; 125 - ``` 126 - 127 - ## Exchanges 128 - 129 - The next bit of `urql`'s inner workings is how these operations are handled. 130 - When a `new Client()` is created you may pass it `url` and `fetchOptions`. 131 - But you can also pass an `exchanges` array. 132 - 133 - **Exchanges** are operation handlers. It'll receive the `client` and a `forward` 134 - function as an object. It then returns a function accepting a stream of 135 - operations and returning a stream of operation results (i.e. GraphQL results). 136 - 137 - In other words, exchanges are handlers that fulfill our GraphQL requests. 138 - They're Input/Output streams, inputs being operations, outputs being results. 139 - They're also composable. The `forward` function that an **exchange** receives 140 - is just another Input/Output handler. 141 - 142 - In practice the signature is: 143 - 144 - ```js 145 - type ExchangeInput = { forward: ExchangeIO, client: Client }; 146 - 147 - type Exchange = (input: ExchangeInput) => ExchangeIO; 148 - type ExchangeIO = (Source<Operation>) => Source<OperationResult>; 149 - ``` 150 - 151 - The simplest yet useful exchange would be one that accepts all operations and 152 - immediately sends them to a GraphQL API with a fetch call. And in fact, 153 - that is what the default `fetchExchange` does. 154 - 155 - The default exchanges that a client will create when custom no `exchanges` 156 - are passed to it are: 157 - 158 - - `dedupExchange`: Deduplicates pending operations (pending = waiting for a response) 159 - - `cacheExchange`: All caching logic for operations and results 160 - - `fetchExchange`: Sends an operation to the API and returns results 161 - 162 - The `client` accepts exchanges and composes them using the `composeExchanges` function 163 - that is also exported by `urql`. 164 - 165 - In essence these exchanges build a pipeline that runs in the order in which the 166 - exchanges are in the list above. 167 - 168 - **First,** ongoing operations are deduplicated. It wouldn't make sense to send the 169 - same operation / request twice at the same time. 170 - 171 - **Second,** operations are checked against the cache. Depending on the `requestPolicy` 172 - cached results can be resolved instead and results from network requests are cached. 173 - 174 - **Third,** operations are sent to the API and the result is normalized. 175 - 176 - ## Operation Results 177 - 178 - Every operation that enters the exchange pipeline will receive a result, either 179 - immediately (read: synchronously) or eventually as the result of a network 180 - request comes in. 181 - 182 - The raw GraphQL result from an API is: `{ data?: T, errors?: GraphQLError[], extensions?: Record<string, any> }`. 183 - And `urql`'s operation results are very similar: `{ data?: T, error?: CombinedError, extensions?: Record<string, any>}`. 184 - 185 - The [`CombinedError` is a very simple wrapper](https://github.com/FormidableLabs/urql/blob/master/src/utils/error.ts) 186 - that has either a `networkError` property with any unexpected errors that might occur, 187 - or a `graphQLErrors` array with the list of errors that have been returned by the API. 188 - 189 - This is a convenience wrapper that helps when the specific _kind of error_ that has occured 190 - does not matter. 191 - 192 - Additionally `urql`'s operation results will also have the `operation` property, which 193 - just contains the original operation itself, which is how the client can tell which result 194 - it has just received.
-485
docs/core/basics.md
··· 1 - --- 2 - title: Basics 3 - order: 2 4 - --- 5 - 6 - # Basics 7 - 8 - As mentioned before, `urql`'s core logic is split into exchanges. 9 - To that end, `urql`'s behavior is completely defined by the exchanges 10 - you pass to it or that are the default ones. 11 - 12 - This document goes through the exchanges that `urql` adds by default. 13 - When you create a client and pass no `exchanges` array some are added 14 - automatically, which is the same as creating a client using the following 15 - exchanges: 16 - 17 - ```js 18 - import { Client, dedupExchange, cacheExchange, fetchExchange } from 'urql'; 19 - 20 - const client = new Client({ 21 - url: '/graphql', 22 - exchanges: [dedupExchange, cacheExchange, fetchExchange], 23 - }); 24 - ``` 25 - 26 - This list of default exchanges is also exported as `defaultExchanges` 27 - however. 28 - 29 - ## fetchExchange 30 - 31 - <a name="fetchexchange"></a> 32 - 33 - The `fetchExchange` handles `query` and `mutation` operations and uses `fetch` 34 - to send GraphQL API requests. 35 - 36 - > _Note:_ Depending on your browser support, you might want to add a fetch 37 - > polyfill to your app. 38 - 39 - It also supports cancellation. When an operation becomes "stale", meaning a 40 - component that requested it has unmounted for instance, a `teardown` 41 - operation is sent which can cause `fetch` to abort ongoing requests 42 - when necessary. 43 - 44 - Generally there's a couple of things to know about `fetch`. 45 - 46 - ### Fetch Options 47 - 48 - You might have noticed that `fetchOptions` is an option on the 49 - client that can be `RequestInit` or `() => RequestInit`. The 50 - `RequestInit` is just passed on to the `OperationContext`, meaning 51 - that you can also pass it to `executeQuery`. 52 - 53 - This is then spread onto the `fetchOptions` that `fetch` will use. 54 - The defaults are as follows. 55 - 56 - ```js 57 - { 58 - body: /* ... */, 59 - headers: { 'Content-Type': 'application/json' }, 60 - method: 'POST', 61 - signal: /* ... */ 62 - } 63 - ``` 64 - 65 - The `signal` is the property that is used for the `abort-controller`. 66 - 67 - The `fetchExchange` will also handle `response.status` correctly and 68 - allow `response.status >= 300` when `redirect` is set to `'manual'`. 69 - 70 - **In summary:** `fetchExchange` is a simple request handler that takes 71 - operations and sends `POST` requests using `fetch`. 72 - 73 - ## cacheExchange 74 - 75 - <a name="cacheexchange"></a> 76 - 77 - The default caching behavior that `urql` uses is defined by the `cacheExchange` 78 - unlike Apollo's `Cache` or `InMemoryCache`, caching behavior is handled as 79 - part of the request pipeline, which makes customization a lot easier as 80 - there's no extra API to learn. 81 - 82 - By default however, `urql`'s caching behavior is not that of a _"normalizing 83 - cache"_ but more of a _"document cache"_. 84 - 85 - ### The document cache 86 - 87 - When an **operation** is sent it is identified by its `key` which is a hash 88 - of the `query` and `variables`. A document cache makes the assumption 89 - that there's **no overlap** between any two given queries. 90 - 91 - When a query is sent and succeeds, the entire operation result is 92 - cached. This is a simple map of `key` to `OperationResult`. 93 - 94 - The **document cache** does not cache by distinct GraphQL types via 95 - `__typename`. Instead it caches whole results. 96 - 97 - When a mutation is sent and comes back the document cache invalidates 98 - parts of the cache. It makes the assumption that a mutation's 99 - `__typename` fields indicate that all these types in the cache 100 - are now invalid. 101 - 102 - For example, when we fetch a list of `TodoItem`s the response will 103 - contain fields of `__typename: 'TodoItem'`. The document cache 104 - then caches the result and also keeps a map of type names to 105 - operation keys. 106 - 107 - When a mutation result comes back that contains `__typename: 'TodoItem'` 108 - as well, the document cache invalidates all previous query results 109 - that also contained these types. 110 - 111 - ### Limitations 112 - 113 - This is a very primitive approach to caching, but works out well 114 - for a lot of content-driven sites. 115 - 116 - It might lead to more requests similar to a relatively simple 117 - content app that just sends RESTful requests. 118 - 119 - The only assumption that `urql` makes is that your mutations 120 - respond with the types that are invalidated. 121 - 122 - Given an `addTodo` mutation for example, you will need to send 123 - back at least one `TodoItem` for the invalidation to happen. 124 - 125 - A **document cache** also doesn't normalize at all, which means 126 - that after fetching a list of items, fetching a single item 127 - will never be fulfilled by this cache. 128 - 129 - ### Request Policies 130 - 131 - The operation context can also contain a `requestPolicy` property 132 - that alters when and how the cache responds. 133 - By default this will be set to `'cache-first'`. 134 - 135 - When `'cache-first'`, the default behavior, is used, the cache 136 - will return all cached results when they're available. When no 137 - cached result is available it will let the operation through, so 138 - that the `fetchExchange` can send a request to the API. 139 - 140 - When `'cache-only'` is passed, the cache will always return the 141 - cached result or default to `{ data: undefined, error: undefined }`, 142 - i.e. an empty result, when nothing is cached for a given operation. 143 - 144 - For `'network-only'` the opposite of `'cache-only'` is done. 145 - The `cacheExchange` will never return cached results, but will 146 - instead immediately forward the operation to the next exchange, 147 - so that the `fetchExchange` can respond with up-to-date data. 148 - The result will still be cached however. 149 - 150 - The last one `'cache-and-network'` is rather special 151 - in that it first does what `'cache-first'` does: it will 152 - return some cached results. After returning a cached result however, 153 - it will forward the operation anyway. This way a temporary cached 154 - result may be displayed that is then updated with fresh data 155 - from the API. 156 - 157 - > _Note:_ `'network-only'` and `'cache-and-network'` are extremely valuable 158 - > given the limitations of the default cache. They can be used to ensure 159 - > that data skips the cache, if it's clear to you that the result will 160 - > need to be up-to-date. 161 - 162 - ### Customization 163 - 164 - The idea of `urql` is that you can customize the caching behavior amongst 165 - other things yourself, if needed. 166 - 167 - [Read more about customizing `urql` in the "Extending & Experimenting" section.](https://formidable.com/open-source/urql/docs/extending-&-experimenting) 168 - 169 - ## Suspense 170 - 171 - React has this neat feature called [`suspense`](https://reactjs.org/docs/code-splitting.html) which 172 - these days is used for `code-splitting` but it can be used for more. In our case when 173 - your query is fetching we can also utilise this `Suspense` boundary to transition 174 - into a loading state instead of you having to do this manually for every component. 175 - 176 - Enabling suspense can be done by going to your client and passing it the option `suspense: true`. 177 - Additionally, we'll need to add an [`exchange`](https://github.com/FormidableLabs/urql-exchange-suspense) 178 - to our client. This exchange will make `client.suspense` linear to `React.lazy` as we would 179 - expect. 180 - 181 - > Be careful when combining this with `ssr`, the next chapter will cover this. 182 - 183 - ## Server-side rendering 184 - 185 - Server-side rendering is a common method to reduce the time it takes for 186 - a user to see a React page's content. Typically this is implemented using 187 - the [`react-dom/server` package](https://reactjs.org/docs/react-dom-server.html). 188 - 189 - `urql` can be set up to fetch data on the server and rehydrate this data 190 - on the client, so that the user's browser does not need to refetch it and 191 - can seamlessly rehydrate your React page. 192 - 193 - There are two parts in `urql` that enable server-side rendering: 194 - 195 - - The `Client` has a `suspense` option, which enables support for React's 196 - experimental Suspense API for data fetching, which allows us to prefetch 197 - data before calling `renderToString` or `renderToNodeStream`. 198 - - The `ssrExchange`, which is a small operation cache that works together 199 - with Suspense to save data on the server and rehydrate it on the client. 200 - 201 - Since Suspense is still an experimental API there's no official way to use 202 - it to prefetch data on the server-side. For this reason we have a companion 203 - library, [`react-ssr-prepass`](https://github.com/FormidableLabs/react-ssr-prepass), which can be used to run a "prepass" 204 - that fetches all suspended data it finds in a React element tree. 205 - 206 - ### Setting up the Client 207 - 208 - When you set up the `Client` for server-side rendering, on the server 209 - you will need to set `suspense` to `true` and on the client to `false`, 210 - 211 - ```js 212 - import { Client } from 'urql'; 213 - 214 - const client = new Client({ 215 - suspense: !process.browser, 216 - // ... 217 - }); 218 - ``` 219 - 220 - You can often achieve this with `process.browser` in most environments if 221 - you're using a single universal file to create a client on the server 222 - and on the client. 223 - 224 - Next up, the `ssrExchange` needs to be set up. It's a factory, since 225 - it has some methods for extracting and rehydrating data. 226 - 227 - ```js 228 - import { 229 - Client, 230 - dedupExchange, 231 - cacheExchange, 232 - fetchExchange, 233 - ssrExchange, 234 - } from 'urql'; 235 - 236 - const ssrCache = ssrExchange(); 237 - 238 - const client = new Client({ 239 - exchanges: [ 240 - dedupExchange, 241 - cacheExchange, 242 - // Put the exchange returned by calling ssrExchange after your cacheExchange, 243 - // but before any asynchronous exchanges like the fetchExchange: 244 - ssrCache, 245 - fetchExchange, 246 - ], 247 - // ... 248 - suspense: !process.browser, 249 - }); 250 - ``` 251 - 252 - The exchange returned by `ssrExchange()` should be added after the `cacheExchange` 253 - (or any other custom cache exchange you've defined), and before any 254 - asynchronous exchanges like the `fetchExchange`. 255 - 256 - If you're also using suspense mode on the client, you can 257 - additionally set the `isClient` option, which tells the `ssrExchange` manually 258 - whether it's on the server or client, so that you can enable the `suspense` 259 - mode on the client-side as well. 260 - 261 - ```js 262 - const ssrCache = ssrExchange({ isClient: !!process.browser }); 263 - ``` 264 - 265 - ### Prefetching on the server 266 - 267 - In your request handler on the server-side, you'll have to add some 268 - code for handling suspense. Typically this is done using a "prepass" that 269 - walks your element tree and awaits suspended promises. 270 - 271 - In order to execute suspense on the server, you may install 272 - [`react-ssr-prepass`](https://github.com/FormidableLabs/react-ssr-prepass), which is a partial server-side rendering library, 273 - that can be used to execute a prepass on a React element tree. 274 - It supports React's experimental Suspense API and awaits thrown 275 - promises during the server-side prepass, which we'll use to prefetch 276 - all queries in your React app. 277 - 278 - ```sh 279 - # react-is is a peer dependency of react-ssr-prepass 280 - yarn add react-ssr-prepass react-is 281 - # or 282 - npm install --save react-ssr-prepass react-is 283 - ``` 284 - 285 - Add `react-ssr-prepass` to your server-side rendering code _before_ 286 - calling `renderToString` or `renderToNodeStream`. This will fetch 287 - all suspended promises, including `urql`'s queries. And after 288 - you can use the `ssrExchange()`'s `extractData` method to 289 - get `urql`'s data: 290 - 291 - ```js 292 - import ssrPrepass from 'react-ssr-prepass'; 293 - 294 - const handler = (req, res) => { 295 - // ... 296 - // We assume you've already set up the urql Client and have 297 - // the `ssrCache = ssrExchange()` variable from somewhere 298 - 299 - await ssrPrepass(<App />); 300 - 301 - // Extract the data from urql's SSR cache 302 - const urqlData = ssrCache.extractData(); 303 - 304 - // Then you can run your rendering code for which the ssrCache 305 - // should remain unchanged 306 - const reactHtml = renderToString(<App />); 307 - 308 - // Make sure to send urql's data down to the client somehow 309 - const urqlHtml = `<script>window.URQL_DATA = ${JSON.stringify(urqlData)};</script>`; 310 - 311 - // And send everything down to the client 312 - // ... 313 - }; 314 - ``` 315 - 316 - ### Rehydrating on the client 317 - 318 - Now you have server-side rendered the page and sent down data 319 - collected during the render pass. As a next step, you should 320 - rehydrate the data on the client-side. 321 - 322 - This is necessary since the same data needs to be available 323 - during React's rehydration so that the client-side renders the 324 - exact same data and UI state. 325 - 326 - You can either do so when creating `ssrExchange`, by passing it `initialState` 327 - as a parameter or calling `restoreData` on it: 328 - 329 - ```js 330 - import { ssrExchange } from 'urql'; 331 - 332 - const ssrCache = ssrExchange({ 333 - initialState: window.URQL_DATA, 334 - }); 335 - 336 - // or: 337 - 338 - ssrCache.restoreData(window.URQL_DATA); 339 - 340 - // Assuming this follows the client setup and is added to the `exchanges` list 341 - // ... 342 - ``` 343 - 344 - Your setup may vary depending on whether your client initialization code 345 - is universal (single file that executes on client and server-side, such as Next.js' 346 - `async getInitialProps`), or two separate files for client and server. 347 - 348 - If you're using [next.js](https://nextjs.org/) or need some more details on how to set this 349 - up [have a look at our SSR + next.js example project](https://github.com/FormidableLabs/urql/tree/master/examples/3-ssr-with-nextjs). 350 - 351 - ## Subscriptions 352 - 353 - One feature of `urql` that was not mentioned in the 354 - ["Getting Started" section](https://formidable.com/open-source/urql/docs/getting-started) is `urql`'s 355 - APIs and ability to handle subscriptions. 356 - 357 - To add support for subscriptions there's the `subscriptionExchange`. 358 - When you first setup subscriptions you will need to add it. 359 - 360 - ```js 361 - import { Client, defaultExchanges, subscriptionExchange } from 'urql'; 362 - 363 - const client = new Client({ 364 - url: '/graphql', 365 - exchanges: [ 366 - ...defaultExchanges, 367 - subscriptionExchange({ 368 - forwardSubscription, 369 - }), 370 - ], 371 - }); 372 - ``` 373 - 374 - In the above example, we add the `subscriptionExchange`, which needs 375 - to be called with some additional options, to the client. 376 - 377 - The `subscriptionExchange` does not make any assumption over the 378 - transport protocol and scheme that is used. Instead the `forwardSubscription` 379 - function will be called with an enriched operation, which can then be 380 - passed to your subscription client. It expects an "Observable-like" 381 - object to be returned, which needs to follow the 382 - [Observable spec](https://github.com/tc39/proposal-observable). 383 - 384 - If you're set up with `apollo-server` or another server that uses 385 - the `subscriptions-transport-ws` package, 386 - [have a look at our subscriptions example project](https://github.com/FormidableLabs/urql/tree/master/examples/2-using-subscriptions). 387 - 388 - Once you've set up the `subscriptionExchange` and your 389 - `forwardSubscription` function, you can start using 390 - the `<Subscription>` component and/or the `useSubscription()` hook. 391 - 392 - > A tutorial on setting up the `subscriptionExchange` is also available as a 393 - > [screencast on egghead](https://egghead.io/lessons/graphql-set-up-graphql-subscriptions-with-urql?pl=introduction-to-urql-a-react-graphql-client-faaa2bf5). 394 - 395 - ### Usage with components 396 - 397 - The `<Subscription>` component is extremely similar to the `<Query>` 398 - component. You can pass it a query and variables, and it will serve 399 - you render props with `data`, `error`, `extensions`, and `fetching`. 400 - 401 - ```js 402 - import { Subscription } from 'urql'; 403 - 404 - const newMessages = ` 405 - subscription MessageSub { 406 - newMessages { 407 - id 408 - from 409 - message 410 - } 411 - } 412 - `; 413 - 414 - <Subscription query={newMessages}> 415 - {({ data }) => /* ... */} 416 - </Subscription> 417 - ``` 418 - 419 - The `data` and `error` of the render props will change every time 420 - a new event is received by the server. When you're accumulating and 421 - collecting events over time, it makes sense to pass this data 422 - into another component and combine it. 423 - 424 - ### Usage with hooks 425 - 426 - The `useSubscription` hooks comes with a similar API to `useQuery`. 427 - It will accept `query` and `variables` as options. 428 - 429 - Additionally the second argument for this hook can be a "reducer function". 430 - This function is similar to what you would pass to `Array.prototype.reduce`. 431 - 432 - It receives the previous set of data that this function has returned or `undefined`. 433 - As the second argument, it receives the event that has come in from the subscription. 434 - You can use this to accumulate the data over time, which is useful for a 435 - list for example. 436 - 437 - In the following example, we create a subscription that informs us of 438 - new messages. We will concatenate the incoming messages, so that we 439 - can display all messages that have come in over the subscription across 440 - events. 441 - 442 - ```js 443 - import React from 'react'; 444 - import { useSubscription } from 'urql'; 445 - 446 - const newMessages = ` 447 - subscription MessageSub { 448 - newMessages { 449 - id 450 - from 451 - text 452 - } 453 - } 454 - `; 455 - 456 - const handleSubscription = (messages = [], response) => { 457 - return [response.newMessages, ...messages]; 458 - }; 459 - 460 - const Messages = () => { 461 - const [res] = useSubscription({ query: newMessages }, handleSubscription); 462 - 463 - if (!res.data) { 464 - return <p>No new messages</p>; 465 - } 466 - 467 - return ( 468 - <ul> 469 - {res.data.map(message => ( 470 - <p key={message.id}> 471 - {message.from}: "{message.text}" 472 - </p> 473 - ))} 474 - </ul> 475 - ); 476 - }; 477 - ``` 478 - 479 - As we can see, the `result.data` is being updated and transformed by 480 - the `handleSubscription` function. This works over time, so as 481 - new messages come in, we will append them to the list of previous 482 - messages. 483 - 484 - > A tutorial on the `useSubscription` hook is also available as a 485 - > [screencast on egghead](https://egghead.io/lessons/graphql-write-a-graphql-subscription-with-react-hooks-using-urql?pl=introduction-to-urql-a-react-graphql-client-faaa2bf5).
-23
docs/core/exchanges.md
··· 1 - --- 2 - title: Exchanges 3 - order: 3 4 - --- 5 - 6 - # Exchanges 7 - 8 - ## Packages 9 - 10 - These exchanges can be imported from the `urql` package. 11 - 12 - - `cacheExchange`: the default document cache implementation 13 - - `debugExchange`: logs information about ongoing operations and results 14 - - `dedupExchange`: deduplicates ongoing operations 15 - - `fetchExchange`: sends operations to GraphQL HTTP endpoints and resolves results 16 - - `ssrExchange`: used to cache results during SSR and rehydrate them on the client-side 17 - - `subscriptionExchange`: used to support GraphQL subscriptions 18 - 19 - ## Addons 20 - 21 - - [`@urql/devtools`](https://github.com/FormidableLabs/urql-devtools): A Chrome extension for monitoring and debugging 22 - - [`@urql/exchange-suspense`](https://github.com/FormidableLabs/urql-exchange-suspense): An experimental exchange for using `<React.Suspense>` 23 - - [`urql-persisted-queries`](https://github.com/Daniel15/urql-persisted-queries): An exchange for adding persisted query support
-162
docs/core/extending-and-experimenting.md
··· 1 - --- 2 - title: Extending & Experimenting 3 - order: 4 4 - --- 5 - 6 - # Extending & Experimenting 7 - 8 - Hopefully you have read the sections on `urql`'s [Architecture](https://formidable.com/open-source/urql/docs/architecture) 9 - and its [Basics](https://formidable.com/open-source/urql/docs/basics). This section will introduce you to hacking 10 - with `urql`. 11 - 12 - `urql` comes with some very functional defaults, but its standard component APIs, 13 - hook APIs, or its core behavior might not be enough for your complex app. Or 14 - maybe you're just looking to play around and experiment with GraphQL clients? 15 - 16 - This document contains two main sections. The first is about reusing `urql`'s 17 - core and build new "outward facing APIs". The second is about writing new 18 - exchanges and hence changing `urql`'s core behavior. 19 - 20 - ## Writing new APIs for the Client 21 - 22 - Usage of `urql`'s Client is not limited to the included React component 23 - and hooks or even React at all. 24 - 25 - The Client is structured so that it can easily be reused to create different 26 - APIs or integrate `urql` with other libraries and frameworks than React. 27 - [Have a look at the API docs to see a full list of the client's methods.](https://formidable.com/open-source/urql/docs/api#client-class) 28 - 29 - While it's possible to write new APIs outside of React or new components 30 - quite easily, this section will only focus on hooks, for illustrative 31 - purposes. 32 - 33 - ### Writing `useQuery` from scratch 34 - 35 - There's a couple of outward facing APIs that `urql` comes with, but none 36 - illustrate how to write new APIs better than 37 - [the `useQuery` hook](https://github.com/FormidableLabs/urql/blob/master/src/hooks/useQuery.ts). 38 - 39 - To write a basic `useQuery` hook, we'll start by pulling in the client via 40 - the context API. Every GraphQL request must pass through the client to make 41 - use of `urql`'s `Operation` management and exchange pipeline. 42 - 43 - ```js 44 - import { useContext } from 'react'; 45 - import { Context } from 'urql'; 46 - 47 - export const useQuery = () => { 48 - const client = useContext(Context); 49 - }; 50 - ``` 51 - 52 - At this point we have the client. Next we'll want to accept a request 53 - and execute a query. 54 - 55 - ```js 56 - import { useContext } from 'react'; 57 - import { Context, createRequest } from 'urql'; 58 - 59 - export const useQuery = ({ query, variables }) => { 60 - const client = useContext(Context); 61 - const request = createRequest(query, variables); 62 - const source = client.executeQuery(request); 63 - }; 64 - ``` 65 - 66 - The `createRequest` helpers is used to create a `GraphQLRequest` object. 67 - It's very simple and apart from putting the `query` and `variables` onto 68 - an object, it also hashes them and adds a `key` property. We then pass this 69 - request to `client.executeQuery` and receive a `Source`. 70 - 71 - The `Source` is a type from [Wonka](https://github.com/kitten/wonka) and 72 - can basically be understood to be an Observable that doesn't follow the 73 - [Observable spec](https://github.com/tc39/proposal-observable) but 74 - instead the [Callbag spec (loosely).](https://github.com/callbag/callbag) 75 - 76 - It comes with some helpers that users of `RxJS` might already be 77 - familiar with. We don't need a lot of these functions though 78 - to just subscribe to `source` and get the data out. In fact, 79 - we'll only need `subscribe`. 80 - 81 - ```js 82 - import { useContext } from 'react'; 83 - import { pipe, subscribe } from 'wonka'; 84 - import { Context, createQuery } from 'urql'; 85 - 86 - export const useQuery = ({ query, variables }) => { 87 - const client = useContext(Context); 88 - const request = createQuery(query, variables); 89 - 90 - pipe( 91 - client.executeQuery(request), 92 - subscribe(({ data, error }) => { 93 - console.log(data, error); 94 - }) 95 - ); 96 - }; 97 - ``` 98 - 99 - We are now able to receive the GraphQL results. Next we'll want to 100 - use `useState` to store the results in some state and we'll want 101 - to wrap the query in `useEffect` so we can trigger it when 102 - the hook's inputs change. 103 - 104 - ```js 105 - import { useContext, useState, useEffect } from 'react'; 106 - import { pipe, subscribe } from 'wonka'; 107 - import { Context, createQuery } from 'urql'; 108 - 109 - export const useQuery = ({ query, variables }) => { 110 - const [result, setResult] = useState({ 111 - fetching: false, 112 - error: undefined, 113 - data: undefined, 114 - }); 115 - 116 - const client = useContext(Context); 117 - 118 - useEffect(() => { 119 - setResult(prev => ({ ...prev, fetching: true })); 120 - 121 - const request = createQuery(query, variables); 122 - 123 - const [teardown] = pipe( 124 - client.executeQuery(request), 125 - subscribe(({ data, error }) => { 126 - setResult({ fetching: false, data, error }); 127 - }) 128 - ); 129 - 130 - return teardown; 131 - }, [query, variables]); 132 - 133 - return result; 134 - }; 135 - ``` 136 - 137 - We have now: 138 - 139 - - Added some default state with `fetching: false` 140 - - Wrapped the query in `useEffect` 141 - - Added the `teardown` function that unsubscribes from the source and return it 142 - in `useEffect` 143 - - Added the second `useEffect` argument to tell it when to rerun 144 - - Added state to set `fetching: true` and to update the result 145 - 146 - In the actual implementation of the hook we also generalize this 147 - execution and expose the `executeQuery` function in the returned tuple. 148 - This is left out here as the implementation can be found in the source code. 149 - 150 - This relatively simple pattern can be used to implement 151 - new APIs. All we need to do is create a `GraphQLRequest`, 152 - subscribe to the result of `executeQuery`, keep track 153 - of the unsubscription (`teardown`) and update 154 - some state. This all can be reapplied when you write your 155 - own APIs. 156 - 157 - ## FAQ 158 - 159 - My component/hooks keeps triggering rerenders when passing in `context` 160 - 161 - - When not memoizing `context` this will always trigger refetches, 162 - this can be done through the `useMemo` hook.
-363
docs/core/getting-started.md
··· 1 - --- 2 - title: Getting Started 3 - order: 0 4 - --- 5 - 6 - # Getting Started 7 - 8 - ## Installation 9 - 10 - Installing `urql` is as quick as you'd expect. Firstly, install it 11 - with your package manager of choice: 12 - 13 - ```sh 14 - yarn add urql 15 - # or 16 - npm install --save urql 17 - ``` 18 - 19 - Then, if you haven't already, make sure that all peer dependencies 20 - are installed as well: 21 - 22 - ```sh 23 - yarn add react react-dom graphql 24 - # or 25 - npm install --save react react-dom graphql 26 - ``` 27 - 28 - > _Note:_ Most libraries related to GraphQL specify `graphql` as their peer 29 - > dependency so that they can adapt to your specific versioning 30 - > requirements. 31 - > The library is updated frequently and remains very backwards compatible, 32 - > but make sure it will work with other GraphQL tooling you might have installed. 33 - 34 - ## Creating a client 35 - 36 - Like similar libraries that manage state and data, you will need to wrap your 37 - app with `urql`'s `<Provider>`. This `<Provider>` holds the `Client` that is 38 - used to manage data, requests, the cache, and other things. It's the "heart" 39 - of `urql` and holds all of its core logic. 40 - 41 - This example creates a `Client`, passes it a GraphQL API's URL, and provides it 42 - using the `<Provider>`. 43 - 44 - ```jsx 45 - import { Provider, createClient } from 'urql'; 46 - 47 - const client = createClient({ 48 - url: 'http://localhost:4000/graphql', 49 - }); 50 - 51 - const YourApp = () => ( 52 - <Provider value={client}> 53 - {/* ... */} 54 - </Provider>; 55 - ); 56 - ``` 57 - 58 - > A tutorial on how to set up a `client` and `Provider` 59 - > is [available as screencast on egghead](https://egghead.io/lessons/graphql-set-up-an-urql-graphql-provider-in-react?pl=introduction-to-urql-a-react-graphql-client-faaa2bf5). 60 - 61 - Every component and query underneath the `<Provider>` in the tree now has access 62 - to the client and will call the client when it needs to execute GraphQL requests. 63 - 64 - The client accepts more options these can be found in the [API](<./api.md#Client(class)>) 65 - 66 - ## Writing queries 67 - 68 - To illustrate how this works, the next example will use `urql`'s `useQuery` 69 - to fetch some GraphQL data. 70 - 71 - ```jsx 72 - import React from 'react'; 73 - import { useQuery } from 'urql'; 74 - 75 - const getTodos = ` 76 - query GetTodos($limit: Int!) { 77 - todos(limit: $limit) { 78 - id 79 - text 80 - isDone 81 - } 82 - } 83 - `; 84 - 85 - const TodoList = ({ limit = 10 }) => { 86 - const [result] = useQuery({ 87 - query: getTodos, 88 - variables: { limit }, 89 - }); 90 - 91 - if (result.fetching) return 'Loading...'; 92 - if (result.error) return 'Oh no!'; 93 - 94 - return ( 95 - <ul> 96 - {result.data.todos.map(({ id, text }) => ( 97 - <li key={id}>{text}</li> 98 - ))} 99 - </ul> 100 - ); 101 - }; 102 - ``` 103 - 104 - When this hook is executed it will send the `query` and `variables` 105 - to your GraphQL API. Here we're using `fetching` to see whether the 106 - request is still being sent and is loading, `error` to see whether any 107 - errors have come back, `data` to get the result, and finally `extensions` 108 - to get any arbitrary extensions data the server may have optionally returned. 109 - 110 - Whenever the query or variables props change, the `useQuery` hook will 111 - send a new request and go back into the `fetching` state. 112 - 113 - The shape of the result include `data` and `error` which is rather similar 114 - to the response a GraphQL API sends back by default. However, the `error` 115 - is not the plural `errors`. `urql` wraps any network error or GraphQL 116 - errors in a `CombinedError` which is more convenient to handle and 117 - observe. 118 - 119 - [Read more about the result's API in the Architecture's Results section.](https://formidable.com/open-source/urql/docs/architecture#operation-results) 120 - 121 - > A tutorial on the `useQuery` hook is also available as a 122 - > [screencast on egghead](https://egghead.io/lessons/graphql-query-graphql-data-with-urql-using-react-hooks?pl=introduction-to-urql-a-react-graphql-client-faaa2bf5). 123 - 124 - ### Using graphql-tag 125 - 126 - You're not limited to just passing in strings as queries. You can also 127 - pass in a fully parsed AST in the form of `DocumentNode` instead. 128 - For this purpose you can use `graphql-tag`. 129 - 130 - This can be extremely helpful, since it enables syntax highlighting 131 - in some editors. It also can be used to preparse the GraphQL query 132 - using `babel-plugin-graphql-tag` or the included Webpack loader. 133 - 134 - You only have to make a small adjustment. Install `graphql-tag` and 135 - you can immediately write tagged template literals instead: 136 - 137 - ```jsx 138 - import React from 'react'; 139 - import gql from 'graphql-tag'; 140 - import { useQuery } from 'urql'; 141 - 142 - const getTodos = gql` 143 - query GetTodos($limit: Int!) { 144 - todos(limit: $limit) { 145 - id 146 - text 147 - isDone 148 - } 149 - } 150 - `; 151 - ``` 152 - 153 - Keep in mind that it makes sense to give your queries unique 154 - names. In this case we've chosen `GetTodos`, since we're simply 155 - listing out some `Todo`s. 156 - 157 - [Find out more about `graphql-tag` on their repository.](https://github.com/apollographql/graphql-tag) 158 - 159 - ## Writing mutations 160 - 161 - There always comes a point when an app will also need to send 162 - mutations to the GraphQL API. A mutation's response is very similar 163 - to a query's response, but often they're used in multiple use cases. 164 - 165 - Sometimes you care about the response, sometimes you don't, sometimes 166 - it might make more sense to imperatively use the mutations' result. 167 - 168 - To support all these use cases `urql`'s `useMutation` hook 169 - is quite flexible. The return value has object that 170 - contains the `executeMutation` method that accepts `variables` 171 - as its first argument. When called it will return a `Promise` with 172 - the mutations result. 173 - 174 - However, the `useMutation` hook will expose the result as well, like 175 - the `useQuery` hook exposes it, with a `fetching`, `data`, 176 - and an `error` property. 177 - 178 - Here's an example of an imperative use case where we create a todo. 179 - 180 - ```js 181 - import React, { Component } from 'react'; 182 - import { useMutation } from 'urql'; 183 - 184 - const addTodo = ` 185 - mutation AddTodo($text: String!) { 186 - addTodo(text: $text) { 187 - id 188 - text 189 - } 190 - } 191 - `; 192 - 193 - const TodoForm = () => { 194 - const [addTodoResult, addTodo] = useMutation(addTodo); 195 - if (addTodoResult.error) { 196 - return 'Oh no!'; 197 - } 198 - 199 - const add = () => { 200 - addTodo({ text: 'learn urql' }) 201 - .then(result => { 202 - // You can do something here or use the result object on the useMutation 203 - }) 204 - .catch(error => { 205 - // You can do something here if it throws 206 - }) 207 - } 208 - 209 - return ( 210 - <button onClick={add}> 211 - Add something! 212 - </button>; 213 - ); 214 - } 215 - ``` 216 - 217 - When using the `useMutation` hook we have the choice to use `.catch` or `.then` 218 - to manually look at our result or we can use the second argument to look at what 219 - the API has returned for said mutation. 220 - 221 - This `executeQuery` method accepts a second argument which is the [OperationContext](<./api.md#OperationContext(type)>) 222 - 223 - > A tutorial on the `useMutation` hook is also available as a 224 - > [screencast on egghead](https://egghead.io/lessons/graphql-write-a-graphql-mutation-using-react-hooks-with-urql?pl=introduction-to-urql-a-react-graphql-client-faaa2bf5). 225 - 226 - ## Refetching data 227 - 228 - `urql` will by default come with a simple "document" cache. Each query 229 - with variables that is requested from a GraphQL API, the result will be 230 - cached completely. When the same query and variables are requested again, 231 - `urql`'s default cache will then return the cached result. This 232 - result is also invalidated when a mutation with similar `__typename`s was 233 - sent. 234 - 235 - [You can find out more about the default caching behavior in the Basics' `cacheExchange` section.](https://formidable.com/open-source/urql/docs/basics#cacheexchange) 236 - 237 - Using `urql`'s default behavior this means we sometimes need a way to refetch 238 - data from the GraphQL API and skip the cache, if we need fresh data. 239 - 240 - The easiest way to always display up-to-date data is to set the `requestPolicy` 241 - to `'cache-and-network'`. Using this policy `urql` will first return a cached 242 - result if it has one, and subsequently it will send a new request to the API 243 - to get the up-to-date result. When `urql` is refreshing data in the background 244 - due to `cache-and-network` your result will also carry `stale: true` on its payload. 245 - 246 - A `requestPolicy` can be passed as a prop: 247 - 248 - ```jsx 249 - <Query query={q} requestPolicy="cache-and-network" />; 250 - 251 - /* or with hooks: */ 252 - 253 - useQuery({ query: q, requestPolicy: 'cache-and-network' }); 254 - ``` 255 - 256 - Including `'cache-and-network'` there are four request policies in total: 257 - 258 - - `cache-first`: The default policy. It doesn't send a request to the API when a result 259 - can be retrieved from the cache. 260 - - `cache-only`: It never sends a request and always uses the cached or an empty result. 261 - - `network-only`: This skips the cache entirely and always sends a request. 262 - - `cache-and-network`: As stated above, this returns the cached result and then also 263 - sends a request to the API. 264 - 265 - [You can find out more about how the default cache behaves when it receives these request policies in the Basics' `cacheExchange` section.](https://formidable.com/open-source/urql/docs/basics#request-policies) 266 - 267 - Next, we can take a look at how to use `'network-only'` to force a refetch 268 - imperatively. In our previous example this would come in handy to refresh the 269 - list of todos. 270 - 271 - ```jsx 272 - import React from 'react'; 273 - import { useQuery } from 'urql'; 274 - 275 - const getTodos = ` 276 - query GetTodos { 277 - todos(limit: 10) { 278 - id 279 - text 280 - isDone 281 - } 282 - } 283 - `; 284 - 285 - const TodoList = ({ limit = 10 }) => { 286 - const [{ fetching, data, error, extensions }, executeQuery] = useQuery({ 287 - query: getTodos, 288 - variables: { limit }, 289 - }); 290 - 291 - if (fetching) return 'Loading...'; 292 - if (error) return 'Oh no!'; 293 - 294 - return ( 295 - <div> 296 - <ul> 297 - {data.todos.map(({ id, text }) => ( 298 - <li key={id}>{text}</li> 299 - ))} 300 - </ul> 301 - <button onClick={() => executeQuery({ requestPolicy: 'network-only' })}> 302 - Refresh 303 - </button> 304 - </div> 305 - ); 306 - }; 307 - ``` 308 - 309 - As can be seen, the `useQuery` hook also expose an `executeQuery` method, which 310 - isn't unlike the `useMutation` hook with its `executeMutation` method. 311 - 312 - We can call this method to rerun the query and pass it a `requestPolicy` different. In this 313 - case we'll pass `'network-only'` which will skip the cache and make sure we actually refresh 314 - our todo list. 315 - 316 - This `executeQuery` method accepts everything that's available on an [OperationContext](<./api.md#OperationContext(type)>) 317 - 318 - ## Pausing queries 319 - 320 - Let's say our query needs a `userId` to correctly execute our query, we don't want to dispatch 321 - the query to receive an error in this scenario we can avoid. 322 - 323 - ```jsx 324 - import React from 'react'; 325 - import { useQuery } from 'urql'; 326 - 327 - const getUser = ` 328 - query getUser (id: $id) { 329 - user(id: $id) { 330 - id 331 - name 332 - } 333 - } 334 - `; 335 - 336 - const Profile = ({ userId }) => { 337 - const [{ data, fetching, error }] = useQuery({ 338 - query: getUser, 339 - variables: { id: userid }, 340 - pause: !userId, 341 - }); 342 - 343 - if (!userId) return 'Please select a "userId".'; 344 - if (fetching) return 'Loading...'; 345 - if (error) return 'Oh no!'; 346 - 347 - return <p>Welcome {data.user.me}</p>; 348 - }; 349 - ``` 350 - 351 - When the user now selects a userId pause will evaluate to `false` and 352 - the query will be executed with the new variable. 353 - 354 - ## Polling 355 - 356 - Your query can be passed an argument named `pollInterval`, this will ensure that your query 357 - is reexecuted every x ms. You'll have to make sure that your `requestPolicy` for these queries 358 - is `cache-and-network` or `network-only` else the data will never be refetched. 359 - 360 - ## More examples 361 - 362 - More examples on how to use `urql` 363 - [can be found in the repository's `examples/` folder](https://github.com/FormidableLabs/urql/tree/master/examples).
-546
docs/core/guides.md
··· 1 - --- 2 - title: Guides 3 - order: 4 4 - --- 5 - 6 - # Guides 7 - 8 - In `urql`, all queries and requests start as "operations". A stream 9 - of operations is piped through "exchanges" which handle any logic 10 - around resolving the operations, eventually sending a result back 11 - in a stream of "operation results". 12 - 13 - They're not unlike middleware in an HTTP server implementation like 14 - Express. They have the ability to forward operations to the next 15 - exchange and to return the stream of results from the next exchange. 16 - 17 - [You can read more about this structure in our "Architecture" section](https://formidable.com/open-source/urql/docs/architecture) 18 - 19 - These guides are not intended as best practices or specific instructions for 20 - writing exchanges. Rather they teach you how to get started on creating 21 - your own exchanges by learning how we write ours. 22 - 23 - ## Introduction 24 - 25 - All exchanges are written with [Wonka](https://wonka.kitten.sh/), a ligtweight iterable and 26 - observable streaming library. Wonka is used `urql` because its behavior 27 - is extremely predictable and built to be treeshakeable. 28 - 29 - [You can read more about how to use it on the Wonka site.](https://wonka.kitten.sh/basics/) 30 - 31 - The basic usage comes down to creating sources, observable-like 32 - functions that _stream_ values over time. 33 - 34 - They're similar to iterables or arrays in that they're used immutably 35 - and with utilities like `map` and `filter` that transform their output, 36 - but they work with values that come in asynchronously over time. 37 - 38 - These utilities are called "operators" and there's a lot of them to 39 - enable you to express any asynchronous or iterable behavior that 40 - you need. 41 - 42 - For instance you can convert an array into a Wonka source, 43 - then filter and map their content, delay their values by a 44 - certain timeout, and finally output the values by subscribing 45 - to the stream: 46 - 47 - ```js 48 - import { pipe, fromArray, delay, filter, map, subscribe } from 'wonka'; 49 - 50 - // pipe applies operators to sources, but operators are just 51 - // functions that accept a source and return a source 52 - const values = pipe( 53 - fromArray([1, 2, 3]), // this is the source 54 - map(x => x * 2), 55 - filter(x => x > 2), // a lot of operators look like Array methods 56 - delay(200) // this delays values by 200ms 57 - ); 58 - 59 - // you can use pipe as many times as you need 60 - const [unsubscribe] = pipe( 61 - values, 62 - // this is cancellable by calling unsubscribe 63 - subscribe(x => console.log(x)) 64 - ); 65 - ``` 66 - 67 - There are more operators, sources and sinks, which you'll see 68 - across these guides, but they're all documented on the 69 - [Wonka API Reference page](https://wonka.kitten.sh/api/). 70 - So it's easy to learn by example on this page, 71 - but you can also read more on how Wonka works over on its site. 72 - 73 - ## The Rules of Exchanges 74 - 75 - Before we jump into writing some exchanges, there are a couple of 76 - patterns and limitations that always remain the same when writing 77 - an exchange. 78 - 79 - ### Forward Operation Streams. Return OperationResult Streams. 80 - 81 - For reference, this is a basic template for an exchange: 82 - 83 - ```js 84 - const noopExchange = ({ client, forward }) => { 85 - return operation$ => { // <-- The ExchangeIO function 86 - const operationResult$ = forward(operations$); 87 - return operationResult$; 88 - }; 89 - }; 90 - ``` 91 - 92 - By convention, variables ending with `$` are streams. Each exchange's `ExchangeIO` function receives a stream containing an [`Operation`](https://formidable.com/open-source/urql/docs/api/#operation-type) object (`operation$`) and must return a stream of [`OperationResult`](https://formidable.com/open-source/urql/docs/api/#operationresult-type) object(s)(`operationResult$`). 93 - 94 - #### Forward and Return Composition 95 - 96 - When you create a client and pass it an array of exchanges, `urql` composes them left-to-right into a single exchange using [`composeExchanges`](https://github.com/FormidableLabs/urql/blob/master/src/exchanges/compose.ts). `composeExchanges` orchestrates stream forward and return between exchanges. Here's the `noopExchange` in context: 97 - 98 - ```js 99 - import { Client, dedupeExchange, fetchExchange } from 'urql'; 100 - 101 - const noopExchange = ({ client, forward }) => { 102 - return operation$ => { // <-- This exchange's ExchangeIO function 103 - // calling forward() here will call the next exchange's ExchangeIO function. 104 - // So before this, 105 - // urql creates an Operation object, places it in a stream (operation$), 106 - // and calls composeExchange's ExchangeIO function with it. 107 - // composeExchange forward()s the operation$ stream to dedupeExchange's ExchangeIO function. 108 - // dedupeExchange filters duplicates and forward()s the stream to noopExchange's ExchangeIO function. 109 - 110 - // Here, noopExchange forward()s the operation$ stream to fetchExchange's ExchangeIO function 111 - const operationResult$ = forward(operations$); 112 - 113 - // fetchExchange receives the operation$ stream, creates an OperationResult object, 114 - // and returns it in an operationResult$ stream. 115 - // finally, noopExchange returns the operationsResult$ stream to dedupeExchange's forward() call. 116 - 117 - return operationResult$; 118 - 119 - // After this, dedupExchange returns operationResult$ to composeExchange's forward() call. 120 - // urql client receives the operationResult$ from composeExchange and provides data to components. 121 - }; 122 - }; 123 - 124 - const client = new Client({ 125 - exchanges: [dedupeExchange, noopExchange, fetchExchange], 126 - }); 127 - ``` 128 - 129 - ### One operations stream only 130 - 131 - When writing an exchange we have to be careful not to "split" the stream 132 - into multiple ones by subscribing multiple times. 133 - 134 - Streams are lazy and immutable by default. Every time you use them, 135 - you create a new chain of streaming operators. 136 - 137 - Your `ExchangeIO` function receives an `operations$` stream, and you 138 - must be careful to either only use it once, or to _share_ its subscription. 139 - 140 - ```js 141 - import { pipe, filter, merge, share } from 'wonka'; 142 - 143 - // DON'T: split use operations$ twice 144 - ({ forward }) => operations$ => { // <-- The ExchangeIO function (inline) 145 - const queries = pipe( 146 - operations$, 147 - filter(op => op.operationName === 'query') 148 - ); 149 - const others = pipe( 150 - operations$, 151 - filter(op => op.operationName !== 'query') 152 - ); 153 - return forward(merge([queries, others])); 154 - }; 155 - 156 - // DO: share operations$ if you have to use it twice 157 - ({ forward }) => operations$ => { // <-- The ExchangeIO function (inline) 158 - const shared = pipe( 159 - operations$, 160 - share 161 - ); 162 - const queries = pipe( 163 - shared, 164 - filter(op => op.operationName === 'query') 165 - ); 166 - const others = pipe( 167 - shared, 168 - filter(op => op.operationName !== 'query') 169 - ); 170 - return forward(merge([queries, others])); 171 - }; 172 - 173 - // DO: use operations$ only once alternatively 174 - ({ forward }) => operations$ => // <-- The ExchangeIO function (inline) 175 - pipe( 176 - operations$, 177 - map(op => { 178 - if (op.operationName === 'query') { 179 - /* ... */ 180 - } else { 181 - /* ... */ 182 - } 183 - }), 184 - forward 185 - ); 186 - ``` 187 - 188 - So if you see the `operations$` stream twice in your exchange code, make sure to 189 - use Wonka's [`share`](https://wonka.kitten.sh/api/operators#share) operator, 190 - to share the underlying subscription between all your streams. 191 - 192 - ### Don't accidentally drop operations 193 - 194 - Typically the `operations$` stream will send you `query`, `mutation`, 195 - `subscription`, and `teardown`. There is no constraint for new operations 196 - to be added later on or a custom exchange adding new operations altogether. 197 - 198 - This means that you have to take "unknown" operations into account and 199 - not `filter` operations too aggressively. 200 - 201 - ```js 202 - import { pipe, filter, merge, share } from 'wonka'; 203 - 204 - // DON'T: drop unknown operations 205 - ({ forward }) => operations$ => { 206 - // This doesn't handle operations that aren't queries 207 - const queries = pipe( 208 - operations$, 209 - filter(op => op.operationName === 'query') 210 - ); 211 - return forward(queries); 212 - }; 213 - 214 - // DO: forward operations that you don't handle 215 - ({ forward }) => operations$ => { 216 - const shared = pipe( 217 - operations$, 218 - share 219 - ); 220 - const queries = pipe( 221 - shared, 222 - filter(op => op.operationName === 'query') 223 - ); 224 - const rest = pipe( 225 - shared, 226 - filter(op => op.operationName !== 'query') 227 - ); 228 - return forward(merge([queries, rest])); 229 - }; 230 - ``` 231 - 232 - If you group and or filter operations by what your exchange is handling, 233 - also make sure that you have a stream of operations that it's not handling, 234 - which you should also forward. 235 - 236 - ### Synchronous first, Asynchronous last 237 - 238 - By default exchanges and Wonka streams are as predictable as possible. 239 - Every operator in Wonka runs synchronously until you actually introduce 240 - asynchronicity. 241 - 242 - This may happen when you use a timing utility from Wonka, like 243 - [`delay`](https://wonka.kitten.sh/api/operators#delay) or [`throttle`](https://wonka.kitten.sh/api/operators#throttle) 244 - Or this could happen because your exchange inherently does something asynchronous, 245 - like fetch some data or use a promise. 246 - 247 - When you write exchanges, some will inevitably be asynchronous, if 248 - they're fetching results, performing authentication, or other tasks 249 - that you have to wait for. 250 - 251 - This can cause problems, because the behavior in `urql` is built 252 - to be _synchronous_ first. This helps us build our suspense mode, 253 - and it helps your components receive cached data on their initial 254 - mount without rerendering. 255 - 256 - This why **all exchanges should be ordered synchronous first and 257 - asynchronous last**. 258 - 259 - The default order of exchanges is: 260 - 261 - ```js 262 - import { dedupExchange, cacheExchange, fetchExchange } from 'wonka'; 263 - 264 - // Also exported as `defaultExchanges`: 265 - [dedupExchange, cacheExchange, fetchExchange]; 266 - ``` 267 - 268 - Both the `dedupExchange` and `cacheExchange` are completely 269 - synchronous and only the `fetchExchange` is asynchronous since 270 - it makes a `fetch` request and waits for a server response. 271 - 272 - When you're adding more exchanges you obviously have a reason 273 - to put them in a specific order. For instance, an authentication exchange 274 - needs to go before the `fetchExchange`. And a secondary cache would 275 - maybe go in front of the default cache exchange. 276 - 277 - But to ensure the correct behavior of suspense mode and 278 - the initialization of our hooks, it's vital to order your exchanges 279 - so that synchronous exchanges come first and asynchronous ones 280 - come last. 281 - 282 - ## Authentication 283 - 284 - Managing and refreshing tokens is a very common case in 285 - modern application development. In this part we'll build 286 - this exchange from scratch. 287 - 288 - > _Note:_ Setting up a full-scale authentication exchange would be 289 - > out of scope here. Instead this section teaches the basics on 290 - > how to wait for an asynchronous request to complete when 291 - > necessary before letting operations through. 292 - 293 - So let's start with a basic template for an exchange 294 - 295 - ```js 296 - import { pipe } from 'wonka'; 297 - 298 - export const refreshTokenExchange = ({ forward }) => { 299 - return operations$ => { 300 - return pipe( 301 - operations$, 302 - forward 303 - ); 304 - }; 305 - }; 306 - ``` 307 - 308 - As of now it enters the exchange and tells it to continue due 309 - to forward being invoked. So this is basically a noop exchange. 310 - 311 - Next up is writing some code that refreshes our token, so imagine 312 - the following method: 313 - 314 - > note that these methods should be replaced with your own logic. 315 - 316 - ```js 317 - const refreshToken = () => { 318 - return Promise.resolve( 319 - fetch('/refreshToken', { 320 - headers: { 321 - 'application-type': 'application/json', 322 - refreshToken: window.localStorage.get('refreshToken'), 323 - }, 324 - }) 325 - .then(res => res.json()) 326 - .then(res => { 327 - window.localStorage.setItem('token', res.data.token); 328 - return res.data.token; 329 - }) 330 - .catch(console.error) 331 - ); 332 - }; 333 - ``` 334 - 335 - Now that we have a way to refresh our token 336 - we can transform the previous exchange to handle the 337 - promise that the `refreshToken` function will return. 338 - 339 - ```js 340 - import { pipe, fromPromise, map } from 'wonka'; 341 - 342 - export const refreshTokenExchange = ({ forward }) => { 343 - return operations$ => { 344 - return pipe( 345 - operations$, 346 - pipe( 347 - fromPromise(refreshToken()), 348 - map(newToken => ({ ...op, context: { ...op.context, token: newToken } })) 349 - ); 350 - forward, 351 - ) 352 - } 353 - } 354 - ``` 355 - 356 - With this change our `refreshToken` function will be invoked every time this pipeline 357 - gets called. Since we have a nested pipe that takes a promise and enriches 358 - our operation with the new token. The `map` will trigger when `fromPromise` 359 - completes. 360 - 361 - > Here we see that we can alter our operation that finally gets 362 - > passed to the `fetchExchange` 363 - 364 - This can be made better by not triggering the refreshToken mechanism when 365 - the token is still valid, we don't want to block our exchange pipeline 366 - on every request. It's blocked since we are waiting from an async action 367 - in `fromPromise`. 368 - 369 - ```js 370 - import { pipe, fromPromise, map, mergeMap } from 'wonka'; 371 - 372 - const readToken = () => window.localStorage.getItem('token'); 373 - const isTokenExpired = () => { 374 - const token = readToken(); 375 - if (!token) return true; 376 - const { expired } = JSON.parse(window.atob(token)); 377 - return !!expired; 378 - } 379 - 380 - export const refreshTokenExchange = ({ forward }) => { 381 - return operations$ => { 382 - return pipe( 383 - operations$, 384 - mergeMap(op => { 385 - if (isTokenExpired()) { 386 - return pipe( 387 - fromPromise(refreshToken()), 388 - map(newToken => ({ ...op, context: { ...op.context, token: newToken } })) 389 - ); 390 - } else { 391 - return fromValue({ ...op, context: { ...op.context, token: readToken() } }) 392 - } 393 - }) 394 - forward, 395 - ) 396 - } 397 - } 398 - ``` 399 - 400 - We use `mergeMap` to see if our token is expired and return our previously 401 - made `pipe` that refreshes our token or use the still valid token. 402 - 403 - Now we face one last problem, what if we dispatch multiple queries 404 - while our token is invalid? This would mean that we invoke several 405 - instances of `refreshToken`, these would be redundant. 406 - Let's transform our exchange into a higher order function to solve this 407 - issue. 408 - 409 - ```js 410 - import { pipe, fromPromise, map, mergeMap, fromValue } from 'wonka'; 411 - 412 - export const refreshTokenExchange = () => { 413 - let promise; 414 - return ({ forward }) => { 415 - return operations$ => { 416 - return pipe( 417 - operations$, 418 - mergeMap(op => { 419 - if (isTokenExpired()) { 420 - return pipe( 421 - fromPromise(promise ? promise : promise = refreshToken()), 422 - map(newToken => { 423 - promise = undefined; 424 - return { ...op, context: { ...op.context, token: newToken } } 425 - }) 426 - ); 427 - } else { 428 - return fromValue({ ...op, context: { ...op.context, token: readToken() } }) 429 - } 430 - }) 431 - forward, 432 - // Inserting an operator here will make it run after the operation 433 - // has completed. 434 - ) 435 - } 436 - } 437 - } 438 - ``` 439 - 440 - All that's left to do is use your own brand new exchange 441 - by adding it into your exchanges array as `refreshTokenExchange()`. 442 - 443 - [Check out the full, working example in a CodeSandbox](https://codesandbox.io/s/refetch-token-exchange-t8b6g) 444 - to run the example you'll have to open the server template. 445 - 446 - [Server template](https://codesandbox.io/s/urql-issue-template-server-0ufyz) 447 - 448 - ## Adjusting an exchange 449 - 450 - Let's say the `fetchExchange` doesn't cut it for you and you want to be able to upload files, 451 - the first thing to do is go to the [urql exchanges](https://github.com/FormidableLabs/urql/tree/master/src/exchanges) 452 - find the exchange you want to change and copy it over. 453 - 454 - In our case we want to alter the `fetchExchange` to handle file uploads. 455 - To accomplish this we'll use an [external dependency](https://www.npmjs.com/package/extract-files) 456 - to extract the files from our variables. 457 - 458 - First things first, we'll need to check if the operation passed into our exchange 459 - contains any files. Here we'll need to alter somethings in the `createFetchSource`. 460 - 461 - ```js 462 - import { extractFiles } from 'extract-files'; 463 - 464 - ... 465 - const createFetchSource = (operation) => { 466 - return make(({next, complete}) => { 467 - const { context } = operation; 468 - const { clone, files } = extractFiles(operation.variables); 469 - const isFileUpload = !!files.size; 470 - }) 471 - } 472 - ... 473 - 474 - ``` 475 - 476 - Now we know when it's a file upload, this enables us to alter 477 - the request when this is true or false. 478 - When it's an upload we don't need to send our request as `application/json` 479 - but as `FormData`. 480 - 481 - ```js 482 - ... 483 - const isFileUpload = !!files.size; 484 - 485 - const extraOptions = 486 - typeof context.fetchOptions === 'function' 487 - ? context.fetchOptions() 488 - : context.fetchOptions || {}; 489 - 490 - const fetchOptions = { 491 - method: 'POST', 492 - ...extraOptions, 493 - }; 494 - 495 - if (isFileUpload) { 496 - fetchOptions.body = new FormData() 497 - 498 - fetchOptions.body.append( 499 - 'operations', 500 - JSON.stringify({ 501 - query: print(operation.query), 502 - variables: Object.assign({}, operation.variables), 503 - }), 504 - ) 505 - 506 - const map = {} 507 - let i = 0 508 - files.forEach(paths => { 509 - map[++i] = paths.map(path => `variables.${path}`) 510 - }); 511 - 512 - fetchOptions.body.append('map', JSON.stringify(map)); 513 - 514 - i = 0 515 - files.forEach((paths, file) => { 516 - fetchOptions.body.append(`${++i}`, file, file.name) 517 - }); 518 - } else { 519 - fetchOptions.headers['content-type'] = 'application/json'; 520 - fetchOptions.body = JSON.stringify({ 521 - query: print(operation.query), 522 - variables: operation.variables, 523 - }); 524 - } 525 - ... 526 - ``` 527 - 528 - This way we have altered the `fetchOptions` variable to include the 529 - `File` or `FileList` and send it to the server in the right format. 530 - 531 - The only thing left to do is to include this new exchange by doing: 532 - 533 - ```js 534 - import { createClient, dedupExchange, cacheExchange } from 'urql'; 535 - import { myFetchExchange } from './fetchExchange'; 536 - 537 - const client = createClient({ 538 - exchanges: [dedupExchange, cacheExchange, myFetchExchange], 539 - }); 540 - ``` 541 - 542 - Now we are all set to use our new `fetchExchange` to upload files as 543 - well as do normal `JSON` requests. 544 - 545 - [Client with uploadFetchExchange](https://codesandbox.io/s/urql-upload-client-d0ozh) 546 - [Server to go with client](https://codesandbox.io/s/urql-upload-server-p49r8)
-8
docs/core/index.md
··· 1 - --- 2 - title: Core 3 - order: 0 4 - --- 5 - 6 - # Test Core Index Page 7 - 8 - testing
-157
docs/core/urql-outside-react.md
··· 1 - --- 2 - title: Outside of React 3 - order: 5 4 - --- 5 - 6 - # urql Outside of React 7 - 8 - While `urql` is a GraphQL client that's mainly targeting React, apart from our React-specific APIs (i.e. the hooks and components), all other elements of the library function the same outside of a React context. In this guide, we'll take a look at using `urql` in environments other than React, including NodeJS and Vue. 9 - 10 - The most useful element for using `urql` outside of React is the `Client`, the library's central orchestrator. The `Client` handles all outgoing requests and incoming responses in `urql`, and comes along with a variety of methods to help you orchestrate communication with your GraphQL API. The most useful of these are the `execute*` methods (`executeQuery`, `executeMutation`, and `executeSubscription`). 11 - 12 - The `execute*` methods all accept an instance of a `GraphQLRequest`, which can be created by using `urql`'s `createRequest` function. `createRequest` will create a unique hash used to identify the GraphQL operation, which is used internally to track it for things like deduplication and caching. 13 - 14 - ```javascript 15 - // load dependencies 16 - require('isomorphic-fetch'); 17 - const { createClient, createRequest } = require('urql/core'); 18 - const gql = require('graphql-tag'); 19 - 20 - // create the urql Client 21 - const client = createClient({ 22 - url: 'https://graphbrainz.herokuapp.com/graphql', 23 - }); 24 - 25 - // define your GraphQL query 26 - const query = gql` 27 - query SearchArtist($search: String!) { 28 - search { 29 - artists(query: $search, first: 1) { 30 - edges { 31 - node { 32 - name 33 - country 34 - releases(first: 10) { 35 - edges { 36 - node { 37 - title 38 - date 39 - media { 40 - format 41 - trackCount 42 - tracks { 43 - title 44 - } 45 - } 46 - } 47 - } 48 - } 49 - } 50 - } 51 - } 52 - } 53 - } 54 - `; 55 - 56 - // create the urql request object 57 - const request = createRequest(query, { search: 'Courtney Barnett' }); 58 - ``` 59 - 60 - Once you've created your request, all that's left is to execute it! When you execute a query, mutation, or subscription, `urql` returns you [a `wonka` Source](https://wonka.kitten.sh/api/sources) containing the `data` and `error` states of that request. You can use [`wonka`'s Sinks](https://wonka.kitten.sh/api/sinks) to access these like so: 61 - 62 - ```javascript 63 - const { pipe, subscribe } = require('wonka'); 64 - 65 - pipe( 66 - client.executeQuery(request), 67 - subscribe(({ data, error }) => { 68 - if (error) { 69 - console.log('Error', error); 70 - } 71 - 72 - console.log('Data', data); 73 - }) 74 - ); 75 - ``` 76 - 77 - Awesome, we've successfully executed a GraphQL query on the server in NodeJS! In practice, this is more useful for writing small Node scripts or a simple CLI for interacting with a GraphQL API. But it does show off the flexibility that `urql`'s `Client` gives us! 78 - 79 - See the full CodeSandbox example [here](https://codesandbox.io/s/urql-node-1jhj8). 80 - 81 - ## Doing More with the Client 82 - 83 - The `Client` comes with more methods than just the `execute*` methods. While the use cases for these additional methods are fairly specific to `urql`'s implementation, they do give you more direct control over the `Client`. Let's say, for example, that you want to reexecute an operation on a predefined interval (a form of long polling). This is fairly trivial to do by calling `Client.reexecuteOperation` with the request's `Operation` object. To create the `Operation` object, use `Client.createOperation`, passing the operation type as the first argument (`"query"`, `"mutation"`, `"subscription"`, or `"teardown"`). 84 - 85 - ```javascript 86 - require('isomorphic-fetch'); 87 - const { createClient, createRequest } = require('urql/core'); 88 - const gql = require('graphql-tag'); 89 - const { pipe, subscribe, interval, take } = require('wonka'); 90 - 91 - const client = createClient({ 92 - url: 'https://graphbrainz.herokuapp.com/graphql', 93 - }); 94 - 95 - const query = gql` 96 - query SearchArtist($search: String!) { 97 - search { 98 - artists(query: $search, first: 1) { 99 - edges { 100 - node { 101 - name 102 - country 103 - releases(first: 10) { 104 - edges { 105 - node { 106 - title 107 - date 108 - media { 109 - format 110 - trackCount 111 - tracks { 112 - title 113 - } 114 - } 115 - } 116 - } 117 - } 118 - } 119 - } 120 - } 121 - } 122 - } 123 - `; 124 - 125 - const request = createRequest(query, { search: 'Courtney Barnett' }); 126 - 127 - pipe( 128 - client.executeQuery(request), 129 - subscribe(({ data, error }) => { 130 - if (error) { 131 - console.log('Error', error); 132 - } 133 - 134 - console.log('Data', data); 135 - }) 136 - ); 137 - 138 - pipe( 139 - interval(2000), 140 - take(10), 141 - subscribe(n => { 142 - // Call client.createRequest operation to create the Operation object. 143 - // The optional third argument is a partial operation context, which allows you 144 - // to change the operation's request policy or fetch options. 145 - const operation = client.createRequestOperation('query', request, { 146 - requestPolicy: 'network-only', 147 - }); 148 - 149 - // Reexecute the operation. Active subscribers will receive updates. 150 - client.reexecuteOperation(operation); 151 - }) 152 - ); 153 - ``` 154 - 155 - Awesome, we've got basic long polling working, where our `Client` is reexecuting the initial request every 2 seconds. By passing `{ requestPolicy: 'network-only' }` as the third argument to `reexecuteOperation`, we ensure that the `Client` issues a new request to the GraphQL API every time rather than pulling results from the cache. You'll notice in this example we use `wonka`'s `take` operator to limit the number of times we call `reexecuteOperation` – in true long polling, you wouldn't do this. 156 - 157 - See the full CodeSanbox example [here](https://codesandbox.io/s/urql-node-polling-erkwe).
+8
docs/graphcache/README.md
··· 1 + --- 2 + title: Graphcache 3 + order: 4 4 + --- 5 + 6 + # Graphcache 7 + 8 + <!-- TODO: Link to subpages -->
-73
docs/graphcache/architecture.md
··· 1 - --- 2 - title: Architecture 3 - order: 1 4 - --- 5 - 6 - # Architecture 7 - 8 - This cache implementation builds on the concept of normalisation, 9 - we can use the naive implementation where we just refetch when we see 10 - matches in `__typename` but in big connected applications this can 11 - cause overhead and a lot of unnecessary fetches. 12 - 13 - We need an approach where we can say, this is our response let's now 14 - change the affected query data. 15 - 16 - Let's look at a higher level how we tackle this. 17 - 18 - ## Incoming request 19 - 20 - We have visited the exchanges before this one and now have our `OperationRequest`. 21 - The first thing we should do is check what this operation entails, it would be useless 22 - to start looking for a `subscription` or `mutation` in our cache. 23 - 24 - In the event of it being a `subscription` we just go forward to the next exchange, 25 - when we are dealing with a `mutation` we check if there are any [optimistic updates](./optimistic.md) 26 - we have to deal with, if this is the case the callback implemented by the library consumer 27 - will be executed with given variables. 28 - 29 - In the event of a `query` there's a bit more that comes into play, first we check if the given 30 - fields are present in our cache this starts with checking if there's a link from `Query` to our 31 - requested field. We will keep traversing this querystring until we decide the query is incomplete, 32 - partial or complete, partial is only supported when the cache has your [introspection schema](./schema.md), 33 - during this traversal we will call your [resolvers](./resolvers.md) if this is needed. 34 - 35 - When a query is incomplete we forward to the next exchange. Partial will result in it being returned as 36 - partial data to the client AND a new `network-only` request to be fired to ensure the partial data is 37 - populated. Complete will result depending in a return unless your requestPolicy is `cache-and-network` 38 - this will return data and dispatch a request for your data to be refreshed. 39 - 40 - > If you are curious this traversal happens in src/operation/query.ts and the decisions about the query 41 - > are made in src/exchange.ts 42 - 43 - ## Incoming response 44 - 45 - The request has completed and we have a response, let's tackle this like the request and look at it 46 - by type. 47 - 48 - When we get a response (or trigger) from a `subscription` classically the only thing that would happen 49 - is your callback would fire. In this cache implementation you get the power to supply an [update](./updates.md) 50 - function, this can be used to update the relevant query but even without an update function this will try 51 - and look at the returned `__typename` and `identifier` if present and try to update a cache entry with it if 52 - present. This is a powerful tool to keep your data updated, imagine we have a `subscription` that listens 53 - to updates on our todos, if this query includes the identifier for that entity this will update the fields 54 - where needed. When this is listening on somehting more complex like we want to add it to a list then you should 55 - resort to the update function. 56 - 57 - > Note that identifiers can be overwritten by the [keys config](./keys.md) 58 - 59 - We've covered `subscription`, let's move on to `mutation`. Initially when we get a response the exchange will 60 - check if there's an optimistic entry for this `mutation` if this is the case then we delete that entry since 61 - we'll get the actual response (even if it is an error). The process of a `mutation` is very analogue to that 62 - of a `subscription`, we check for an update function and if it's not there we try to automatically update the 63 - data. 64 - 65 - When we get a `query` response we just write this to the cache, nothing more. 66 - 67 - Internally we keep track of dependant queries so we can notify them that their data was updated, this way our 68 - React components can decide to rerender. 69 - 70 - ## Concluding 71 - 72 - This does not only limit the amount of requests and so on, it also allows the application user to 73 - have a better experience. Nobody likes to have a series of loading Spinners because data got invalidated.
+1 -1
docs/graphcache/help.md
··· 1 1 --- 2 2 title: Help 3 - order: 2 3 + order: 6 4 4 --- 5 5 6 6 # Help!
-82
docs/graphcache/introspectionQuery.js
··· 1 - // This removes some fields: 2 - // - defaultValues for inputs 3 - // - deprecationReasons and isDeprecated 4 - // - descriptions 5 - // - directives 6 - // 7 - // This results in a 10-15% size decrease, this could be more 8 - // but then buildClientSchema won't execute 9 - // simple_schema was the test and is updated to the trimmed schema. 10 - export default ` 11 - query IntrospectionQuery { 12 - __schema { 13 - queryType { name } 14 - mutationType { name } 15 - subscriptionType { name } 16 - types { 17 - ...FullType 18 - } 19 - } 20 - } 21 - fragment FullType on __Type { 22 - kind 23 - name 24 - fields(includeDeprecated: true) { 25 - name 26 - args { 27 - ...InputValue 28 - } 29 - type { 30 - ...TypeRef 31 - } 32 - } 33 - inputFields { 34 - ...InputValue 35 - } 36 - interfaces { 37 - ...TypeRef 38 - } 39 - enumValues(includeDeprecated: true) { 40 - name 41 - } 42 - possibleTypes { 43 - ...TypeRef 44 - } 45 - } 46 - fragment InputValue on __InputValue { 47 - name 48 - type { ...TypeRef } 49 - } 50 - fragment TypeRef on __Type { 51 - kind 52 - name 53 - ofType { 54 - kind 55 - name 56 - ofType { 57 - kind 58 - name 59 - ofType { 60 - kind 61 - name 62 - ofType { 63 - kind 64 - name 65 - ofType { 66 - kind 67 - name 68 - ofType { 69 - kind 70 - name 71 - ofType { 72 - kind 73 - name 74 - } 75 - } 76 - } 77 - } 78 - } 79 - } 80 - } 81 - } 82 - `;
-40
docs/graphcache/keys.md
··· 1 - --- 2 - title: Keys 3 - order: 3 4 - --- 5 - 6 - # Keys 7 - 8 - When resolving entities the graph cache will try to look at the entity 9 - and use `id` or `_id` to identify them. 10 - 11 - The `keys` property allows us to intervene in this behavior by pointing 12 - to another location on the entity or by pre-/post-fixing it. 13 - 14 - Let's look at an example. Say we have a set of todos each with a `__typename` 15 - of `Todo`, but instead of identifying on `id` or `_id` we want to identify 16 - each record by its `name`. 17 - 18 - ```js 19 - const myGraphCache = cacheExchange({ 20 - keys: { 21 - Todo: data => data.name, 22 - }, 23 - }); 24 - ``` 25 - 26 - Now when we query or write a Todo it will use `name` to identify the record 27 - in the cache. All other records will be resolved the traditional way. 28 - 29 - In the same way, you could say that a Todo meant only for admin access is 30 - prefixed with `admin`. 31 - 32 - ```js 33 - const myGraphCache = cacheExchange({ 34 - keys: { 35 - Todo: data => (data.isAdminOnly ? `admin-${data.name}` : data.name), 36 - }, 37 - }); 38 - ``` 39 - 40 - [Back](../README.md)
+72
docs/graphcache/normalized-caching.md
··· 1 + --- 2 + title: Normalized Caching 3 + order: 1 4 + --- 5 + 6 + # Normalized Caching 7 + 8 + In urql we have the option to utilize a normalized caching mechanism, 9 + this opens up a world of new features ranging from automatic updates 10 + to offline capabilities. 11 + 12 + Instead of storing a query by its `operationKey` we'll store the entities 13 + we get back and normalize them so for instance: 14 + 15 + ```js 16 + todo: { 17 + __typename: 'Todo', 18 + id: 1, 19 + title: 'implement graphcache', 20 + author: { 21 + __typename: 'Author', 22 + id: 1, 23 + name: 'urql-team', 24 + } 25 + } 26 + ``` 27 + 28 + will become 29 + 30 + ```js 31 + { 32 + "Todo:1.title": 'implement graphcache', 33 + "Todo:1.author": 1, 34 + "Author:1.name": 'urql-team', 35 + } 36 + ``` 37 + 38 + This allows us to for instance take the result of an update mutation to 39 + `Todo:1` and automatcally update altered properties, this also allows us to 40 + reuse entities. We will always try to create a key with the `__typename` and the 41 + `id` or `_id` whichever is present. 42 + 43 + The custom `keys` property comes into play when we don't have an `id` or `_id`, 44 + in this scenario graphcache will warn us and ask to create a key for said entity. 45 + 46 + Let's look at an example. Say we have a set of todos each with a `__typename` 47 + of `Todo`, but instead of identifying on `id` or `_id` we want to identify 48 + each record by its `name`. 49 + 50 + ```js 51 + import { cacheExchange } from '@urql/exchange-graphcache'; 52 + 53 + const myGraphCache = cacheExchange({ 54 + keys: { 55 + Todo: data => data.name, 56 + }, 57 + }); 58 + ``` 59 + 60 + Now when we query or write a Todo it will use `name` to identify the record 61 + in the cache. All other records will be resolved the traditional way. 62 + 63 + In the same way, you could say that a Todo meant only for admin access is 64 + prefixed with `admin`. 65 + 66 + ```js 67 + const myGraphCache = cacheExchange({ 68 + keys: { 69 + Todo: data => (data.isAdminOnly ? `admin-${data.name}` : data.name), 70 + }, 71 + }); 72 + ```
-42
docs/graphcache/optimistic.md
··· 1 - --- 2 - title: Optimistic 3 - order: 3 4 - --- 5 - 6 - # Optimistic 7 - 8 - Let's say we want to work offline or we don't want to wait for 9 - responses from the server. 10 - 11 - Optimistic responses can be a great solution to this problem. Optimisitc 12 - responses are simply a mapping of the name of a mutation to a function. 13 - This function gets three 14 - arguments: 15 - 16 - - `variables` – The variables used to execute the mutation. 17 - - `cache` – The cache we've already seen in [resolvers](./resolvers.md) and 18 - [updates](./updates.md). This can be used to get a certain entity/property 19 - from the cache. 20 - - `info` – Contains the used fragments and field arguments. 21 - 22 - Let's see an example. 23 - 24 - ```js 25 - const myGraphCache = cacheExchange({ 26 - optimistic: { 27 - addTodo: (variables, cache, info) => { 28 - console.log(variables); // { id: '1', text: 'optimistic' } 29 - return { 30 - ...variables, 31 - __typename: 'Todo', // We still have to let the cache know what entity we are on. 32 - }; 33 - }, 34 - }, 35 - }); 36 - ``` 37 - 38 - Now that we return `variables` our `Todo:1` will be updated to have 39 - the new `text` property. In our cache this will form a layer above 40 - the property. When a response from the server comes in this layer 41 - will be removed and the response from the server will be used to 42 - replace the original properties.
+3 -3
docs/graphcache/populate.md docs/advanced/auto-populate-mutations.md
··· 1 1 --- 2 - title: Populate Exchange 3 - order: 4 2 + title: Auto-populate Mutations 3 + order: 3 4 4 --- 5 5 6 - # Populate Exchange 6 + # Automatically populating Mutations 7 7 8 8 `populate` is an exchange for auto-populating fields in your mutations. 9 9
+7 -7
docs/graphcache/resolvers.md docs/graphcache/computed-queries.md
··· 1 1 --- 2 - title: Resolvers 3 - order: 5 2 + title: Computed-queries 3 + order: 2 4 4 --- 5 5 6 6 # Resolvers ··· 32 32 see more about this [below](#cache.resolve). 33 33 - `info` – This contains the fragments used in the query and the field arguments in the query. 34 34 35 - ## `cache.resolve` 35 + ## Cache parameter 36 + 37 + ### resolve 36 38 37 39 The `cache.resolve` method is used to get links and property values from the cache. 38 40 Our cache methods have three arguments: ··· 100 102 101 103 will do the trick. 102 104 103 - ## `cache.readQuery` 105 + ### Reading a query 104 106 105 107 Another method the cache allows is to let you read a full query, this method 106 108 accepts an object of `query` and optionally `variables`. ··· 111 113 112 114 This way we'll get the stored data for the `TodosQuery` with given variables. 113 115 114 - ## `cache.readFragment` 116 + ### Reading a fragment 115 117 116 118 The store allows the user to also read a fragment for a certain entity, this function 117 119 accepts a `fragment` and an `id`. This looks like the following. ··· 199 201 200 202 With inwards merging the nodes will be in this order: `[1, 2, ..., 89, 99]` 201 203 And with outwards merging: `[..., 89, 99, 1, 2, ...]` 202 - 203 - [Back](../README.md)
+6 -13
docs/graphcache/schema.md docs/graphcache/schema-awareness.md
··· 1 1 --- 2 - title: Schema 3 - order: 6 2 + title: Schema-awareness 3 + order: 4 4 4 --- 5 5 6 - # Schema 6 + # Schema-awareness 7 7 8 8 As mentioned in the docs we allow for the schema to be passed 9 - to the `cacheExchange` this allows for partial results in deterministic 9 + to the `cacheExchange` this allows for partial results and deterministic 10 10 fragment matching. 11 11 With deterministic fragment matching we mean that if you use an interface 12 - or a union we will be 100% sure you're allowed to do so. 12 + or a union we will be 100% sure you're allowed to do so, we'll check if the 13 + type you request can actually be returned from this union/interface. 13 14 14 15 But how do you get this schema? Well let's consider some steps, first 15 16 make sure `introspection` is turned on on your server. This is very crucial ··· 19 20 20 21 ```js 21 22 // import a fetch library for node. 22 - import introspectionQuery from '@urql/exchange-graphcache/docs/introspectionQuery'; 23 - // or 24 23 import { getIntrospectionQuery } from 'graphql'; 25 24 26 25 fetch('http://localhost:3000/graphql', { ··· 50 49 51 50 const cache = cacheExchange({ schema }); 52 51 ``` 53 - 54 - This should do the trick, it's also possible to just open your playground 55 - and manually execute the introspection with the query found [here](./introspectionQuery.js) 56 - this one is the smallest representation. 57 - 58 - > Note that our query can be unsuited for your case, it's best to resort to the one offered from graphql itself in that case
+42 -4
docs/graphcache/updates.md docs/graphcache/custom-updates.md
··· 1 1 --- 2 - title: Updates 3 - order: 7 2 + title: Custom-updates 3 + order: 3 4 4 --- 5 5 6 - # Updates 6 + # Custom Updates (on Mutations or Subscriptions), 7 + 8 + ## Data-updates 7 9 8 10 When the cache receives a response it will try and do its best to 9 11 incorporate that response into the current cache. However, for adding and ··· 131 133 132 134 Next time we hit the query for agendas this will be refetched. 133 135 134 - [Back](../README.md) 136 + ## Optimistic updates 137 + 138 + Let's say we want to work offline or we don't want to wait for 139 + responses from the server. 140 + 141 + Optimistic responses can be a great solution to this problem. Optimisitc 142 + responses are simply a mapping of the name of a mutation to a function. 143 + This function gets three 144 + arguments: 145 + 146 + - `variables` – The variables used to execute the mutation. 147 + - `cache` – The cache we've already seen in [resolvers](./resolvers.md) and 148 + [updates](./updates.md). This can be used to get a certain entity/property 149 + from the cache. 150 + - `info` – Contains the used fragments and field arguments. 151 + 152 + Let's see an example. 153 + 154 + ```js 155 + const myGraphCache = cacheExchange({ 156 + optimistic: { 157 + addTodo: (variables, cache, info) => { 158 + console.log(variables); // { id: '1', text: 'optimistic' } 159 + return { 160 + ...variables, 161 + __typename: 'Todo', // We still have to let the cache know what entity we are on. 162 + }; 163 + }, 164 + }, 165 + }); 166 + ``` 167 + 168 + Now that we return `variables` our `Todo:1` will be updated to have 169 + the new `text` property. In our cache this will form a layer above 170 + the property. When a response from the server comes in this layer 171 + will be removed and the response from the server will be used to 172 + replace the original properties.
+8
docs/showcase.md
··· 1 + --- 2 + title: Showcase 3 + order: 5 4 + --- 5 + 6 + # Showcase 7 + 8 +
+1 -1
packages/site/package.json
··· 41 41 "react-router-ga": "^1.0.0", 42 42 "react-scroll": "^1.7.15", 43 43 "react-static": "^7.2.3", 44 - "react-static-plugin-md-pages": "^0.1.0", 44 + "react-static-plugin-md-pages": "^0.1.1", 45 45 "styled-components": "^5.0.1" 46 46 }, 47 47 "devDependencies": {
+60 -12
packages/site/src/components/mdx.js
··· 36 36 background: ${p => p.theme.colors.passiveBg}; 37 37 color: ${p => p.theme.colors.code}; 38 38 font-family: ${p => p.theme.fonts.code}; 39 - font-size: ${p => p.theme.fontSizes.code}; 39 + font-size: ${p => p.theme.fontSizes.small}; 40 40 border-radius: ${p => p.theme.spacing.xs}; 41 41 42 42 display: inline-block; ··· 47 47 margin: 0; 48 48 `; 49 49 50 + const ImageWrapper = styled.div` 51 + margin: ${p => p.theme.spacing.md} 0; 52 + border: 1px solid ${p => p.theme.colors.border}; 53 + border-radius: ${p => p.theme.spacing.xs}; 54 + background: ${p => p.theme.colors.bg}; 55 + 56 + display: flex; 57 + flex-direction: column; 58 + 59 + & > img { 60 + padding: ${p => p.theme.spacing.md}; 61 + align-self: center; 62 + max-height: 40vh; 63 + } 64 + `; 65 + 66 + const ImageAlt = styled.span.attrs(() => ({ 67 + 'aria-hidden': true, // This is just duplicating alt 68 + }))` 69 + display: block; 70 + padding: ${p => p.theme.spacing.xs} ${p => p.theme.spacing.sm}; 71 + border-top: 1px solid ${p => p.theme.colors.border}; 72 + background: ${p => p.theme.colors.passiveBg}; 73 + font-size: ${p => p.theme.fontSizes.small}; 74 + `; 75 + 76 + const Image = ({ alt, src }) => ( 77 + <ImageWrapper> 78 + <img alt={alt} src={src} /> 79 + <ImageAlt>{alt}</ImageAlt> 80 + </ImageWrapper> 81 + ); 82 + 50 83 const HighlightCode = ({ className = '', children }) => { 51 84 const language = getLanguage(className); 52 85 ··· 58 91 language={language} 59 92 > 60 93 {({ className, style, tokens, getLineProps, getTokenProps }) => ( 61 - <Pre className={className} style={style}> 62 - <Code> 63 - {tokens.map((line, i) => ( 64 - <div {...getLineProps({ line, key: i })}> 65 - {line.map((token, key) => ( 66 - <span {...getTokenProps({ token, key })} /> 67 - ))} 68 - </div> 69 - ))} 70 - </Code> 71 - </Pre> 94 + <Code 95 + style={{ ...style, backgroundColor: 'none' }} 96 + className={className} 97 + > 98 + {tokens.map((line, i) => ( 99 + <div {...getLineProps({ line, key: i })}> 100 + {line.map((token, key) => ( 101 + <span {...getTokenProps({ token, key })} /> 102 + ))} 103 + </div> 104 + ))} 105 + </Code> 72 106 )} 73 107 </Highlight> 74 108 ); 75 109 }; 76 110 111 + const Blockquote = styled.blockquote` 112 + margin: ${p => p.theme.spacing.md} 0; 113 + padding: 0 0 0 ${p => p.theme.spacing.md}; 114 + border-left: 0.5rem solid ${p => p.theme.colors.border}; 115 + font-size: ${p => p.theme.fontSizes.small}; 116 + 117 + & > * { 118 + margin: ${p => p.theme.spacing.sm} 0; 119 + } 120 + `; 121 + 77 122 const components = { 123 + pre: Pre, 124 + img: Image, 125 + blockquote: Blockquote, 78 126 inlineCode: InlineCode, 79 127 code: HighlightCode, 80 128 };
+9 -2
packages/site/src/components/navigation.js
··· 19 19 position: fixed; 20 20 left: 0; 21 21 top: 0; 22 + bottom: 0; 22 23 `; 23 24 24 25 export const SidebarWrapper = styled.aside` 25 26 position: fixed; 27 + bottom: 0; 28 + top: ${p => p.theme.layout.header}; 29 + -webkit-overflow-scrolling: touch; 30 + overflow-y: scroll; 31 + 26 32 display: flex; 27 33 flex-direction: column; 28 34 z-index: 1; ··· 34 40 background: ${p => p.theme.colors.bg}; 35 41 line-height: ${p => p.theme.lineHeights.body}; 36 42 font-size: ${p => p.theme.fontSizes.small}; 43 + 37 44 @media ${({ theme }) => theme.media.sm} { 38 45 width: ${p => p.theme.layout.sidebar}; 39 46 } ··· 41 48 42 49 export const SidebarNavItem = styled(Link)` 43 50 display: block; 44 - margin-bottom: ${p => p.theme.spacing.xs}; 51 + margin: ${p => p.theme.spacing.xs} 0; 45 52 color: ${p => p.theme.colors.accent}; 46 53 font-weight: ${p => p.theme.fontWeights.heading}; 47 54 text-decoration: none; ··· 50 57 51 58 export const SidebarNavSubItemWrapper = styled.div` 52 59 padding-left: ${p => p.theme.spacing.sm}; 53 - margin-bottom: ${p => p.theme.spacing.sm}; 54 60 border-left: 1px solid ${p => p.theme.colors.activeBorder}; 61 + margin-bottom: ${p => p.theme.spacing.xs}; 55 62 `; 56 63 57 64 export const SidebarNavSubItem = styled(Link)`
+12 -12
packages/site/src/components/sidebar.js
··· 1 - import React, { Fragment, useMemo, useState } from 'react'; 2 - import PropTypes from 'prop-types'; 1 + import React, { Fragment, useMemo } from 'react'; 2 + import styled from 'styled-components'; 3 3 import { useLocation } from 'react-router-dom'; 4 - import styled, { css } from 'styled-components'; 5 4 import * as path from 'path'; 6 5 7 6 import { useMarkdownTree, useMarkdownPage } from 'react-static-plugin-md-pages'; ··· 15 14 SideBarStripes, 16 15 } from './navigation'; 17 16 18 - import { mediaSizes } from '../styles/theme'; 19 - 20 17 import logoSidebar from '../assets/sidebar-badge.svg'; 21 18 22 19 const HeroLogo = styled.img.attrs(() => ({ ··· 35 32 const ContentWrapper = styled.div` 36 33 display: flex; 37 34 flex-direction: column; 38 - padding: ${p => p.theme.spacing.xs} 0; 35 + padding-top: ${p => p.theme.spacing.xs}; 36 + padding-bottom: ${p => p.theme.spacing.lg}; 39 37 `; 40 38 41 39 const relative = (from, to) => { 42 40 if (!from || !to) return null; 43 - const pathname = path.relative(path.dirname(from), to); 41 + let pathname = path.relative(path.dirname(from), to); 42 + if (from.endsWith('/')) pathname = '../' + pathname; 44 43 return { pathname }; 45 44 }; 46 45 47 46 const Sidebar = ({ sidebarOpen }) => { 47 + const { pathname } = useLocation(); 48 48 const currentPage = useMarkdownPage(); 49 49 const tree = useMarkdownTree(); 50 50 ··· 56 56 return tree.children.map(page => { 57 57 return ( 58 58 <Fragment key={page.key}> 59 - <SidebarNavItem to={relative(currentPage.path, page.path)}> 59 + <SidebarNavItem to={relative(pathname, page.path)}> 60 60 {page.frontmatter.title} 61 61 </SidebarNavItem> 62 62 63 - {page.children && ( 63 + {page.children && page.children.length ? ( 64 64 <SidebarNavSubItemWrapper> 65 65 {page.children.map(childPage => ( 66 66 <SidebarNavSubItem 67 - to={relative(currentPage.path, childPage.path)} 67 + to={relative(pathname, childPage.path)} 68 68 key={childPage.key} 69 69 > 70 70 {childPage.frontmatter.title} 71 71 </SidebarNavSubItem> 72 72 ))} 73 73 </SidebarNavSubItemWrapper> 74 - )} 74 + ) : null} 75 75 </Fragment> 76 76 ); 77 77 }); 78 - }, [tree, currentPage]); 78 + }, [currentPage, tree, pathname]); 79 79 80 80 return ( 81 81 <SidebarContainer hidden={!sidebarOpen}>
+1 -3
packages/site/src/screens/docs/index.js
··· 1 - import React, { forwardRef, useState, useRef } from 'react'; 1 + import React, { useState } from 'react'; 2 2 import styled from 'styled-components'; 3 3 import PropTypes from 'prop-types'; 4 4 import { withRouteData } from 'react-static'; 5 - import { Link } from 'react-router-dom'; 6 5 7 6 import Article from './article'; 8 7 import Header from './header'; ··· 10 9 11 10 import burger from '../../assets/burger.svg'; 12 11 import closeButton from '../../assets/close.svg'; 13 - import logoFormidableDark from '../../assets/logo_formidable_dark.svg'; 14 12 15 13 const Container = styled.div` 16 14 display: flex;
+1 -1
packages/site/src/styles/theme.js
··· 44 44 export const fontSizes = { 45 45 small: '0.9em', 46 46 body: '1.8rem', 47 - code: '0.9em', 47 + code: '0.8em', 48 48 h1: '3.45em', 49 49 h2: '2.11em', 50 50 h3: '1.64em',
+1 -4
packages/site/static.config.js
··· 11 11 { 12 12 location: '../../docs', 13 13 template: './src/screens/docs', 14 - order: { 15 - core: 0, 16 - graphcache: 1, 17 - }, 14 + pathPrefix: 'docs', 18 15 }, 19 16 ], 20 17
+4 -4
yarn.lock
··· 8721 8721 dependencies: 8722 8722 object-is "^1.0.2" 8723 8723 8724 - react-static-plugin-md-pages@^0.1.0: 8725 - version "0.1.0" 8726 - resolved "https://registry.yarnpkg.com/react-static-plugin-md-pages/-/react-static-plugin-md-pages-0.1.0.tgz#2e6ff6e8900b42e284008cc6ab8af9675ab95df7" 8727 - integrity sha512-xmtgf7PfChjcsVtq0ifvVaQgem1NRRADerUiK41gK6b/c9gQkDE0tE5YL5PTfKaHePFfk2g58HQUsl8c4MO6rw== 8724 + react-static-plugin-md-pages@^0.1.1: 8725 + version "0.1.1" 8726 + resolved "https://registry.yarnpkg.com/react-static-plugin-md-pages/-/react-static-plugin-md-pages-0.1.1.tgz#7cb19dad439eb81e820d17f48e11a948b248bb93" 8727 + integrity sha512-g374YtRDgPK6dIMRXaRbFuOYwXta/kASUVAY3rc+z7/YIiCqyjkS1Dkt/pyBAaTaXGsrXI4wZcIUxgN5Z0Fz3w== 8728 8728 dependencies: 8729 8729 "@mdx-js/mdx" "^1.5.5" 8730 8730 "@mdx-js/react" "^1.5.5"