Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/hey-api/openapi-ts/llms.txt

Use this file to discover all available pages before exploring further.

The Nuxt client provides native integration with Nuxt 3, offering composables like useFetch, useAsyncData, and SSR support out of the box.

Installation

npm install @hey-api/client-nuxt

Basic Usage

1

Create a client instance

import { createClient } from '@hey-api/client-nuxt';

export const client = createClient({
  baseURL: 'https://api.example.com',
});
2

Use with $fetch

<script setup>
import { client } from '~/lib/api-client';

const { data, error } = await client.get({
  url: '/users',
  composable: '$fetch',
});
</script>

<template>
  <div>
    <div v-if="error">Error: {{ error }}</div>
    <div v-else>
      <div v-for="user in data" :key="user.id">
        {{ user.name }}
      </div>
    </div>
  </div>
</template>
3

Use with useFetch

<script setup>
import { client } from '~/lib/api-client';

const { data, error, pending, refresh } = await client.get({
  url: '/users',
  composable: 'useFetch',
});
</script>

<template>
  <div>
    <div v-if="pending">Loading...</div>
    <div v-else-if="error">Error: {{ error }}</div>
    <div v-else>
      <div v-for="user in data" :key="user.id">
        {{ user.name }}
      </div>
      <button @click="refresh()">Refresh</button>
    </div>
  </div>
</template>

Configuration

The Nuxt client extends Nuxt’s fetch configuration:

Client Options

import { createClient } from '@hey-api/client-nuxt';

const client = createClient({
  // Base URL for all requests
  baseURL: 'https://api.example.com',

  // Default headers
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'your-api-key',
  },

  // Query parameters (can be refs)
  query: {
    version: 'v1',
  },

  // Authentication
  auth: async (auth) => getToken(),

  // Body serialization
  bodySerializer: (body) => JSON.stringify(body),

  // Query serialization
  querySerializer: {
    array: { style: 'form', explode: true },
    object: { style: 'deepObject', explode: true },
  },

  // Request validator
  requestValidator: async (data) => validateRequest(data),

  // Response transformer
  responseTransformer: async (data) => transformResponse(data),

  // Response validator
  responseValidator: async (data) => validateResponse(data),

  // Standard fetch options
  credentials: 'include',
  mode: 'cors',
  cache: 'default',

  // Nuxt-specific options
  retry: 1,
  retryDelay: 500,
  retryStatusCodes: [408, 409, 425, 429, 500, 502, 503, 504],
  timeout: 10000,
});

Composables

The Nuxt client supports all Nuxt composables:

$fetch

Direct fetch without reactivity:
const data = await client.get({
  url: '/users',
  composable: '$fetch',
});

console.log(data);

useFetch

Reactive data fetching with SSR support:
<script setup>
const { data, pending, error, refresh } = await client.get({
  url: '/users/{id}',
  path: { id: route.params.id },
  composable: 'useFetch',
});
</script>

useAsyncData

Custom async data handling:
<script setup>
const { data, pending, error, refresh } = await client.get({
  url: '/users',
  composable: 'useAsyncData',
  key: 'users-list',
  asyncDataOptions: {
    server: true,
    lazy: false,
    default: () => [],
  },
});
</script>

useLazyFetch

Lazy loading with useFetch:
<script setup>
const { data, pending, error } = await client.get({
  url: '/users',
  composable: 'useLazyFetch',
});
</script>

<template>
  <div>
    <div v-if="pending">Loading...</div>
    <div v-else-if="data">
      <!-- Render data -->
    </div>
  </div>
</template>

useLazyAsyncData

Lazy loading with useAsyncData:
<script setup>
const { data, pending } = await client.get({
  url: '/users',
  composable: 'useLazyAsyncData',
  key: 'users',
});
</script>

HTTP Methods

<script setup>
const { data, error } = await client.get({
  url: '/users',
  query: {
    page: 1,
    limit: 10,
  },
  composable: 'useFetch',
});
</script>

Reactive Parameters

Use Vue refs for reactive query parameters:
<script setup>
import { ref } from 'vue';

const page = ref(1);
const limit = ref(10);

const { data, refresh } = await client.get({
  url: '/users',
  query: {
    page,
    limit,
  },
  composable: 'useFetch',
});

// When page changes, the request automatically updates
function nextPage() {
  page.value++;
}
</script>

<template>
  <div>
    <UsersList :users="data" />
    <button @click="nextPage">Next Page</button>
  </div>
</template>

Authentication

import { useCookie } from '#app';

const client = createClient({
  baseURL: 'https://api.example.com',
  auth: async (auth) => {
    if (auth.scheme === 'bearer') {
      const token = useCookie('access_token');
      return token.value;
    }
  },
});

Token from Composable

<script setup>
import { useAuth } from '~/composables/useAuth';

const { token } = useAuth();

const { data } = await client.get({
  url: '/protected',
  headers: {
    Authorization: `Bearer ${token.value}`,
  },
  composable: 'useFetch',
});
</script>

Per-Request Security

const { data } = await client.get({
  url: '/protected',
  security: [
    {
      type: 'apiKey',
      in: 'header',
      name: 'X-API-Key',
    },
  ],
  composable: 'useFetch',
});

