Guide

Queries

Compose GET actions with Nuxt useAsyncData and forward needed authentication headers during SSR.

GET queries with Nuxt

Use a .get.ts action for reads, and compose it with Nuxt's existing useAsyncData for SSR payload reuse, refresh, and hydration. The module does not introduce a separate cache.

server/actions/get-stats.get.ts
import { createSafeActionClient } from '#safe-action'

export default createSafeActionClient().action(() => ({ status: 'ready' }))
<script setup lang="ts">
import { getStats } from '#safe-action/actions'

// During SSR, forward only the headers the action needs. On the client this is {}.
const headers = useRequestHeaders(['cookie'])
const action = useAction(getStats, { headers })

const { data, error, status, refresh } = await useAsyncData('action:stats', async () => {
  const result = await action.executeAsync(undefined)
  if (result.serverError !== undefined || result.validationErrors !== undefined) {
    throw createError({ statusCode: 500, statusMessage: 'Unable to load stats' })
  }
  // This read action returns an object; useAsyncData handlers should return defined data.
  if (result.data === undefined) throw createError({ statusCode: 500, statusMessage: 'Missing stats' })
  return result.data
})
</script>

<template>
  <p v-if="status === 'pending'">Loading…</p>
  <p v-else-if="error" role="alert">Unable to load stats</p>
  <p v-else>{{ data?.status }}</p>
  <button @click="refresh()">Refresh</button>
</template>

The example is an unauthenticated read, but the allowlisted cookie-forwarding pattern also applies to authenticated actions. Server-side global $fetch does not automatically forward the current browser request's cookies. Do not forward all incoming headers wholesale. Use the default transport on the server; detached browser fetch cannot resolve a relative URL in every server runtime.

For input-dependent reads, include stable serialized input in the useAsyncData key or use Nuxt's reactive key/watch options. Return only defined successful data. A void mutation is not a useful query handler. Nuxt owns the cached query data; useAction state is execution-local and is not hydrated query state.

Keep mutations POST/PUT/PATCH/DELETE and refresh relevant Nuxt keys after success. This module disables automatic transport retries; application retry policy and idempotency remain explicit.

Nuxt useAsyncData · Nuxt request headers

Copyright © 2026