---
title: "Queries & mutations"
description: "Real-time Convex data in Vue — useQuery, useQueries, useMutation, useAction, and pagination."
canonical_url: "https://nuxt-backend.dev/docs/data/queries-and-mutations"
---
# Queries & mutations

> Real-time Convex data in Vue — useQuery, useQueries, useMutation, useAction, and pagination.

These composables are auto-imported in app code. They wrap the live `ConvexVueClient` and stay reactive across hydration.

## `useQuery()`

Returns a `ShallowRef<T | undefined>` — `undefined` while loading — and **throws** query errors. Args are reactive; pass `'skip'` to pause.

```vue
<script setup lang="ts">
import { api } from '#backend/api'

const messages = useQuery(api.messages.list, {})
// messages.value: Message[] | undefined

const profile = useQuery(
  api.users.get,
  computed(() => userId.value ? { userId: userId.value } : 'skip'),
)
</script>
```

### Errors in the result: `useQuery_experimental()`

The experimental object form returns a `ShallowRef` of a discriminated union with `status` and either `data` or `error`. Errors are returned in the result unless `throwOnError: true`.

```vue
<script setup lang="ts">
import { api } from '#backend/api'

const result = useQuery_experimental({
  query: api.messages.list,
  args: computed(() => ({ channel: channel.value })),
  throwOnError: false,
})
// result.value.status: 'pending' | 'success' | 'error'
</script>
```

## `useQueries()`

Use this when the **set** of active queries is dynamic.

```vue
<script setup lang="ts">
import { api } from '#backend/api'

const results = useQueries({
  messages: { query: api.messages.list, args: { channel: '#general' } },
  me: { query: api.users.me, args: {} },
})
// results.value.messages — results.value.me
</script>
```

## `useMutation()`

```vue
<script setup lang="ts">
import { api } from '#backend/api'

const sendMessage = useMutation(api.messages.send)
await sendMessage({ body: 'Hello!' })

// Optimistic updates
const optimisticSend = sendMessage.withOptimisticUpdate((localStore, args) => {
  localStore.setQuery(api.messages.list, {}, [
    { _id: 'optimistic', body: args.body },
  ])
})
</script>
```

<callout color="primary" icon="i-lucide-flask-conical">

**Live demo** — a reactive `useQuery` driven by `useMutation`, running against this site's Convex deployment:

</callout>

<demo-counter>



</demo-counter>

## `useAction()`

```vue
<script setup lang="ts">
import { api } from '#backend/api'

const generateImage = useAction(api.images.generate)
const url = await generateImage({ prompt: 'a cat' })
</script>
```

## Pagination

`usePaginatedQuery()` returns a `ShallowRef` of `{ results, status, isLoading, loadMore }`, where `status` is one of `'LoadingFirstPage' | 'CanLoadMore' | 'LoadingMore' | 'Exhausted'`. Query errors are **thrown**.

```vue
<script setup lang="ts">
import { api } from '#backend/api'

const messages = usePaginatedQuery(
  api.messages.list,
  { channel: '#general' },
  { initialNumItems: 20 },
)

messages.value.loadMore(20)
</script>
```

### `usePaginatedQuery_experimental()`

Adds an object-form overload on top of `usePaginatedQuery`. The positional overload behaves identically (TitleCase `status`, throws on error). The object overload returns `{ data, status, error, canLoadMore, isLoading, loadMore }`, where `status` is `'pending' | 'success' | 'error'`; errors are returned via `status: 'error'` unless `throwOnError: true`.

```vue
<script setup lang="ts">
import { api } from '#backend/api'

const messages = usePaginatedQuery_experimental({
  query: api.messages.list,
  args: { channel: '#general' },
  initialNumItems: 20,
})
// messages.value.status: 'pending' | 'success' | 'error'
</script>
```

<note>

React backs its experimental hook with the Convex client's native `PaginatedQueryClient`, which the published `convex` package does not export. This port reuses the same manual page-management implementation `usePaginatedQuery` uses, which yields identical observable results.

</note>

## Imperative access: `useBackendClient()`

For one-off imperative calls, reach for the client directly. Most app code should prefer the composables.

```vue
<script setup lang="ts">
import { api } from '#backend/api'

const backend = useBackendClient()
// or: const { $convex } = useNuxtApp()

const result = await backend.query(api.messages.list, {})
</script>
```

The [client reference](https://nuxt-backend.dev/docs/reference/nuxt/client) lists its methods.

<warning>

There is no server-side `$convex` injection. In Nitro routes and other server code, use [server utilities](https://nuxt-backend.dev/docs/data/server-rendering) (`fetchQuery`, `fetchMutation`, …) instead.

</warning>

## Connection state

```vue
<script setup lang="ts">
const connection = useConnectionState()
</script>

<template>
  <span v-if="!connection.isWebSocketConnected">Offline</span>
</template>
```

<demo-connection>



</demo-connection>


## Sitemap

See the full [sitemap](https://nuxt-backend.dev/sitemap.md) for all pages.