Server-Sent Events

Stream real-time data:
<script setup>
import { ref, onUnmounted } from 'vue';

const events = ref([]);
let stream;

onMounted(async () => {
  stream = await client.sse.get({
    url: '/events',
    onSseEvent: (event) => {
      events.value.push(event.data);
    },
    onSseError: (error) => {
      console.error('Stream error:', error);
    },
  });
});

onUnmounted(() => {
  stream?.close();
});
</script>

<template>
  <div>
    <div v-for="(event, i) in events" :key="i">
      {{ event }}
    </div>
  </div>
</template>

Caching and Refresh

Manual Refresh

<script setup>
const { data, refresh } = await client.get({
  url: '/users',
  composable: 'useFetch',
});

const refreshUsers = () => refresh();
</script>

<template>
  <div>
    <UsersList :users="data" />
    <button @click="refreshUsers">Refresh</button>
  </div>
</template>

Auto Refresh

<script setup>
const { data } = await client.get({
  url: '/stats',
  composable: 'useFetch',
  asyncDataOptions: {
    watch: [route.params.id],
  },
});
</script>

Cache Keys

<script setup>
const userId = route.params.id;

const { data } = await client.get({
  url: '/users/{id}',
  path: { id: userId },
  composable: 'useFetch',
  key: `user-${userId}`,
});
</script>

SSR Support

Server-Side Rendering

<script setup>
// This will run on the server during SSR
const { data } = await client.get({
  url: '/users',
  composable: 'useFetch',
  asyncDataOptions: {
    server: true, // Default
  },
});
</script>

Client-Only Fetching

<script setup>
const { data } = await client.get({
  url: '/users',
  composable: 'useFetch',
  asyncDataOptions: {
    server: false, // Skip SSR
  },
});
</script>

Error Handling

<script setup>
const { data, error, status } = await client.get({
  url: '/users',
  composable: 'useFetch',
});

const errorMessage = computed(() => {
  if (error.value) {
    return `Error ${status.value}: ${error.value.message}`;
  }
  return null;
});
</script>

<template>
  <div>
    <div v-if="errorMessage" class="error">
      {{ errorMessage }}
    </div>
    <div v-else>
      <!-- Render data -->
    </div>
  </div>
</template>

Advanced Examples

Parallel Requests

<script setup>
const [usersResult, postsResult] = await Promise.all([
  client.get({ url: '/users', composable: 'useFetch' }),
  client.get({ url: '/posts', composable: 'useFetch' }),
]);

const users = usersResult.data;
const posts = postsResult.data;
</script>

Dependent Requests

<script setup>
const { data: user } = await client.get({
  url: '/users/{id}',
  path: { id: route.params.id },
  composable: 'useFetch',
});

const { data: posts } = await client.get({
  url: '/users/{id}/posts',
  path: { id: user.value.id },
  composable: 'useFetch',
  asyncDataOptions: {
    immediate: false,
  },
});

watch(user, (newUser) => {
  if (newUser) {
    posts.refresh();
  }
});
</script>

Optimistic Updates

<script setup>
const { data: users, refresh } = await client.get({
  url: '/users',
  composable: 'useFetch',
});

const createUser = async (newUser) => {
  // Optimistic update
  users.value = [...users.value, { ...newUser, id: Date.now() }];

  try {
    const { data } = await client.post({
      url: '/users',
      body: newUser,
      composable: '$fetch',
    });
    
    // Refresh to get actual data
    await refresh();
  } catch (error) {
    // Revert on error
    await refresh();
  }
};
</script>

TypeScript Types

import type {
  Client,
  Config,
  RequestOptions,
  RequestResult,
  Composable,
} from '@hey-api/client-nuxt';

// Type-safe client wrapper
function createApiClient(config: Config): Client {
  return createClient({
    ...config,
    baseURL: useRuntimeConfig().public.apiUrl,
  });
}

// Typed composable
type UsersResponse = RequestResult<'useFetch', User[], Error>;

const users: UsersResponse = await client.get({
  url: '/users',
  composable: 'useFetch',
});

Best Practices

Choose the right composable for your use case:
  • $fetch for one-off requests
  • useFetch for reactive SSR data
  • useAsyncData for custom async logic
  • useLazyFetch for deferred loading
Use refs for parameters that should trigger re-fetching:
<script setup>
const search = ref('');

const { data } = await client.get({
  url: '/users',
  query: { search }, // Reactive!
  composable: 'useFetch',
});
</script>
Use unique keys for caching:
<script setup>
const { data } = await client.get({
  url: '/users/{id}',
  path: { id },
  key: `user-${id}`,
  composable: 'useFetch',
});
</script>
Always show loading indicators:
<template>
  <div v-if="pending">Loading...</div>
  <div v-else-if="error">Error: {{ error }}</div>
  <div v-else>
    <!-- Data -->
  </div>
</template>

Next Steps

Fetch Client

Learn about the underlying Fetch client

Composables

Explore Nuxt composables

SSR Support

Configure server-side rendering

Authentication

Set up API authentication