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) - Add documentation for @urql/vue (#1165)

* Add styling for depth-3 legend items

* Add Vue section to "Getting Started" page

* Add API docs for @urql/vue

* Add Vue section to "Queries" page

* Update "Comparison" page

Also updates the React Suspense client-side note, since it's been
fixed in previous versions.

* Add Vue section to "Mutations"

* Add Vue section to "Subscriptions"

* Add sections on Vue Suspense and SSR

* Remove "Reading on" headings from Mutations page

authored by

Phil Pluckthun and committed by
GitHub
a71bfea4 f3cf0c96

+871 -20
+57
docs/advanced/server-side-rendering.md
··· 253 253 254 254 When this does seem like the appropriate solution any component wrapped with `withUrqlClient` will receive the `resetUrqlClient` 255 255 property, when invoked this will create a new top-level client and reset all prior operations. 256 + 257 + ## Vue Suspense 258 + 259 + In Vue 3 a [new feature was introduced](https://vuedose.tips/go-async-in-vue-3-with-suspense/) that 260 + natively allows components to suspend while data is loading, which works universally on the server 261 + and on the client, where a replacement loading template is rendered on a parent while data is 262 + loading. 263 + 264 + We've previously seen how we can change our usage of `useQuery`'s `PromiseLike` result to [make use 265 + of Vue Suspense on the "Queries" page.](../basics/queries.md#vue-suspense) 266 + 267 + Any component's `setup()` function can be updated to instead be an `async setup()` function, in 268 + other words, to return a `Promise` instead of directly returning its data. This means that we can 269 + update any `setup()` function to make use of Suspense. 270 + 271 + On the server-side we can then use `@vue/server-renderer`'s `renderToString`, which will return a 272 + `Promise` that resolves when all suspense-related loading is completed. 273 + 274 + ```jsx 275 + import { createSSRApp } = from 'vue' 276 + import { renderToString } from '@vue/server-renderer'; 277 + 278 + import urql, { 279 + createClient, 280 + dedupExchange, 281 + cacheExchange, 282 + fetchExchange, 283 + ssrExchange 284 + } from '@urql/vue'; 285 + 286 + const handleRequest = async (req, res) => { 287 + // This is where we'll put our root component 288 + const app = createSSRApp(Root) 289 + 290 + // NOTE: All we care about here is that the SSR Exchange is included 291 + const ssr = ssrExchange({ isClient: false }); 292 + app.use(urql, { 293 + exchanges: [dedupExchange, cacheExchange, ssr, fetchExchange] 294 + }); 295 + 296 + const markup = await renderToString(app); 297 + 298 + res.status(200).send(` 299 + <html> 300 + <body> 301 + <div id="root">${markup}</div> 302 + <script> 303 + window.__URQL_DATA__ = JSON.parse(${data}); 304 + </script> 305 + </body> 306 + </html> 307 + `); 308 + }; 309 + ``` 310 + 311 + This effectively renders our Vue app on the server-side and provides the client-side data for 312 + rehydration that we've set up in the above [SSR Exchange section](#the-ssr-exchange) to use.
+72
docs/advanced/subscriptions.md
··· 168 168 new messages come in, we will append them to the list of previous 169 169 messages. 170 170 171 + [Read more about the `useSubscription` API in the API docs for it.](../api/urql.md#usesubscription) 172 + 171 173 ## Svelte 172 174 173 175 The `subscription` function in `@urql/svelte` comes with a similar API to `query`, which [we've ··· 223 225 As we can see, the `$result.data` is being updated and transformed by the `handleSubscription` 224 226 function. This works over time, so as new messages come in, we will append them to 225 227 the list of previous messages. 228 + 229 + [Read more about the `subscription` API in the API docs for it.](../api/svelte.md#subscription) 230 + 231 + ## Vue 232 + 233 + The `useSubscription` API is very similar to `useQuery`, which [we've learned about in 234 + the "Queries" page in the "Basics" section.](../basics/queries.md#vue) 235 + 236 + Its usage is extremely similar in that it accepts options, which may contain `query` and 237 + `variables`. However, it also accepts a second argument, which is a reducer function, similar to 238 + what you would pass to `Array.prototype.reduce`. 239 + 240 + It receives the previous set of data that this function has returned or `undefined`. 241 + As the second argument, it receives the event that has come in from the subscription. 242 + You can use this to accumulate the data over time, which is useful for a 243 + list for example. 244 + 245 + In the following example, we create a subscription that informs us of 246 + new messages. We will concatenate the incoming messages so that we 247 + can display all messages that have come in over the subscription across 248 + events. 249 + 250 + ```jsx 251 + <template> 252 + <div v-if="error"> 253 + Oh no... {{error}} 254 + </div> 255 + <div v-else> 256 + <ul v-if="data"> 257 + <li v-for="msg in data">{{ msg.from }}: "{{ msg.text }}"</li> 258 + </ul> 259 + </div> 260 + </template> 261 + 262 + <script> 263 + import { useSubscription } from '@urql/vue'; 264 + 265 + export default { 266 + setup() { 267 + const handleSubscription = (messages = [], response) => { 268 + return [response.newMessages, ...messages]; 269 + }; 270 + 271 + const result = useSubscription({ 272 + query: ` 273 + subscription MessageSub { 274 + newMessages { 275 + id 276 + from 277 + text 278 + } 279 + } 280 + ` 281 + }, handleSubscription) 282 + 283 + return { 284 + data: result.data, 285 + error: result.error, 286 + }; 287 + } 288 + }; 289 + </script> 290 + ``` 291 + 292 + As we can see, the `result.data` is being updated and transformed by 293 + the `handleSubscription` function. This works over time, so as 294 + new messages come in, we will append them to the list of previous 295 + messages. 296 + 297 + [Read more about the `useSubscription` API in the API docs for it.](../api/vue.md#usesubscription) 226 298 227 299 ## One-off Subscriptions 228 300
+119
docs/api/vue.md
··· 1 + --- 2 + title: '@urql/vue' 3 + order: 3 4 + --- 5 + 6 + # Vue API 7 + 8 + ## useQuery 9 + 10 + Accepts a single required options object as an input with the following properties: 11 + 12 + | Prop | Type | Description | 13 + | --------------- | ------------------------ | -------------------------------------------------------------------------------------------------------- | 14 + | `query` | `string \| DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | 15 + | `variables` | `?object` | The variables to be used with the GraphQL request. | 16 + | `requestPolicy` | `?RequestPolicy` | An optional [request policy](./core.md#requestpolicy) that should be used specifying the cache strategy. | 17 + | `pause` | `?boolean` | A boolean flag instructing [execution to be paused](../basics/queries.md#pausing-usequery). | 18 + | `pollInterval` | `?number` | Every `pollInterval` milliseconds the query will be reexecuted. | 19 + | `context` | `?object` | Holds the contextual information for the query. | 20 + 21 + Each of these inputs may also be [reactive](https://v3.vuejs.org/api/refs-api.html) (e.g. a `ref`) 22 + and are allowed to change over time which will issue a new query. 23 + 24 + This function returns an object with the shape of an [`OperationResult`](./core.md#operationresult) 25 + with an added `fetching` property, indicating whether the query is currently being fetched and an 26 + `isPaused` property which will indicate whether `useQuery` is currently paused and won't 27 + automatically start querying. 28 + 29 + All of the properties on this result object are also marked as 30 + [reactive](https://v3.vuejs.org/api/refs-api.html) using `ref` and will update accordingly as the 31 + query is executed. 32 + 33 + The result furthermore carries several utility methods: 34 + 35 + | Method | Description | 36 + | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 37 + | `pause()` | This will pause automatic querying, which is equivalent to setting `pause.value = true` | 38 + | `resume()` | This will resume a paused automatic querying, which is equivalent to setting `pause.value = false` | 39 + | `executeQuery(opts)` | This will execute a new query with the given partial [`Partial<OperationContext>`](./core.md#operationcontext) regardless of whether the query is currently paused or not. This also returns the result object again for chaining. | 40 + 41 + Furthermore the returned result object of `useQuery` is also a `PromiseLike`, which allows you to 42 + take advantage of [Vue 3's experimental Suspense feature.](https://vuedose.tips/go-async-in-vue-3-with-suspense/) 43 + When the promise is used, e.g. you `await useQuery(...)` then the `PromiseLike` will only resolve 44 + once a result from the API is available. 45 + 46 + [Read more about how to use the `useQuery` API on the "Queries" page.](../basics/queries.md#vue) 47 + 48 + ## useMutation 49 + 50 + Accepts a single `query` argument of type `string | DocumentNode` and returns a result object with 51 + the shape of an [`OperationResult`](./core.md#operationresult) with an added `fetching` property. 52 + 53 + All of the properties on this result object are also marked as 54 + [reactive](https://v3.vuejs.org/api/refs-api.html) using `ref` and will update accordingly as the 55 + mutation is executed. 56 + 57 + The object also carries a special `executeMutation` method, which accepts variables and optionally a 58 + [`Partial<OperationContext>`](./core.md#operationcontext) and may be used to start executing a 59 + mutation. It returns a `Promise` resolving to an [`OperationResult`](./core.md#operationresult) 60 + 61 + [Read more about how to use the `useMutation` API on the "Mutations" 62 + page.](../basics/mutations.md#vue) 63 + 64 + ## useSubscription 65 + 66 + Accepts a single required options object as an input with the following properties: 67 + 68 + | Prop | Type | Description | 69 + | ----------- | ------------------------ | ------------------------------------------------------------------------------------------- | 70 + | `query` | `string \| DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | 71 + | `variables` | `?object` | The variables to be used with the GraphQL request. | 72 + | `pause` | `?boolean` | A boolean flag instructing [execution to be paused](../basics/queries.md#pausing-usequery). | 73 + | `context` | `?object` | Holds the contextual information for the subscription. | 74 + 75 + Each of these inputs may also be [reactive](https://v3.vuejs.org/api/refs-api.html) (e.g. a `ref`) 76 + and are allowed to change over time which will issue a new query. 77 + 78 + `useSubscription` also optionally accepts a second argument, which may be a handler function with 79 + a type signature of: 80 + 81 + ```js 82 + type SubscriptionHandler<T, R> = (previousData: R | undefined, data: T) => R; 83 + ``` 84 + 85 + This function will be called with the previous data (or `undefined`) and the new data that's 86 + incoming from a subscription event, and may be used to "reduce" the data over time, altering the 87 + value of `result.data`. 88 + 89 + This function returns an object with the shape of an [`OperationResult`](./core.md#operationresult) 90 + with an added `fetching` property, indicating whether the subscription is currently running and an 91 + `isPaused` property which will indicate whether `useSubscription` is currently paused. 92 + 93 + All of the properties on this result object are also marked as 94 + [reactive](https://v3.vuejs.org/api/refs-api.html) using `ref` and will update accordingly as the 95 + query is executed. 96 + 97 + The result furthermore carries several utility methods: 98 + 99 + | Method | Description | 100 + | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 101 + | `pause()` | This will pause the subscription, which is equivalent to setting `pause.value = true` | 102 + | `resume()` | This will resume the subscription, which is equivalent to setting `pause.value = false` | 103 + | `executeSubscription(opts)` | This will start a new subscription with the given partial [`Partial<OperationContext>`](./core.md#operationcontext) regardless of whether the subscription is currently paused or not. This also returns the result object again for chaining. | 104 + 105 + [Read more about how to use the `useSubscription` API on the "Subscriptions" 106 + page.](../advanced/subscriptions.md#vue) 107 + 108 + ## Context API 109 + 110 + In Vue the [`Client`](./core.md#client) is provided either to your app or to a parent component of a 111 + given subtree and is then subsequently injected whenever one of the above composition functions is 112 + used. 113 + 114 + You can manually retrieve the `Client` in your component by calling `useClient`. Symmetrically you 115 + can provide the `Client` from any of your components using the `provideClient` function. 116 + Alternatively, `@urql/vue` also has a default export of a [Vue Plugin function](https://v3.vuejs.org/guide/plugins.html#using-a-plugin). 117 + 118 + Both `provideClient` and the plugin function either accept an [instance of 119 + `Client`](./core.md#client) or the same options that `createClient` accepts as inputs.
+108
docs/basics/getting-started.md
··· 196 196 ``` 197 197 198 198 [On the next page we'll learn about executing "Queries".](./queries.md#svelte) 199 + 200 + ## Vue 201 + 202 + This "Getting Started" guide covers how to install and set up `urql` and provide a `Client` for 203 + Vue. The `@urql/vue` package, which provides bindings for Vue, doesn't fundamentally 204 + function differently from `@urql/preact`, or `urql` and uses the same [Core Package and 205 + `Client`](../concepts/core-package.md). 206 + 207 + The `@urql/vue` bindings have been written with [Vue 208 + 3](https://github.com/vuejs/vue-next/releases/tag/v3.0.0) in mind and use Vue's newer [Composition 209 + API](https://v3.vuejs.org/guide/composition-api-introduction.html). This gives the `@urql/vue` 210 + bindings capabilities to be more easily integrated into your existing `setup()` functions. 211 + 212 + ### Installation 213 + 214 + Installing `@urql/vue` is quick and no other packages are immediately necessary. 215 + 216 + ```sh 217 + yarn add @urql/vue graphql 218 + # or 219 + npm install --save @urql/vue graphql 220 + ``` 221 + 222 + Most libraries related to GraphQL also need the `graphql` package to be installed as a peer 223 + dependency, so that they can adapt to your specific versioning requirements. That's why we'll need 224 + to install `graphql` alongside `@urql/vue`. 225 + 226 + Both the `@urql/vue` and `graphql` packages follow [semantic versioning](https://semver.org) and 227 + all `@urql/vue` packages will define a range of compatible versions of `graphql`. Watch out 228 + for breaking changes in the future however, in which case your package manager may warn you about 229 + `graphql` being out of the defined peer dependency range. 230 + 231 + ### Setting up the `Client` 232 + 233 + The `@urql/vue` package exports a method called `createClient` which we can use to create 234 + the GraphQL client. This central `Client` manages all of our GraphQL requests and results. 235 + 236 + ```js 237 + import { createClient } from '@urql/vue'; 238 + 239 + const client = createClient({ 240 + url: 'http://localhost:3000/graphql', 241 + }); 242 + ``` 243 + 244 + At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started. 245 + 246 + Another common option is `fetchOptions`. This option allows us to customize the options that will be 247 + passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object or 248 + a function returning an options object. 249 + 250 + In the following example we'll add a token to each `fetch` request that our `Client` sends to our 251 + GraphQL API. 252 + 253 + ```js 254 + const client = createClient({ 255 + url: 'http://localhost:3000/graphql', 256 + fetchOptions: () => { 257 + const token = getToken(); 258 + return { 259 + headers: { authorization: token ? `Bearer ${token}` : '' }, 260 + }; 261 + }, 262 + }); 263 + ``` 264 + 265 + ### Providing the `Client` 266 + 267 + To make use of the `Client` in Vue we will have to provide from a parent component to its child 268 + components. This will share one `Client` with the rest of our app. In `@urql/vue` there are two 269 + different ways to achieve this. 270 + 271 + The first method is to use `@urql/vue`'s `provideClient` function. This must be called in any of 272 + your parent components and accepts either a `Client` directly or just the options that you'd pass to 273 + `createClient`. 274 + 275 + ```html 276 + <script> 277 + import { createClient, provideClient } from '@urql/vue'; 278 + 279 + const client = createClient({ 280 + url: 'http://localhost:3000/graphql', 281 + }); 282 + 283 + provideClient(client); 284 + </script> 285 + ``` 286 + 287 + Alternatively we may use the exported `install` function and treat `@urql/vue` as a plugin by 288 + importing its default export and using it [as a plugin](https://v3.vuejs.org/guide/plugins.html#using-a-plugin). 289 + 290 + ```js 291 + import { createApp } from 'vue'; 292 + import Root from './App.vue'; 293 + import urql from '@urql/vue'; 294 + 295 + const app = createApp(Root); 296 + 297 + app.use(urql, { 298 + url: 'http://localhost:3000/graphql', 299 + }); 300 + 301 + app.mount('#app'); 302 + ``` 303 + 304 + The plugin also accepts `createClient`'s options or a `Client` as its inputs. 305 + 306 + [On the next page we'll learn about executing "Queries".](./queries.md#vue)
+127 -6
docs/basics/mutations.md
··· 39 39 40 40 Similar to the `useQuery` output, `useMutation` returns a tuple. The first item in the tuple again 41 41 contains `fetching`, `error`, and `data` — it's identical since this is a common pattern of how 42 - `urql` presents _operation results_. 42 + `urql` presents [operation results](../api/core.md#operationresult). 43 43 44 44 Unlike the `useQuery` hook, the `useMutation` hook doesn't execute automatically. At this point in 45 45 our example, no mutation will be performed. To execute our mutation we instead have to call the ··· 60 60 updateTodo(variables).then(result => { 61 61 // The result is almost identical to `updateTodoResult` with the exception 62 62 // of `result.fetching` not being set. 63 + // It is an OperationResult. 63 64 }); 64 65 }; 65 66 }; 66 67 ``` 67 68 68 - This is useful when your UI has to display progress or results on the mutation, and the returned 69 + The result is useful when your UI has to display progress on the mutation, and the returned 69 70 promise is particularly useful when you're adding side-effects that run after the mutation has 70 71 completed. 71 72 ··· 93 94 }; 94 95 ``` 95 96 96 - ### Reading on 97 - 98 - There are some more tricks we can use with `useMutation`. [Read more about its API in the API docs for 99 - it.](../api/urql.md#usemutation) 97 + There are some more tricks we can use with `useMutation`.<br /> 98 + [Read more about its API in the API docs for it.](../api/urql.md#usemutation) 100 99 101 100 [On the next page we'll learn about "Document Caching", `urql`'s default caching 102 101 mechanism.](./document-caching.md) ··· 226 225 227 226 [On the next page we'll learn about "Document Caching", `urql`'s default caching 228 227 mechanism.](./document-caching.md) 228 + 229 + ## Vue 230 + 231 + This guide covers how to send mutations in Vue using `@urql/vue`'s `useMutation` API. 232 + The `useMutation` function isn't dissimilar from `useQuery` but is triggered manually and accepts 233 + only a `DocumentNode` or `string` as an input. 234 + 235 + ### Sending a mutation 236 + 237 + Let's again pick up an example with an imaginary GraphQL API for todo items, and dive into an 238 + example! We'll set up a mutation that _updates_ a todo item's title. 239 + 240 + ```js 241 + import { useMutation } from '@urql/vue'; 242 + 243 + export default { 244 + setup() { 245 + const { executeMutation: updateTodo } = useMutation(` 246 + mutation ($id: ID!, $title: String!) { 247 + updateTodo (id: $id, title: $title) { 248 + id 249 + title 250 + } 251 + } 252 + `); 253 + 254 + return { updateTodo }; 255 + }, 256 + }; 257 + ``` 258 + 259 + Similar to the `useQuery` output, `useMutation` returns a result object, which reflects the data of 260 + an executed mutation. That means it'll contain the familiar `fetching`, `error`, and `data` 261 + properties — it's identical since this is a common pattern of how `urql` 262 + presents [operation results](../api/core.md#operationresult). 263 + 264 + Unlike the `useQuery` hook, the `useMutation` hook doesn't execute automatically. At this point in 265 + our example, no mutation will be performed. To execute our mutation we instead have to call the 266 + `executeMutation` method on the result with some variables. 267 + 268 + ### Using the mutation result 269 + 270 + When calling our `updateTodo` function we have two ways of getting to the result as it comes back 271 + from our API. We can either use the result itself, since all properties related to the last 272 + [operation result](../api/core.md#operationresult) are marked as [reactive 273 + ](https://v3.vuejs.org/guide/reactivity-fundamentals.html) — or we can use the promise that the 274 + `executeMutation` method returns when it's called: 275 + 276 + ```js 277 + import { useMutation } from '@urql/vue'; 278 + 279 + export default { 280 + setup() { 281 + const updateTodoResult = useMutation(` 282 + mutation ($id: ID!, $title: String!) { 283 + updateTodo (id: $id, title: $title) { 284 + id 285 + title 286 + } 287 + } 288 + `); 289 + 290 + return { 291 + updateTodo(id, title) { 292 + const variables = { id, title: title || '' }; 293 + updateTodoResult(variables).then(result => { 294 + // The result is almost identical to `updateTodoResult` with the exception 295 + // of `result.fetching` not being set and its properties not being reactive. 296 + // It is an OperationResult. 297 + }); 298 + }, 299 + }; 300 + }, 301 + }; 302 + ``` 303 + 304 + The reactive result that `useMutation` returns is useful when your UI has to display progress or 305 + results on the mutation, and the returned promise is particularly useful when you're adding 306 + side-effects that run after the mutation has completed. 307 + 308 + ### Handling mutation errors 309 + 310 + It's worth noting that the promise we receive when calling the execute function will never 311 + reject. Instead it will always return a promise that resolves to a result. 312 + 313 + If you're checking for errors, you should use `result.error` instead, which will be set 314 + to a `CombinedError` when any kind of errors occurred while executing your mutation. 315 + [Read more about errors on our "Errors" page.](./errors.md) 316 + 317 + ```js 318 + import { useMutation } from '@urql/vue'; 319 + 320 + export default { 321 + setup() { 322 + const updateTodoResult = useMutation(` 323 + mutation ($id: ID!, $title: String!) { 324 + updateTodo (id: $id, title: $title) { 325 + id 326 + title 327 + } 328 + } 329 + `); 330 + 331 + return { 332 + updateTodo(id, title) { 333 + const variables = { id, title: title || '' }; 334 + updateTodoResult(variables).then(result => { 335 + if (result.error) { 336 + console.error('Oh no!', result.error); 337 + } 338 + }); 339 + }, 340 + }; 341 + }, 342 + }; 343 + ``` 344 + 345 + There are some more tricks we can use with `useMutation`.<br /> 346 + [Read more about its API in the API docs for it.](../api/vue.md#usemutation) 347 + 348 + [On the next page we'll learn about "Document Caching", `urql`'s default caching 349 + mechanism.](./document-caching.md)
+372
docs/basics/queries.md
··· 443 443 [Read more about its API in the API docs for it.](../api/svelte.md#operationStore) 444 444 445 445 [On the next page we'll learn about "Mutations" rather than Queries.](./mutations.md#svelte) 446 + 447 + ## Vue 448 + 449 + This guide covers how to query data with Vue with our `Client` now fully set up and provided to an 450 + app. We'll implement queries using the `useQuery` function from `@urql/vue`. 451 + 452 + ### Run a first query 453 + 454 + For the following examples, we'll imagine that we're querying data from a GraphQL API that contains 455 + todo items. Let's dive right into it! 456 + 457 + ```jsx 458 + <template> 459 + <div v-if="fetching"> 460 + Loading... 461 + </div> 462 + <div v-else-if="error"> 463 + Oh no... {{error}} 464 + </div> 465 + <div v-else> 466 + <ul v-if="data"> 467 + <li v-for="todo in data.todos">{{ todo.title }}</li> 468 + </ul> 469 + </div> 470 + </template> 471 + 472 + <script> 473 + import { useQuery } from '@urql/vue'; 474 + 475 + export default { 476 + setup() { 477 + const result = useQuery({ 478 + query: ` 479 + { 480 + todos { 481 + id 482 + title 483 + } 484 + } 485 + ` 486 + }); 487 + 488 + return { 489 + fetching: result.fetching, 490 + data: result.data, 491 + error: result.error, 492 + }; 493 + } 494 + }; 495 + </script> 496 + ``` 497 + 498 + Here we have implemented our first GraphQL query to fetch todos. We see that `useQuery` accepts 499 + options and returns a tuple. In this case we've set the `query` option to our GraphQL query. The 500 + tuple we then get in return is an array that contains a result object and a re-execute function. 501 + 502 + The result object contains several properties. The `fetching` field indicates whether we're currently 503 + loading data, `data` contains the actual `data` from the API's result, and `error` is set when either 504 + the request to the API has failed or when our API result contained some `GraphQLError`s, which 505 + we'll get into later on the ["Errors" page](./errors.md). 506 + 507 + All of these properties on the result are derived from the [shape of 508 + `OperationResult`](../api/core.md#operationresult) and are marked as [reactive 509 + ](https://v3.vuejs.org/guide/reactivity-fundamentals.html), which means they may 510 + update while the query is running, which will automatically update your UI. 511 + 512 + ### Variables 513 + 514 + Typically we'll also need to pass variables to our queries, for instance, if we are dealing with 515 + pagination. For this purpose `useQuery` also accepts a `variables` input, which we can 516 + use to supply variables to our query. 517 + 518 + ```jsx 519 + <template> 520 + ... 521 + </template> 522 + 523 + <script> 524 + import { useQuery } from '@urql/vue'; 525 + 526 + export default { 527 + props: ['from', 'limit'], 528 + setup({ from, limit }) { 529 + return useQuery({ 530 + query: ` 531 + query ($from: Int!, $limit: Int!) { 532 + todos(from: $from, limit: $limit) { 533 + id 534 + title 535 + } 536 + } 537 + `, 538 + variables: { from, limit } 539 + }); 540 + } 541 + }; 542 + </script> 543 + ``` 544 + 545 + As when we're sending GraphQL queries manually using `fetch`, the variables will be attached to the 546 + `POST` request body that is sent to our GraphQL API. 547 + 548 + All inputs that are passed to `useQuery` may also be [reactive 549 + state](https://v3.vuejs.org/guide/reactivity-fundamentals.html). This means that both the inputs and 550 + outputs of `useQuery` are reactive and may change over time. 551 + 552 + ```jsx 553 + <template> 554 + <ul v-if="data"> 555 + <li v-for="todo in data.todos">{{ todo.title }}</li> 556 + </ul> 557 + <button @click="from += 10">Next Page</button> 558 + </template> 559 + 560 + <script> 561 + import { useQuery } from '@urql/vue'; 562 + 563 + export default { 564 + setup() { 565 + const from = ref(0); 566 + 567 + const result = useQuery({ 568 + query: ` 569 + query ($from: Int!, $limit: Int!) { 570 + todos(from: $from, limit: $limit) { 571 + id 572 + title 573 + } 574 + } 575 + `, 576 + variables: { from, limit: 10 } 577 + }); 578 + 579 + return { 580 + from, 581 + data: result.data, 582 + }; 583 + } 584 + }; 585 + </script> 586 + ``` 587 + 588 + ### Pausing `useQuery` 589 + 590 + In some cases we may want `useQuery` to execute a query when a pre-condition has been met, and not 591 + execute the query otherwise. For instance, we may be building a form and want a validation query to 592 + only take place when a field has been filled out. 593 + 594 + Since with Vue 3's Composition API we won't just conditionally call `useQuery` we can instead pass a 595 + reactive `pause` input to `useQuery`. 596 + 597 + In the previous example we've defined a query with mandatory arguments. The `$from` and `$limit` 598 + variables have been defined to be non-nullable `Int!` values. 599 + 600 + Let's pause the query we've just written to not execute when these variables are empty, to 601 + prevent `null` variables from being executed. We can do this by computing `pause` to become `true` 602 + whenever these variables are falsy: 603 + 604 + ```js 605 + import { reactive } from 'vue' 606 + import { useQuery } from '@urql/vue'; 607 + 608 + export default { 609 + props: ['from', 'limit'], 610 + setup({ from, limit }) { 611 + return useQuery({ 612 + query: ` 613 + query ($from: Int!, $limit: Int!) { 614 + todos(from: $from, limit: $limit) { 615 + id 616 + title 617 + } 618 + } 619 + `, 620 + variables: { from, limit }, 621 + pause: computed(() => !from.value || !limit.value) 622 + }); 623 + } 624 + }; 625 + </script> 626 + ``` 627 + 628 + Now whenever the mandatory `$from` or `$limit` variables aren't supplied the query won't be executed. 629 + This also means that `result.data` won't change, which means we'll still have access to our old data 630 + even though the variables may have changed. 631 + 632 + It's worth noting that depending on whether `from` and `limit` are reactive or not you may have to 633 + change how `pause` is computed. But there's also an imperative alternative to this API. Not only 634 + does the result you get back from `useQuery` have an `isPaused` ref, it also has `pause()` and 635 + `resume()` methods. 636 + 637 + ```jsx 638 + <template> 639 + <div v-if="fetching"> 640 + Loading... 641 + </div> 642 + <button @click="isPaused ? resume() : pause()">Toggle Query</button> 643 + </template> 644 + 645 + <script> 646 + import { useQuery } from '@urql/vue'; 647 + 648 + export default { 649 + setup() { 650 + return useQuery({ 651 + query: ` 652 + { 653 + todos { 654 + id 655 + title 656 + } 657 + } 658 + ` 659 + }); 660 + } 661 + }; 662 + </script> 663 + ``` 664 + 665 + This means that no matter whether you're in or outside of `setup()` or rather supplying the inputs 666 + to `useQuery` or using the outputs, you'll have access to ways to pause or unpause the query. 667 + 668 + ### Request Policies 669 + 670 + As has become clear in the previous sections of this page, the `useQuery` hook accepts more options 671 + than just `query` and `variables`. Another option we should touch on is `requestPolicy`. 672 + 673 + The `requestPolicy` option determines how results are retrieved from our `Client`'s cache. By 674 + default this is set to `cache-first`, which means that we prefer to get results from our cache, but 675 + are falling back to sending an API request. 676 + 677 + In total there are four different policies that we can use: 678 + 679 + - `cache-first` (the default) prefers cached results and falls back to sending an API request when 680 + no prior result is cached. 681 + - `cache-and-network` returns cached results but also always sends an API request, which is perfect 682 + for displaying data quickly while keeping it up-to-date. 683 + - `network-only` will always send an API request and will ignore cached results. 684 + - `cache-only` will always return cached results or `null`. 685 + 686 + The `cache-and-network` policy is particularly useful, since it allows us to display data instantly 687 + if it has been cached, but also refreshes data in our cache in the background. This means though 688 + that `fetching` will be `false` for cached results although an API request may still be ongoing in 689 + the background. 690 + 691 + For this reason there's another field on results, `result.stale`, which indicates that the cached 692 + result is either outdated or that another request is being sent in the background. 693 + 694 + Request policies aren't specific to `urql`'s Vue bindings, but are a common feature in its core. [You 695 + can learn more about request policies on the API docs.](../api/core.md#requestpolicy) 696 + 697 + ### Reexecuting Queries 698 + 699 + The `useQuery` hook updates and executes queries whenever its inputs, like the `query` or 700 + `variables` change, but in some cases we may find that we need to programmatically trigger a new 701 + query. This is the purpose of the `executeQuery` method which is a method on the result object 702 + that `useQuery` returns. 703 + 704 + Triggering a query programmatically may be useful in a couple of cases. It can for instance be used 705 + to refresh data that is currently being displayed. In these cases we may also override the 706 + `requestPolicy` of our query just once and set it to `network-only` to skip the cache. 707 + 708 + ```js 709 + import { useQuery } from '@urql/vue'; 710 + 711 + export default { 712 + setup() { 713 + const result = useQuery({ 714 + query: ` 715 + { 716 + todos { 717 + id 718 + title 719 + } 720 + } 721 + ` 722 + }); 723 + 724 + return { 725 + data: result.data, 726 + fetching: result.fetching, 727 + error: result.error, 728 + refresh() { 729 + result.executeQuery({ 730 + requestPolicy: 'network-only' 731 + }); 732 + } 733 + }; 734 + } 735 + }; 736 + </script> 737 + ``` 738 + 739 + Calling `refresh` in the above example will execute the query again forcefully, and will skip the 740 + cache, since we're passing `requestPolicy: 'network-only'`. 741 + 742 + Furthermore the `executeQuery` method can also be used to programmatically start a query even 743 + when `pause` is `true`, which would usually stop all automatic queries. 744 + 745 + ### Vue Suspense 746 + 747 + In Vue 3 a [new feature was introduced](https://vuedose.tips/go-async-in-vue-3-with-suspense/) that 748 + natively allows components to suspend while data is loading, which works universally on the server 749 + and on the client, where a replacement loading template is rendered on a parent while data is 750 + loading. 751 + 752 + Any component's `setup()` function can be updated to instead be an `async setup()` function, in 753 + other words, to return a `Promise` instead of directly returning its data. This means that we can 754 + update any `setup()` function to make use of Suspense. 755 + 756 + The `useQuery`'s returned result supports this, since it is a `PromiseLike`. We can update one of 757 + our examples to have a suspending component by changing our usage of `useQuery`: 758 + 759 + ```jsx 760 + <template> 761 + <ul> 762 + <li v-for="todo in data.todos">{{ todo.title }}</li> 763 + </ul> 764 + </template> 765 + 766 + <script> 767 + import { useQuery } from '@urql/vue'; 768 + 769 + export default { 770 + async setup() { 771 + const { data, error } = await useQuery({ 772 + query: ` 773 + { 774 + todos { 775 + id 776 + title 777 + } 778 + } 779 + ` 780 + }); 781 + 782 + return { data }; 783 + } 784 + }; 785 + </script> 786 + ``` 787 + 788 + As we can see, `await useQuery(...)` here suspends the component and what we render will not have to 789 + handle the loading states of `useQuery` at all. Instead in Vue Suspense we'll have to wrap a parent 790 + component in a "Suspense boundary." This boundary is what switches a parent to a loading state while 791 + parts of its children are fetching data. The suspense promise is in essence "bubbling up" until it 792 + finds a "Suspense boundary". 793 + 794 + ``` 795 + <template> 796 + <Suspense> 797 + <template #default> 798 + <MyAsyncComponent /> 799 + </template> 800 + <template #fallback> 801 + <span>Loading...</span> 802 + </template> 803 + </Suspense> 804 + </template> 805 + ``` 806 + 807 + As long as any parent component is wrapping our component which uses `async setup()` in this 808 + boundary, we'll get Vue Suspense to work correctly and trigger this loading state. When a child 809 + suspends this component will switch to using its `#fallback` template rather than its `#default` 810 + template. 811 + 812 + ### Reading on 813 + 814 + There are some more tricks we can use with `useQuery`. [Read more about its API in the API docs for 815 + it.](../api/vue.md#usequery) 816 + 817 + [On the next page we'll learn about "Mutations" rather than Queries.](./mutations.md#vue)
+12 -12
docs/comparison.md
··· 79 79 80 80 ### Framework Bindings 81 81 82 - | | urql | Apollo | Relay | 83 - | ------------------------------ | -------------------------------- | ------------------- | ------------------ | 84 - | React Bindings | ✅ | ✅ | ✅ | 85 - | React Concurrent Hooks Support | ✅ | 🛑 | ✅ (experimental) | 86 - | React Legacy Hooks Support | ✅ | ✅ | 🟡 `relay-hooks` | 87 - | React Suspense (Experimental) | ✅ (experimental on client-side) | 🛑 | ✅ | 88 - | Next.js Integration | ✅ `next-urql` | 🟡 | 🔶 | 89 - | Preact Support | ✅ | 🔶 | 🔶 | 90 - | Svelte Bindings | ✅ | 🟡 `svelte-apollo` | 🟡 `svelte-relay` | 91 - | Vue Bindings | 🛑 (planned) | 🟡 `vue-apollo` | 🟡 `vue-relay` | 92 - | Angular Bindings | 🛑 | 🟡 `apollo-angular` | 🟡 `relay-angular` | 93 - | Initial Data on mount | ✅ | ✅ | ✅ | 82 + | | urql | Apollo | Relay | 83 + | ------------------------------ | -------------- | ------------------- | ------------------ | 84 + | React Bindings | ✅ | ✅ | ✅ | 85 + | React Concurrent Hooks Support | ✅ | 🛑 | ✅ (experimental) | 86 + | React Legacy Hooks Support | ✅ | ✅ | 🟡 `relay-hooks` | 87 + | React Suspense (Experimental) | ✅ | 🛑 | ✅ | 88 + | Next.js Integration | ✅ `next-urql` | 🟡 | 🔶 | 89 + | Preact Support | ✅ | 🔶 | 🔶 | 90 + | Svelte Bindings | ✅ | 🟡 `svelte-apollo` | 🟡 `svelte-relay` | 91 + | Vue Bindings | ✅ | 🟡 `vue-apollo` | 🟡 `vue-relay` | 92 + | Angular Bindings | 🛑 | 🟡 `apollo-angular` | 🟡 `relay-angular` | 93 + | Initial Data on mount | ✅ | ✅ | ✅ | 94 94 95 95 Interestingly all three libraries heavily support React as they were all started from the React 96 96 community outwards, but Apollo and Vue benefit from community bindings for different frameworks a
+4 -2
packages/site/src/screens/docs/article.js
··· 64 64 const HeadingItem = styled.li` 65 65 line-height: ${p => p.theme.lineHeights.heading}; 66 66 margin-bottom: ${p => p.theme.spacing.xs}; 67 + margin-left: ${p => (p.depth >= 3 ? p.theme.spacing.sm : 0)}; 67 68 68 69 > a { 69 70 font-size: ${p => p.theme.fontSizes.small}; 70 - font-weight: ${p => p.theme.fontWeights.body}; 71 + font-weight: ${p => 72 + p.depth < 3 ? p.theme.fontWeights.links : p.theme.fontWeights.body}; 71 73 color: ${p => p.theme.colors.passive}; 72 74 text-decoration: none; 73 75 } ··· 85 87 <LegendTitle>In this section</LegendTitle> 86 88 <HeadingList> 87 89 {headings.map(heading => ( 88 - <HeadingItem key={heading.slug}> 90 + <HeadingItem key={heading.slug} depth={heading.depth}> 89 91 <a href={`#${heading.slug}`}>{heading.value}</a> 90 92 </HeadingItem> 91 93 ))}