Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1---
2title: Vue Bindings
3order: 1
4---
5
6# Vue
7
8## Getting started
9
10The `@urql/vue` bindings have been written with [Vue
113](https://github.com/vuejs/vue-next/releases/tag/v3.0.0) in mind and use Vue's newer [Composition
12API](https://v3.vuejs.org/guide/composition-api-introduction.html). This gives the `@urql/vue`
13bindings capabilities to be more easily integrated into your existing `setup()` functions.
14
15### Installation
16
17Installing `@urql/vue` is quick and no other packages are immediately necessary.
18
19```sh
20yarn add @urql/vue graphql
21# or
22npm install --save @urql/vue graphql
23```
24
25Most libraries related to GraphQL also need the `graphql` package to be installed as a peer
26dependency, so that they can adapt to your specific versioning requirements. That's why we'll need
27to install `graphql` alongside `@urql/vue`.
28
29Both the `@urql/vue` and `graphql` packages follow [semantic versioning](https://semver.org) and
30all `@urql/vue` packages will define a range of compatible versions of `graphql`. Watch out
31for breaking changes in the future however, in which case your package manager may warn you about
32`graphql` being out of the defined peer dependency range.
33
34### Setting up the `Client`
35
36The `@urql/vue` package exports a `Client` class, which we can use to create
37the GraphQL client. This central `Client` manages all of our GraphQL requests and results.
38
39```js
40import { Client, cacheExchange, fetchExchange } from '@urql/vue';
41
42const client = new Client({
43 url: 'http://localhost:3000/graphql',
44 exchanges: [cacheExchange, fetchExchange],
45});
46```
47
48At the bare minimum we'll need to pass an API's `url` and `exchanges` when we create a `Client`
49to get started.
50
51Another common option is `fetchOptions`. This option allows us to customize the options that will be
52passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object or
53a function returning an options object.
54
55In the following example we'll add a token to each `fetch` request that our `Client` sends to our
56GraphQL API.
57
58```js
59const client = new Client({
60 url: 'http://localhost:3000/graphql',
61 exchanges: [cacheExchange, fetchExchange],
62 fetchOptions: () => {
63 const token = getToken();
64 return {
65 headers: { authorization: token ? `Bearer ${token}` : '' },
66 };
67 },
68});
69```
70
71### Providing the `Client`
72
73To make use of the `Client` in Vue we will have to provide from a parent component to its child
74components. This will share one `Client` with the rest of our app. In `@urql/vue` there are two
75different ways to achieve this.
76
77The first method is to use `@urql/vue`'s `provideClient` function. This must be called in any of
78your parent components and accepts either a `Client` directly or just the options that you'd pass to
79`Client`.
80
81```html
82<script>
83 import { Client, provideClient, cacheExchange, fetchExchange } from '@urql/vue';
84
85 const client = new Client({
86 url: 'http://localhost:3000/graphql',
87 exchanges: [cacheExchange, fetchExchange],
88 });
89
90 provideClient(client);
91</script>
92```
93
94Alternatively we may use the exported `install` function and treat `@urql/vue` as a plugin by
95importing its default export and using it [as a plugin](https://v3.vuejs.org/guide/plugins.html#using-a-plugin).
96
97```js
98import { createApp } from 'vue';
99import Root from './App.vue';
100import urql, { cacheExchange, fetchExchange } from '@urql/vue';
101
102const app = createApp(Root);
103
104app.use(urql, {
105 url: 'http://localhost:3000/graphql',
106 exchanges: [cacheExchange, fetchExchange],
107});
108
109app.mount('#app');
110```
111
112The plugin also accepts `Client`'s options or a `Client` as its inputs.
113
114## Queries
115
116We'll implement queries using the `useQuery` function from `@urql/vue`.
117
118### Run a first query
119
120For the following examples, we'll imagine that we're querying data from a GraphQL API that contains
121todo items. Let's dive right into it!
122
123```jsx
124<template>
125 <div v-if="fetching">
126 Loading...
127 </div>
128 <div v-else-if="error">
129 Oh no... {{error}}
130 </div>
131 <div v-else>
132 <ul v-if="data">
133 <li v-for="todo in data.todos" :key="todo.id">{{ todo.title }}</li>
134 </ul>
135 </div>
136</template>
137
138<script>
139import { gql, useQuery } from '@urql/vue';
140
141export default {
142 setup() {
143 const result = useQuery({
144 query: gql`
145 {
146 todos {
147 id
148 title
149 }
150 }
151 `
152 });
153
154 return {
155 fetching: result.fetching,
156 data: result.data,
157 error: result.error,
158 };
159 }
160};
161</script>
162```
163
164Here we have implemented our first GraphQL query to fetch todos. We see that `useQuery` accepts
165options and returns a result object. In this case we've set the `query` option to our GraphQL query.
166
167The result object contains several properties. The `fetching` field indicates whether we're currently
168loading data, `data` contains the actual `data` from the API's result, and `error` is set when either
169the request to the API has failed or when our API result contained some `GraphQLError`s, which
170we'll get into later on the ["Errors" page](./errors.md).
171
172All of these properties on the result are derived from the [shape of
173`OperationResult`](../api/core.md#operationresult) and are marked as [reactive
174](https://v3.vuejs.org/guide/reactivity-fundamentals.html), which means they may
175update while the query is running, which will automatically update your UI.
176
177### Variables
178
179Typically we'll also need to pass variables to our queries, for instance, if we are dealing with
180pagination. For this purpose `useQuery` also accepts a `variables` input, which we can
181use to supply variables to our query.
182
183```jsx
184<template>
185 ...
186</template>
187
188<script>
189import { gql, useQuery } from '@urql/vue';
190
191export default {
192 props: ['from', 'limit'],
193 setup({ from, limit }) {
194 return useQuery({
195 query: gql`
196 query ($from: Int!, $limit: Int!) {
197 todos(from: $from, limit: $limit) {
198 id
199 title
200 }
201 }
202 `,
203 variables: { from, limit }
204 });
205 }
206};
207</script>
208```
209
210As when we're sending GraphQL queries manually using `fetch`, the variables will be attached to the
211`POST` request body that is sent to our GraphQL API.
212
213All inputs that are passed to `useQuery` may also be [reactive
214state](https://v3.vuejs.org/guide/reactivity-fundamentals.html). This means that both the inputs and
215outputs of `useQuery` are reactive and may change over time.
216
217```jsx
218<template>
219 <ul v-if="data">
220 <li v-for="todo in data.todos" :key="todo.id">{{ todo.title }}</li>
221 </ul>
222 <button @click="from += 10">Next Page</button>
223</template>
224
225<script>
226import { gql, useQuery } from '@urql/vue';
227
228export default {
229 setup() {
230 const from = ref(0);
231
232 const result = useQuery({
233 query: gql`
234 query ($from: Int!, $limit: Int!) {
235 todos(from: $from, limit: $limit) {
236 id
237 title
238 }
239 }
240 `,
241 variables: { from, limit: 10 }
242 });
243
244 return {
245 from,
246 data: result.data,
247 };
248 }
249};
250</script>
251```
252
253### Pausing `useQuery`
254
255In some cases we may want `useQuery` to execute a query when a pre-condition has been met, and not
256execute the query otherwise. For instance, we may be building a form and want a validation query to
257only take place when a field has been filled out.
258
259Since with Vue 3's Composition API we won't just conditionally call `useQuery` we can instead pass a
260reactive `pause` input to `useQuery`.
261
262In the previous example we've defined a query with mandatory arguments. The `$from` and `$limit`
263variables have been defined to be non-nullable `Int!` values.
264
265Let's pause the query we've just written to not execute when these variables are empty, to
266prevent `null` variables from being executed. We can do this by computing `pause` to become `true`
267whenever these variables are falsy:
268
269```js
270import { reactive } from 'vue'
271import { gql, useQuery } from '@urql/vue';
272
273export default {
274 props: ['from', 'limit'],
275 setup({ from, limit }) {
276 const shouldPause = computed(() => from == null || limit == null);
277 return useQuery({
278 query: gql`
279 query ($from: Int!, $limit: Int!) {
280 todos(from: $from, limit: $limit) {
281 id
282 title
283 }
284 }
285 `,
286 variables: { from, limit },
287 pause: shouldPause
288 });
289 }
290};
291</script>
292```
293
294Now whenever the mandatory `$from` or `$limit` variables aren't supplied the query won't be executed.
295This also means that `result.data` won't change, which means we'll still have access to our old data
296even though the variables may have changed.
297
298It's worth noting that depending on whether `from` and `limit` are reactive or not you may have to
299change how `pause` is computed. But there's also an imperative alternative to this API. Not only
300does the result you get back from `useQuery` have an `isPaused` ref, it also has `pause()` and
301`resume()` methods.
302
303```jsx
304<template>
305 <div v-if="fetching">
306 Loading...
307 </div>
308 <button @click="isPaused ? resume() : pause()">Toggle Query</button>
309</template>
310
311<script>
312import { gql, useQuery } from '@urql/vue';
313
314export default {
315 setup() {
316 return useQuery({
317 query: gql`
318 {
319 todos {
320 id
321 title
322 }
323 }
324 `
325 });
326 }
327};
328</script>
329```
330
331This means that no matter whether you're in or outside of `setup()` or rather supplying the inputs
332to `useQuery` or using the outputs, you'll have access to ways to pause or unpause the query.
333
334### Request Policies
335
336As has become clear in the previous sections of this page, the `useQuery` hook accepts more options
337than just `query` and `variables`. Another option we should touch on is `requestPolicy`.
338
339The `requestPolicy` option determines how results are retrieved from our `Client`'s cache. By
340default this is set to `cache-first`, which means that we prefer to get results from our cache, but
341are falling back to sending an API request.
342
343Request policies aren't specific to `urql`'s Vue bindings, but are a common feature in its core.
344[You can learn more about how the cache behaves given the four different policies on the "Document
345Caching" page.](../basics/document-caching.md)
346
347```js
348import { useQuery } from '@urql/vue';
349
350export default {
351 setup() {
352 return useQuery({
353 query: TodosQuery,
354 requestPolicy: 'cache-and-network',
355 });
356 },
357};
358```
359
360Specifically, a new request policy may be passed directly to `useQuery` as an option.
361This policy is then used for this specific query. In this case, `cache-and-network` is used and
362the query will be refreshed from our API even after our cache has given us a cached result.
363
364Internally, the `requestPolicy` is just one of several "**context** options". The `context`
365provides metadata apart from the usual `query` and `variables` we may pass. This means that
366we may also change the `Client`'s default `requestPolicy` by passing it there.
367
368```js
369import { Client, cacheExchange, fetchExchange } from '@urql/vue';
370
371const client = new Client({
372 url: 'http://localhost:3000/graphql',
373 exchanges: [cacheExchange, fetchExchange],
374 // every operation will by default use cache-and-network rather
375 // than cache-first now:
376 requestPolicy: 'cache-and-network',
377});
378```
379
380### Context Options
381
382As mentioned, the `requestPolicy` option on `useQuery` is a part of `urql`'s context options.
383In fact, there are several more built-in context options, and the `requestPolicy` option is
384one of them. Another option we've already seen is the `url` option, which determines our
385API's URL. These options aren't limited to the `Client` and may also be passed per query.
386
387```jsx
388import { useQuery } from '@urql/vue';
389
390export default {
391 setup() {
392 return useQuery({
393 query: TodosQuery,
394 context: {
395 requestPolicy: 'cache-and-network',
396 url: 'http://localhost:3000/graphql?debug=true',
397 },
398 });
399 },
400};
401```
402
403As we can see, the `context` property for `useQuery` accepts any known `context` option and can be
404used to alter them per query rather than globally. The `Client` accepts a subset of `context`
405options, while the `useQuery` option does the same for a single query.
406[You can find a list of all `Context` options in the API docs.](../api/core.md#operationcontext)
407
408### Reexecuting Queries
409
410The `useQuery` hook updates and executes queries whenever its inputs, like the `query` or
411`variables` change, but in some cases we may find that we need to programmatically trigger a new
412query. This is the purpose of the `executeQuery` method which is a method on the result object
413that `useQuery` returns.
414
415Triggering a query programmatically may be useful in a couple of cases. It can for instance be used
416to refresh data that is currently being displayed. In these cases we may also override the
417`requestPolicy` of our query just once and set it to `network-only` to skip the cache.
418
419```js
420import { gql, useQuery } from '@urql/vue';
421
422export default {
423 setup() {
424 const result = useQuery({
425 query: gql`
426 {
427 todos {
428 id
429 title
430 }
431 }
432 `,
433 });
434
435 return {
436 data: result.data,
437 fetching: result.fetching,
438 error: result.error,
439 refresh() {
440 result.executeQuery({
441 requestPolicy: 'network-only',
442 });
443 },
444 };
445 },
446};
447```
448
449Calling `refresh` in the above example will execute the query again forcefully, and will skip the
450cache, since we're passing `requestPolicy: 'network-only'`.
451
452Furthermore the `executeQuery` function can also be used to programmatically start a query even
453when `pause` is set to `true`, which would usually stop all automatic queries. This can be used to
454perform one-off actions, or to set up polling.
455
456### Vue Suspense
457
458In Vue 3 a [new feature was introduced](https://vuedose.tips/go-async-in-vue-3-with-suspense/) that
459natively allows components to suspend while data is loading, which works universally on the server
460and on the client, where a replacement loading template is rendered on a parent while data is
461loading.
462
463Any component's `setup()` function can be updated to instead be an `async setup()` function, in
464other words, to return a `Promise` instead of directly returning its data. This means that we can
465update any `setup()` function to make use of Suspense.
466
467The `useQuery`'s returned result supports this, since it is a `PromiseLike`. We can update one of
468our examples to have a suspending component by changing our usage of `useQuery`:
469
470```jsx
471<template>
472 <ul>
473 <li v-for="todo in data.todos" :key="todo.id">{{ todo.title }}</li>
474 </ul>
475</template>
476
477<script>
478import { gql, useQuery } from '@urql/vue';
479
480export default {
481 async setup() {
482 const { data, error } = await useQuery({
483 query: gql`
484 {
485 todos {
486 id
487 title
488 }
489 }
490 `
491 });
492
493 return { data };
494 }
495};
496</script>
497```
498
499As we can see, `await useQuery(...)` here suspends the component and what we render will not have to
500handle the loading states of `useQuery` at all. Instead in Vue Suspense we'll have to wrap a parent
501component in a "Suspense boundary." This boundary is what switches a parent to a loading state while
502parts of its children are fetching data. The suspense promise is in essence "bubbling up" until it
503finds a "Suspense boundary".
504
505```
506<template>
507 <Suspense>
508 <template #default>
509 <MyAsyncComponent />
510 </template>
511 <template #fallback>
512 <span>Loading...</span>
513 </template>
514 </Suspense>
515</template>
516```
517
518As long as any parent component is wrapping our component which uses `async setup()` in this
519boundary, we'll get Vue Suspense to work correctly and trigger this loading state. When a child
520suspends this component will switch to using its `#fallback` template rather than its `#default`
521template.
522
523### Chaining calls in Vue Suspense
524
525As shown [above](#vue-suspense), in Vue Suspense the `async setup()` lifecycle function can be used
526to set up queries in advance, wait for them to have fetched some data, and then let the component
527render as usual.
528
529However, because the `async setup()` function can be used with `await`-ed promise calls, we may run
530into situations where we're trying to call functions like `useQuery()` after we've already awaited
531another promise and will be outside of the synchronous scope of the `setup()` lifecycle. This means
532that the `useQuery` (and `useSubscription` & `useMutation`) functions won't have access to the
533`Client` anymore that we'd have set up using `provideClient`.
534
535To prevent this, we can create something called a "client handle" using the `useClientHandle`
536function.
537
538```js
539import { gql, useClientHandle } from '@urql/vue';
540
541export default {
542 async setup() {
543 const handle = useClientHandle();
544
545 await Promise.resolve(); // NOTE: This could be any await call
546
547 const result = await handle.useQuery({
548 query: gql`
549 {
550 todos {
551 id
552 title
553 }
554 }
555 `,
556 });
557
558 return { data: result.data };
559 },
560};
561```
562
563As we can see, when we use `handle.useQuery()` we're able to still create query results although we've
564interrupted the synchronous `setup()` lifecycle with a `Promise.resolve()` delay. This would also
565allow us to create chained queries by using
566[`computed`](https://v3.vuejs.org/guide/reactivity-computed-watchers.html#computed-values) to use an
567output from a preceding result in a next `handle.useQuery()` call.
568
569### Reading on
570
571There are some more tricks we can use with `useQuery`. [Read more about its API in the API docs for
572it.](../api/vue.md#usequery)
573
574## Mutations
575
576The `useMutation` function is similar to `useQuery` but is triggered manually and accepts
577only a `DocumentNode` or `string` as an input.
578
579### Sending a mutation
580
581Let's again pick up an example with an imaginary GraphQL API for todo items, and dive into an
582example! We'll set up a mutation that _updates_ a todo item's title.
583
584```js
585import { gql, useMutation } from '@urql/vue';
586
587export default {
588 setup() {
589 const { executeMutation: updateTodo } = useMutation(gql`
590 mutation ($id: ID!, $title: String!) {
591 updateTodo(id: $id, title: $title) {
592 id
593 title
594 }
595 }
596 `);
597
598 return { updateTodo };
599 },
600};
601```
602
603Similar to the `useQuery` output, `useMutation` returns a result object, which reflects the data of
604an executed mutation. That means it'll contain the familiar `fetching`, `error`, and `data`
605properties — it's identical since this is a common pattern of how `urql`
606presents [operation results](../api/core.md#operationresult).
607
608Unlike the `useQuery` hook, the `useMutation` hook doesn't execute automatically. At this point in
609our example, no mutation will be performed. To execute our mutation we instead have to call the
610`executeMutation` method on the result with some variables.
611
612### Using the mutation result
613
614When calling our `updateTodo` function we have two ways of getting to the result as it comes back
615from our API. We can either use the result itself, since all properties related to the last
616[operation result](../api/core.md#operationresult) are marked as [reactive
617](https://v3.vuejs.org/guide/reactivity-fundamentals.html) — or we can use the promise that the
618`executeMutation` method returns when it's called:
619
620```js
621import { gql, useMutation } from '@urql/vue';
622
623export default {
624 setup() {
625 const updateTodoResult = useMutation(gql`
626 mutation ($id: ID!, $title: String!) {
627 updateTodo(id: $id, title: $title) {
628 id
629 title
630 }
631 }
632 `);
633
634 return {
635 updateTodo(id, title) {
636 const variables = { id, title: title || '' };
637 updateTodoResult.executeMutation(variables).then(result => {
638 // The result is almost identical to `updateTodoResult` with the exception
639 // of `result.fetching` not being set and its properties not being reactive.
640 // It is an OperationResult.
641 });
642 },
643 };
644 },
645};
646```
647
648The reactive result that `useMutation` returns is useful when your UI has to display progress or
649results on the mutation, and the returned promise is particularly useful when you're adding
650side-effects that run after the mutation has completed.
651
652### Handling mutation errors
653
654It's worth noting that the promise we receive when calling the execute function will never
655reject. Instead it will always return a promise that resolves to a result.
656
657If you're checking for errors, you should use `result.error` instead, which will be set
658to a `CombinedError` when any kind of errors occurred while executing your mutation.
659[Read more about errors on our "Errors" page.](./errors.md)
660
661```js
662import { gql, useMutation } from '@urql/vue';
663
664export default {
665 setup() {
666 const updateTodoResult = useMutation(gql`
667 mutation ($id: ID!, $title: String!) {
668 updateTodo(id: $id, title: $title) {
669 id
670 title
671 }
672 }
673 `);
674
675 return {
676 updateTodo(id, title) {
677 const variables = { id, title: title || '' };
678 updateTodoResult.executeMutation(variables).then(result => {
679 if (result.error) {
680 console.error('Oh no!', result.error);
681 }
682 });
683 },
684 };
685 },
686};
687```
688
689There are some more tricks we can use with `useMutation`.<br />
690[Read more about its API in the API docs for it.](../api/vue.md#usemutation)
691
692## Reading on
693
694This concludes the introduction for using `urql` with Vue. The rest of the documentation
695is mostly framework-agnostic and will apply to either `urql` in general or the `@urql/core` package,
696which is the same between all framework bindings. Hence, next we may want to learn more about one of
697the following to learn more about the internals:
698
699- [How does the default "document cache" work?](./document-caching.md)
700- [How are errors handled and represented?](./errors.md)
701- [A quick overview of `urql`'s architecture and structure.](../architecture.md)
702- [Setting up other features, like authentication, uploads, or persisted queries.](../advanced/README.md)