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.

(docs) - Expand "Core" page in the documentation (#1419)

authored by

Phil Pluckthun and committed by
GitHub
7ab81d0c 3933e2d8

+207 -51
+207 -51
docs/basics/core.md
··· 13 13 all exports of our `@urql/core` core library. This means that if we want to use `urql`'s `Client` 14 14 imperatively or with Node.js we'd use `@urql/core`'s utilities or the `Client` directly. 15 15 16 - ## Getting started 16 + In other words, if we're using framework bindings then writing `import { Client } from "@urql/vue"` 17 + for instance is the same as `import { Client } from "@urql/core"`. 18 + This means that we can use the core utilities and exports that are shared between all bindings 19 + directly or install `@urql/core` separately. We can even use `@urql/core` directly without any 20 + framework bindings. 17 21 18 - Installing `@urql/core` is quick and no other packages are immediately necessary. 22 + ## Installation 23 + 24 + As we said above, if we are using bindings then those will already have installed `@urql/core` as 25 + they depend on it. They also all re-export all exports from `@urql/core`, so we can use those no 26 + matter which bindings we've installed. However, it's also possible to explicitly install 27 + `@urql/core` or use it standalone, e.g. in a Node.js environment. 19 28 20 29 ```sh 21 30 yarn add @urql/core graphql ··· 23 32 npm install --save @urql/core graphql 24 33 ``` 25 34 26 - ### One-off Queries and Mutations 27 - 28 - When you're using `urql` to send one-off queries or mutations — rather than in full framework code, 29 - where updates are important — it's common to convert the streams that we get to promises. The 30 - `client.query` and `client.mutation` methods have a shortcut to do just that. 35 + Since all bindings and all exchanges depend on `@urql/core`, we may sometimes run into problems 36 + where the package manager installs _two versions_ of `@urql/core`, which is a duplication problem. 37 + This can cause type errors in TypeScript or cause some parts of our application to bundle two 38 + different versions of the package or use slightly different utilities. We can fix this by 39 + deduplicating our dependencies. 31 40 32 - ```js 33 - const QUERY = ` 34 - query Test($id: ID!) { 35 - getUser(id: $id) { 36 - id 37 - name 38 - } 39 - } 40 - `; 41 - 42 - client 43 - .query(QUERY, { id: 'test' }) 44 - .toPromise() 45 - .then(result => { 46 - console.log(result); // { data: ... } 47 - }); 41 + ```sh 42 + npx yarn-deduplicate && yarn 43 + # or 44 + npm dedupe 48 45 ``` 49 46 50 - This may be useful when we don't plan on cancelling queries or we don't care about future updates to 51 - this data and are just looking to query a result once. 52 - 53 - Similarly there's a way to read data from the cache synchronously, provided that the cache has 54 - received a result for a given query before. The `Client` has a `readQuery` method which is a 55 - shortcut for just that. 56 - 57 - ```js 58 - const QUERY = ` 59 - query Test($id: ID!) { 60 - getUser(id: $id) { 61 - id 62 - name 63 - } 64 - } 65 - `; 66 - 67 - const result = client.readQuery(QUERY, { id: 'test' }); 68 - 69 - result; // null or { data: ... } 70 - ``` 71 - 72 - Since the streams in `urql` operate synchronously, internally this method subscribes to 73 - `client.executeQuery` and unsubscribes immediately. If a result is available in the cache it will be 74 - resolved synchronously prior to the unsubscribe. If not, the query is cancelled and no request will be sent to the GraphQL API. 75 - 76 - ### gql 47 + ## GraphQL Tags 77 48 78 49 A notable utility function is the `gql` tagged template literal function, which is a drop-in 79 50 replacement for `graphql-tag`, if you're coming from other GraphQL clients. 80 51 81 - Wherever `urql` accepts a query document, you may either pass a string or a `DocumentNode`. `gql` is 52 + Wherever `urql` accepts a query document, we can either pass a string or a `DocumentNode`. `gql` is 82 53 a utility that allows a `DocumentNode` to be created directly, and others to be interpolated into 83 54 it, which is useful for fragments for instance. This function will often also mark GraphQL documents 84 55 for syntax highlighting in most code editors. ··· 139 110 ${TodoFragment} 140 111 `; 141 112 ``` 113 + 114 + This will all look familiar when coming from the `graphql-tag` package. The functionality is 115 + identical and the output is approximately the same. The two packages are also intercompatible. 116 + However, one small change that `@urql/core`'s implementation makes is that your fragment names don't 117 + have to be globally unique, since it's possible to create some one-off fragments every now and then. 118 + It also pre-generates a "hash key" for the `DocumentNode` which is what `urql` does anyway, thus 119 + avoiding some extra work compared to when the `graphql-tag` package is used with `urql`. 120 + 121 + ## Using the `urql` Client 122 + 123 + The `Client` is the main "hub" and store for everything that `urql` does. It is used by all 124 + framework bindings and from the other pages in the "Basics" section we can see that creating a 125 + `Client` comes up across all bindings and use-cases for `urql`. 126 + 127 + [Read more about the `Client` and `urql`'s architecture on the "Architecture" 128 + page.](../architecture.md) 129 + 130 + ### Setting up the `Client` 131 + 132 + The `@urql/core` package exports a function called `createClient` which we can use to 133 + create the GraphQL client. This central `Client` manages all of our GraphQL requests and results. 134 + 135 + ```js 136 + import { createClient } from 'urql'; 137 + 138 + const client = createClient({ 139 + url: 'http://localhost:3000/graphql', 140 + }); 141 + ``` 142 + 143 + At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started. 144 + 145 + Another common option is `fetchOptions`. This option allows us to customize the options that will be 146 + passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object or 147 + a function returning an options object. 148 + 149 + In the following example we'll add a token to each `fetch` request that our `Client` sends to our 150 + GraphQL API. 151 + 152 + ```js 153 + const client = createClient({ 154 + url: 'http://localhost:3000/graphql', 155 + fetchOptions: () => { 156 + const token = getToken(); 157 + return { 158 + headers: { authorization: token ? `Bearer ${token}` : '' }, 159 + }; 160 + }, 161 + }); 162 + ``` 163 + 164 + ### The `Client`s options 165 + 166 + As we've seen above, the most important option for the `Client` is `url`, since it won't work 167 + without it. However, another important option on the `Client` is the `exchanges` option. 168 + 169 + This option passes a list of exchanges to the `Client`, which tell it how to execute our requests 170 + and how to cache data in a certain order. By default this will be populated with the list of 171 + `defaultExchanges`. 172 + 173 + ```js 174 + import { createClient, defaultExchanges } from 'urql'; 175 + 176 + const client = createClient({ 177 + url: 'http://localhost:3000/graphql', 178 + // the default: 179 + exchanges: defaultExchanges, 180 + // the same as: 181 + exchanges: [dedupExchange, cacheExchange, fetchExchange] 182 + }); 183 + ``` 184 + 185 + Later, [in the "Advanced" section](../advanced/README.md) we'll see many more features that `urql` 186 + supports by adding new exchanges to this list. On [the "Architecture" page](../architecture.md) 187 + we'll also learn more about what exchanges are and why they exist. 188 + 189 + For now it's sufficient for us to know that our requests are executed using the logic in the 190 + exchanges in order. First, the `dedupExchange` deduplicates requests if we send the same queries 191 + twice, the `cacheExchange` implements the default "document caching" behaviour (as we'll learn about 192 + on the ["Document Caching"](./document-caching.md) page), and lastly the `fetchExchange` is 193 + responsible for sending our requests to our GraphQL API. 194 + 195 + ### One-off Queries and Mutations 196 + 197 + When you're using `urql` to send one-off queries or mutations — rather than in full framework code, 198 + where updates are important — it's common to convert the streams that we get to promises. The 199 + `client.query` and `client.mutation` methods have a shortcut to do just that. 200 + 201 + ```js 202 + const QUERY = ` 203 + query Test($id: ID!) { 204 + getUser(id: $id) { 205 + id 206 + name 207 + } 208 + } 209 + `; 210 + 211 + client 212 + .query(QUERY, { id: 'test' }) 213 + .toPromise() 214 + .then(result => { 215 + console.log(result); // { data: ... } 216 + }); 217 + ``` 218 + 219 + In the above example we're executing a query on the client, are passing some variables and are 220 + calling the `toPromise()` method on the return value to execute the request immediately and get the 221 + result as a promise. This may be useful when we don't plan on cancelling queries or we don't 222 + care about future updates to this data and are just looking to query a result once. 223 + 224 + The same can be done for mutations by calling the `client.mutation` method instead of the 225 + `client.query` method. 226 + 227 + Similarly there's a way to read data from the cache synchronously, provided that the cache has 228 + received a result for a given query before. The `Client` has a `readQuery` method which is a 229 + shortcut for just that. 230 + 231 + ```js 232 + const QUERY = ` 233 + query Test($id: ID!) { 234 + getUser(id: $id) { 235 + id 236 + name 237 + } 238 + } 239 + `; 240 + 241 + const result = client.readQuery(QUERY, { id: 'test' }); 242 + 243 + result; // null or { data: ... } 244 + ``` 245 + 246 + In the above example we call `readQuery` and receive a result immediately. This result will be 247 + `null` if the `cacheExchange` doesn't have any results cached for the given query. 248 + 249 + ### Subscribing to Results 250 + 251 + GraphQL Clients are by their nature "reactive", meaning that when we execute a query, we expect to 252 + get future results for this query. [On the "Document Caching" page](./document-caching.md) we'll 253 + learn how mutations can invalidate results in the cache. This process (and others just like it) can 254 + cause our query to be refetched. 255 + 256 + In essence, if we're subscribing to results rather than using a promise, like we've seen above, then 257 + we're able to see future changes for our query's results. If a mutation causes a query to be 258 + refetched from our API in the background then we'll see a new result. If we execute a query 259 + somewhere else then we'll get notified of the new API result as well, as long as we're subscribed. 260 + 261 + ```js 262 + import { pipe, subscribe } from 'wonka'; 263 + 264 + const QUERY = ` 265 + query Test($id: ID!) { 266 + getUser(id: $id) { 267 + id 268 + name 269 + } 270 + } 271 + `; 272 + 273 + const { unsubscribe } = pipe( 274 + client.query(QUERY, { id: 'test' }), 275 + subscribe(result => { 276 + console.log(result); // { data: ... } 277 + }) 278 + ); 279 + ``` 280 + 281 + This code example is similar to the one before. However, instead of sending a one-off query, we're 282 + subscribing to the query. Internally, this causes the `Client` to do the exact same, but the 283 + subscription means that our callback may be called repeatedly. We may get future results as well as 284 + the first one. 285 + 286 + This also works synchronously. As we've seen before `client.readQuery` can give us a result 287 + immediately if our cache already has a result for the given query. The same principle applies here! 288 + Our callback will be called synchronously if the cache already has a result. 289 + 290 + Once we're not interested in any results anymore we need to clean up after ourselves by calling 291 + `unsubscribe`. This stops the subscription and makes sure that the `Client` doesn't actively update 292 + the query anymore or refetches it. We can think of this pattern as being very similar to events or 293 + event hubs. 294 + 295 + We're using [the Wonka library for our streams](https://wonka.kitten.sh/basics/background) which 296 + we'll learn more about [on the "Architecture" page](./architecture.md). But we can think of this as 297 + React's effects being called over time, or as `window.addEventListener`. 142 298 143 299 ## Common Utilities in Core 144 300