Queries ​
This page shows how to fetch GraphQL data in Vue with the useQuery composable and attach the result to your UI.
Executing a Query ​
The useQuery composable is the primary way to execute queries. Call it in your component's <script setup> and pass a GraphQL document:
<script setup lang="ts">
const { current } = useQuery(gql`
query GetDogs {
dogs {
id
breed
}
}
`)
</script>
<template>
<div v-if="current.loading">
Loading...
</div>
<div v-else-if="current.error">
Error: {{ current.error.message }}
</div>
<ul v-else-if="current.resultState === 'complete'">
<li v-for="dog in current.result.dogs" :key="dog.id">
{{ dog.breed }}
</li>
</ul>
</template>When your component renders, useQuery returns a current ref containing a discriminated union with loading, error, result, and resultState properties. Using current provides better type narrowing—when you check current.resultState === 'complete', TypeScript knows current.result is defined.
Each property is also available as an individual ref (result, loading, error), but we recommend using current for type-safety.
Variables ​
Pass variables in the options object. The variables option accepts a plain object, a reactive object, or a getter function:
<script setup lang="ts">
const breed = ref('bulldog')
const { current } = useQuery(gql`
query GetDog($breed: String!) {
dog(breed: $breed) {
id
name
}
}
`, {
variables: {
breed,
},
})
</script>When breed changes, the query automatically re-executes with the new value.
Variables as a Getter ​
For props or computed values, use a getter function:
<script setup lang="ts">
const { breed } = defineProps<{ breed: string }>()
const { current } = useQuery(gql`
query GetDog($breed: String!) {
dog(breed: $breed) { id }
}
`, {
variables: () => ({
breed,
}),
})
</script>Caching ​
Apollo Client automatically caches query results. When you execute the same query again, it returns cached data instantly without a network request.
<script setup lang="ts">
const { breed } = defineProps<{ breed: string }>()
// First time: fetches from network
// Second time with same breed: returns from cache instantly
const { current } = useQuery(gql`
query GetDogPhoto($breed: String!) {
dog(breed: $breed) {
id
photo
}
}
`, {
variables: () => ({ breed }),
})
</script>Learn more about caching in the Caching Overview.
Updating Cached Data ​
Polling ​
Poll the server at a fixed interval:
<script setup lang="ts">
const { current } = useQuery(gql`
query GetNotifications {
notifications { id }
}
`, {
pollInterval: 5000, // Poll every 5 seconds
})
</script>Control polling dynamically:
<script setup lang="ts">
const { query } = useQuery(QUERY)
// Start polling every 2 seconds
query.value?.startPolling(2000)
// Stop polling
query.value?.stopPolling()
</script>Refetching ​
Manually refetch in response to user actions:
<script setup lang="ts">
const { breed } = defineProps<{ breed: string }>()
const { current, refetch } = useQuery(gql`
query GetDogPhoto($breed: String!) {
dog(breed: $breed) {
id
photo
}
}
`, {
variables: () => ({ breed }),
})
</script>
<template>
<img
v-if="current.resultState === 'complete'"
:src="current.result.dog.photo"
>
<button @click="refetch()">
Refresh
</button>
</template>Pass new variables to refetch:
const { refetch } = useQuery(QUERY, { variables: { breed: 'bulldog' } })
// Refetch with different variables
refetch({ breed: 'poodle' })Loading States ​
The current.loading property indicates when a query is in flight:
<script setup lang="ts">
const { current } = useQuery(QUERY)
</script>
<template>
<div v-if="current.loading">
Loading...
</div>
<div v-else-if="current.resultState === 'complete'">
{{ current.result }}
</div>
</template>For more granular control, use current.networkStatus:
<script setup lang="ts">
const { current, refetch } = useQuery(QUERY)
</script>
<template>
<div v-if="current.networkStatus === NetworkStatus.refetch">
Refetching...
</div>
</template>Error Handling ​
The current.error property contains any error that occurred:
<script setup lang="ts">
const { current } = useQuery(QUERY)
</script>
<template>
<div v-if="current.error" class="error">
{{ current.error.message }}
</div>
</template>For comprehensive error handling including partial data, see Error Handling.
Fetch Policies ​
Control how the query interacts with the cache using fetchPolicy:
const { current } = useQuery(QUERY, {
fetchPolicy: 'network-only',
})| Policy | Description |
|---|---|
cache-first | Check cache first. Fetch from network only if not in cache. (default) |
cache-and-network | Return cache immediately, then fetch from network and update. |
network-only | Always fetch from network, but cache the result. |
cache-only | Only read from cache, never fetch from network. |
no-cache | Always fetch from network, don't cache the result. |
Disabling Queries ​
Prevent a query from executing with the enabled option:
<script setup lang="ts">
const { userId } = defineProps<{ userId?: string }>()
const { current } = useQuery(
gql`
query GetUser($id: ID!) {
user(id: $id) { id }
}
`,
() =>
userId == null
? { enabled: false }
: {
variables: { id: userId },
}
)
</script>The query won't execute until enabled becomes true.
Event Hooks ​
React to query lifecycle events:
const { onNextState, onResult, onError } = useQuery(QUERY)
// Called on every state change with the full current object
onNextState((current) => {
console.log('State:', current.resultState, current.loading)
})
// Called when result is available (complete, partial, or streaming)
onResult((result) => {
console.log('Data received:', result.data)
})
// Called when an error occurs
onError((error) => {
console.error('Query failed:', error)
})Next Steps ​
- Mutations - Learn how to update data
- Lazy Queries - Execute queries on demand
- Error Handling - Handle errors gracefully