Queries
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.
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.