Queries
You can query GraphQL APIs with the Query component after you've setup the GraphQL Client.
The Query component uses slots and scoped slots to provide the query state to the slot template.
To run a query, the Query component takes a required query
prop that can be either a string
containing the query or a DocumentNode
loaded by graphql-tag/loader
from .graphql
files.
The Query component is renderless by default, meaning it will not render any extra HTML other than its slot, but only when exactly one child is present, if multiple children exist inside its slot it will render a span
.
<template>
<div>
<Query query="{ todos { text } }" v-slot="{ data }">
<div v-if="data">
<p v-for="todo in data.todos">{{ todo.text }}</p>
</div>
</Query>
</div>
</template>
<script>
import { Query } from 'vue-gql';
export default {
components: {
Query
}
};
</script>
By default the query will run on the server-side if applicable (via serverPrefetch
) or on mounted (client-side) if it didn't already.
The examples from now on will omit much of the boilerplate and will only use the Query
component to demonstrate its uses clearly.
graphql-tag
You can use graphql-tag
to compile your queries or load them with the graphql-tag/loader
.
// in script
const todos = gql`
todos {
id
text
}
`;
// in template
<Query :query="todos" v-slot="{ data }">
<div v-if="data">
<p v-for="todo in data.todos">{{ todo.text }}</p>
</div>
</Query>
Here we are using require
with the graphql-tag/loader
:
<Query :query="require('@/graphql/todos').Todos" v-slot="{ data }">
<div v-if="data">
<p v-for="todo in data.todos">{{ todo.text }}</p>
</div>
</Query>
Variables
You can provide variables to your queries using the variables
optional prop, which is an object containing the variables you would normally send to a GraphQL request.
<template>
<Query :query="todo" :variables="{ id: 123 }" v-slot="{ data }">
<div v-if="data">
<p v-for="todo in data.todos">{{ todo.text }}</p>
</div>
</Query>
</template>
<script>
const todo = `
query FetchTodo ($id: ID!) {
todo (id: $id) {
text
}
}
`;
export default {
// ... same as before,
data: () => ({
todo
})
};
</script>
Slot Props
fetching
The Query slot props contain more useful information that you can use to build better experience for your users, for example you can use the fetching
slot prop to display a loading indicator.
<Query query="{ todos { text } }" v-slot="{ data, fetching }">
<!-- Your Loading Indicator Component -->
<Loading v-if="fetching" />
<div v-else>
<p v-for="todo in data.todos">{{ todo.text }}</p>
</div>
</Query>
done
The done
slot prop is a boolean that indicates that the query has been completed.
errors
The errors
slot prop contains all errors encountered when running the query.
<Query query="{ todos { text } }" v-slot="{ data, errors }">
<!-- Your Custom component to handle error display -->
<ErrorPage v-if="errors" :errors="errors" />
<div v-else>
<p v-for="todo in data.todos">{{ todo.text }}</p>
</div>
</Query>
execute
Sometimes you want to re-fetch the query or run it after some action, the execute
slot prop is a function that re-runs the query. This example executes the query after the button has been clicked, note that the query is still fetched initially.
<Query query="{ posts { id title } }" v-slot="{ data, execute }">
<div v-if="data">
<ul>
<li v-for="post in data.posts" :key="post.id">{{ post.title }}</li>
</ul>
<button @click="execute()"></button>
</div>
</Query>
Caching
Unique queries are cached in memory, the uniqueness here is an id calculated by the query body, and its variables. Meaning if the same query is run with the same variables it will be fetched from the cache by default and will not hit the network. Cache is deleted after the user closes/refreshes the page.
By default the client uses cache-first
policy to handle queries, the full list of available policies are:
cache-first
: If found in cache return it, otherwise fetch it from the network.network-only
: Always fetch from the network and do not cache it.cache-and-network
: If found in cache return it, but fetch the fresh value and cache it for next time, if not found in cache it will fetch it from network and cache it.
You can force the Query component to fetch using any of the policies mentioned, you can do this by passing a cachePolicy
option to the execute
slot prop:
<Query query="{ posts { id title } }" v-slot="{ data, execute }">
<div v-if="data">
<ul>
<li v-for="post in data.posts" :key="post.id">{{ post.title }}</li>
</ul>
<button @click="execute({ cachePolicy: 'network-only' })"></button>
</div>
</Query>
Calling execute
with a different cache policy will not change the default policy, the policy you specify will always be used for the next request upon calling execute
.
Setting default cache policy
You can set the default policy when you are providing the GraphQL client by passing cachePolicy
option to the createClient
function.
const client = createClient({
url: '/graphql', // Your endpoint
cachePolicy: 'network-only'
});
This will make all the Query components under the Provider tree use the network-only
policy by default, you can still override with the execute
slot prop.
Cache Prop
You could also pass the cachePolicy
prop to the Query
component to set its default caching policy explicitly.
<Query
query="{ posts { id title } }"
cache-policy="network-only"
v-slot="{ data }"
>
<div v-if="data">
<ul>
<li v-for="post in data.posts" :key="post.id">{{ post.title }}</li>
</ul>
</div>
</Query>
Watching Variables
Often you want to re-fetch the query when a variable changes, this is done for you by default as long as the query uses variables
prop.
<Query
query="query getPost ($id: ID!) { post (id: $id) { id title } }"
:variables="{ id }"
v-slot="{ data }"
>
<div v-if="data">
<h1>{{ data.post.title }}</h1>
</div>
</Query>
This examples re-runs the query whenever the id
changes, the results of re-fetched queries follows the configured cache-policy.
Disabling variable watching
You can disable the mentioned behavior by setting pause
prop to true
.
<Query
query="query getPost ($id: ID!) { post (id: $id) { id title } }"
:variables="{ id }"
:pause="true"
v-slot="{ data }"
>
<div v-if="data">
<h1>{{ data.post.title }}</h1>
</div>
</Query>