Skip to content

useAsyncGraphqlQuery

This composable is a wrapper around Nuxt's useAsyncData that executes a GraphQL query via the middleware. It provides SSR-compatible, reactive data fetching with automatic refetching when variables change.

Basic Usage

typescript
const { data } = await useAsyncGraphqlQuery('users')

This is equivalent to:

typescript
const { data } = await useAsyncData(() => useGraphqlQuery('users'))

With Variables

Pass variables as the second argument:

typescript
const { data } = await useAsyncGraphqlQuery('filmById', { id: '123' })

Reactive Variables

When variables are wrapped in a computed ref, the query automatically refetches when variables change:

typescript
import type { UserByIdQueryVariables } from '#graphql-operations'

const route = useRoute()

const variables = computed<UserByIdQueryVariables>(() => {
  return {
    id: route.params.id.toString(),
  }
})

const { data } = await useAsyncGraphqlQuery('userById', variables)

The composable automatically adds reactive variables to the watch option of useAsyncData, triggering a refetch whenever the variables change.

Options

The third argument accepts options that extend AsyncDataOptions with additional GraphQL-specific settings:

typescript
const { data } = await useAsyncGraphqlQuery('users', null, {
  // All useAsyncData options are supported
  transform: (response) => response.data.users,
  default: () => [],
  immediate: true,
  server: true,

  // GraphQL-specific options
  graphqlCaching: { client: true },
  fetchOptions: { headers: { 'x-custom': 'value' } },
  clientContext: { language: 'en' },
})

transform

Transform the response before storing it in data. This is useful to extract nested data or modify the response structure:

typescript
const { data: users } = await useAsyncGraphqlQuery('users', null, {
  transform: (response) => {
    // response.data is UsersQuery
    return response.data.users
  },
})

// users.value is now the users array directly

graphqlCaching

Enable client-side caching for this query. Requires clientCache.enabled: true in module options. See Caching for more details.

typescript
const { data } = await useAsyncGraphqlQuery('users', null, {
  graphqlCaching: {
    client: true,
  },
})

When enabled:

  • Results are cached in memory using an LRU cache
  • Subsequent calls with the same variables return cached data
  • Cache survives client-side navigation
  • SSR payload data is preserved during hydration

fetchOptions

Pass options to the underlying $fetch call to the middleware endpoint:

typescript
const { data } = await useAsyncGraphqlQuery('users', null, {
  fetchOptions: {
    headers: {
      authorization: `Bearer ${token}`,
    },
    timeout: 5000,
  },
})

clientContext

Override or extend the global client context for this specific request. Values here take precedence over those defined in defineGraphqlClientOptions:

typescript
const { data } = await useAsyncGraphqlQuery('users', null, {
  clientContext: {
    language: 'de', // Override the global language for this request
  },
})

Return Value

Returns the same object as useAsyncData:

typescript
const {
  data, // Ref<T | undefined> - The response data
  pending, // Ref<boolean> - Loading state
  error, // Ref<Error | undefined> - Error if request failed
  status, // Ref<'idle' | 'pending' | 'success' | 'error'>
  refresh, // () => Promise<void> - Manually refetch data
  execute, // () => Promise<void> - Same as refresh
  clear, // () => void - Clear data and error
} = await useAsyncGraphqlQuery('users')

Type Safety

The composable is fully typed based on your GraphQL operations:

typescript
// Variables are type-checked
const { data } = await useAsyncGraphqlQuery('userById', {
  id: '123', // ✅ Correct type
  // id: 123  // ❌ Type error: expected string
})

// Response data is typed
if (data.value) {
  console.log(data.value.data.userById?.name) // ✅ Autocomplete works
}

Operations that require variables will show a type error if variables are omitted:

typescript
// ❌ Type error: variables required
const { data } = await useAsyncGraphqlQuery('userById')

// ✅ Correct
const { data } = await useAsyncGraphqlQuery('userById', { id: '123' })

Hot Module Reloading

During development, the composable automatically refetches data when the underlying GraphQL document or any used fragments change. This provides instant feedback when modifying queries.

Important Considerations

Execution Context

Like useAsyncData, this composable must be called in specific contexts:

  • Component setup function - At the root level, not inside callbacks
  • Plugin setup - During plugin initialization
  • Route middleware - During navigation
  • Other composables - When called from a composable that follows these rules
typescript
// ✅ Correct - at root of setup
const { data } = await useAsyncGraphqlQuery('users')

// ❌ Wrong - inside a callback
const onClick = async () => {
  // This won't work correctly with SSR
  const { data } = await useAsyncGraphqlQuery('users')
}

For fetching data inside event handlers or callbacks, use useGraphqlQuery instead.

Unique Keys

The composable automatically generates a unique key for useAsyncData based on:

  • The operation name
  • A hash of the variables

This ensures that different variable combinations are cached separately and multiple instances don't conflict.

Examples

Pagination

typescript
import type { UsersPaginatedQueryVariables } from '#graphql-operations'

const page = ref(1)
const limit = 10

const variables = computed<UsersPaginatedQueryVariables>(() => ({
  offset: (page.value - 1) * limit,
  limit,
}))

const { data, pending } = await useAsyncGraphqlQuery(
  'usersPaginated',
  variables,
  {
    transform: (response) => response.data.users,
  },
)

function nextPage() {
  page.value++
  // Query automatically refetches due to reactive variables
}

Conditional Fetching

typescript
const userId = ref<string | null>(null)

const variables = computed(() => (userId.value ? { id: userId.value } : null))

const { data } = await useAsyncGraphqlQuery('userById', variables, {
  // Don't fetch until we have a userId
  immediate: false,
})

// Later, when userId is set:
userId.value = '123'
// The query will now execute

Error Handling

typescript
const { data, error, refresh } = await useAsyncGraphqlQuery('users')

// Check for errors
if (error.value) {
  console.error('Query failed:', error.value.message)
}

// Retry on error
async function retry() {
  await refresh()
}

With Default Value

typescript
interface User {
  id: string
  name: string
}

const { data } = await useAsyncGraphqlQuery('users', null, {
  transform: (response) => response.data.users ?? [],
  default: () => [] as User[],
})

// data.value is never undefined, defaults to empty array

Comparison with useGraphqlQuery

FeatureuseAsyncGraphqlQueryuseGraphqlQuery
SSR Support✅ Built-in✅ When wrapped in useAsyncData
Reactive Variables✅ Automatic refetch❌ Manual handling required
ReturnsAsyncData objectPromise with response
Use in Callbacks❌ Not recommended✅ Works anywhere
Caching✅ With useAsyncData✅ With graphqlCaching option
HMR Refresh✅ Automatic❌ Manual

Choose useAsyncGraphqlQuery for page-level data fetching with SSR support. Choose useGraphqlQuery for imperative fetching in event handlers, plugins, or when you need more control.

Released under the MIT License.