useApiFetch

View source
基于 Nuxt useFetch 封装的 API 请求 composable,提供自动认证、业务状态码检查和 Toast 提示。

用法

使用自动导入的 useApiFetch composable 进行 API 请求,基于 Nuxt useFetch 封装,提供自动认证、数据解包、业务状态码检查和统一的错误处理。

<script setup lang="ts">
interface User {
  id: number
  name: string
  email: string
}

// 基础用法(自动解包 data 字段)
const { data, pending, error, refresh } = await useApiFetch<User[]>('/users')

// POST 请求
const { data } = await useApiFetch('/users', {
  method: 'POST',
  body: { name: 'test', email: 'test@example.com' }
})

// 使用其他端点
const { data } = await useApiFetch('/users', { endpoint: 'v2' })
</script>
  • useApiFetch 继承 Nuxt useFetch 的所有功能,同时提供额外的 API 集成特性。
  • 自动从 session 获取 token 并添加到请求头(通过 $api 实例)。
  • 自动检查业务状态码并抛出错误。
  • 内置 Toast 提示,支持自定义配置。

数据请求模式

Movk Nuxt 提供三种数据请求 composable,签名一致,按执行时机选择:

Composable执行时机阻塞导航场景
useApiFetchSSR + CSR首屏核心数据
useLazyApiFetchSSR + CSR次要 / 非首屏数据
useClientApiFetch仅 CSR非 SEO 敏感数据

useApiFetch 在 SSR 阶段就发起请求,首屏直出数据:

ready
{
  "id": "me",
  "name": "Movk Demo",
  "email": "demo@movk.dev",
  "avatar": "/avatar.png",
  "role": "developer",
  "issuedAt": "2026-06-21T09:40:29.099Z"
}
<script setup lang="ts">
interface Profile {
  id: string
  name: string
  email: string
  role: string
}

const { data, pending, error, refresh } = await useApiFetch<Profile>('/profile')
</script>

<template>
  <div class="flex flex-col gap-3">
    <div class="flex items-center gap-2 flex-wrap">
      <UBadge v-if="pending" color="warning" variant="subtle">
        pending
      </UBadge>
      <UBadge v-else-if="error" color="error" variant="subtle">
        error
      </UBadge>
      <UBadge v-else color="success" variant="subtle">
        ready
      </UBadge>
      <UButton size="sm" variant="outline" icon="i-lucide-refresh-cw" @click="refresh()">
        刷新
      </UButton>
    </div>
    <pre class="text-xs p-3 rounded bg-elevated overflow-auto">{{ error ? { error: error.message } : data }}</pre>
  </div>
</template>

数据解包与转换

useApiFetch 接收到的已是 $api 解包后的业务数据,泛型 T 直接声明该数据类型;transform 接收解包后的数据做二次转换:

// 泛型 T 直接声明业务数据类型(即 data 字段的类型)
const { data } = await useApiFetch<User>('/user')
// data.value = { id: 1, name: 'test' }

// 结合 transform 做二次转换
const { data } = await useApiFetch<{ content: User[] }, SelectItem[]>('/users', {
  transform: ({ content }) => content.map(u => ({ label: u.name, value: u.id }))
})
解包机制、skipUnwrap / skipBusinessCheck 的交互示例见 $api

业务状态码检查

$api 拦截器自动检查业务状态码,不在 successCodes 中时抛出 ApiError(见下方错误分类)。传 skipBusinessCheck: true 跳过校验(不抛 ApiError),但仍解包 dataKey 字段:

const { data } = await useApiFetch('/external', {
  skipBusinessCheck: true
})
业务校验与 skipBusinessCheck 的交互示例见 $api

错误分类

模块只产出 ApiError(业务错误)一种自定义错误,其余均为 ofetch 原生 FetchError;通过 isBusinessErrorstatusCode 区分场景。下拉切换错误类型观察 error.value 形态:

  • business:HTTP 200 但 code 不在 successCodes 内 → ApiErrorisBusinessError 为 true。
  • http-400 / http-500:原生 FetchErrorstatusCode 为对应码。
  • http-422FetchErrordata 字段携带服务端补充信息。
  • network:连接被强制断开 → 无 statusCodeFetchError

模块只产出 ApiError(业务错误)一种自定义错误,其余均为 ofetch 原生 FetchError;通过 isBusinessErrorstatusCode 区分场景。

<script setup lang="ts">
import type { ApiError } from '@movk/nuxt'
import type { FetchError } from 'ofetch'

const props = defineProps<{
  mode: 'business' | 'http-400' | 'http-422' | 'http-500' | 'network'
}>()

const urlMap: Record<typeof props.mode, string> = {
  'business': '/demo/errors?type=business',
  'http-400': '/profile?fail=1',
  'http-422': '/demo/errors?type=422',
  'http-500': '/demo/errors?type=500',
  'network': '/demo/errors?type=network'
}

const { error, execute } = useApiFetch(() => urlMap[props.mode], {
  immediate: false,
  toast: false
})

const info = computed(() => {
  const err = error.value
  if (!err) return null
  const apiErr = err as Partial<ApiError>
  const fetchErr = err as FetchError
  return {
    kind: apiErr.isBusinessError ? 'ApiError(业务错误)' : 'FetchError',
    statusCode: apiErr.statusCode ?? fetchErr.statusCode ?? null,
    message: err.message,
    isBusinessError: apiErr.isBusinessError ?? false,
    data: fetchErr.data ?? null
  }
})
</script>

<template>
  <div class="flex flex-col gap-3">
    <UButton size="sm" color="error" variant="outline" icon="i-lucide-circle-alert" @click="execute()">
      触发 {{ mode }}
    </UButton>
    <p class="text-xs text-muted">
      模块只产出 <code>ApiError</code>(业务错误)一种自定义错误,其余均为 ofetch 原生 <code>FetchError</code>;通过 <code>isBusinessError</code><code>statusCode</code> 区分场景。
    </p>
    <pre class="text-xs p-3 rounded bg-elevated overflow-auto">{{ info }}</pre>
  </div>
</template>

Toast 提示

toast 选项支持请求级配置:false 全关、{ success: false } 仅留错误、successMessage / errorMessage 快捷文本,或传完整 Toast 属性(coloriconduration 等):

// 关闭 Toast / 仅保留错误提示 / 快捷文本
await useApiFetch('/users', { toast: false })
await useApiFetch('/users', { toast: { success: false } })
await useApiFetch('/users', {
  toast: { successMessage: '创建成功!', errorMessage: '创建失败,请重试' }
})

// 完整 Toast 属性
await useApiFetch('/users', {
  toast: {
    success: { title: '创建成功', color: 'success', icon: 'i-lucide-circle-check' },
    error: { title: '创建失败', color: 'error', duration: 5000 }
  }
})
五种 Toast 模式的交互示例见 $api

API

useApiFetch()

useApiFetch<T = unknown, DataT = T>(url: string | (() => string), options?: UseApiFetchOptions<T, DataT>): UseApiFetchReturn<DataT>

创建 API 请求。

Parameters

url
string | (() => string) required
请求 URL 或返回 URL 的函数。支持响应式 URL。
options
UseApiFetchOptions<T, DataT>
请求配置选项。支持所有 Nuxt useFetch 选项,以及额外的 API 集成选项。

Type Parameters

T
type
业务数据类型(已由 $api 自动解包)。即 API 响应中 data 字段的数据类型。
DataT
type
transform 转换后的最终类型。默认等于 T

Returns

返回 useFetch 的响应对象,包含以下属性:

data
Ref<DataT | null>
响应数据(已解包和转换)。初始值为 null,请求成功后更新为实际数据。
error
Ref<FetchError | ApiError | null>
错误对象。网络错误为 FetchError,业务状态码错误为 ApiError(包含 statusCoderesponseisBusinessError 属性)。
status
Ref<'idle' | 'pending' | 'success' | 'error'>
请求状态。
  • 'idle' - 尚未开始(仅 immediate: false 时)
  • 'pending' - 请求进行中
  • 'success' - 请求成功
  • 'error' - 请求失败
pending
Ref<boolean>
是否正在请求中。等价于 status.value === 'pending'
refresh
(opts?: { dedupe?: boolean }) => Promise<void>
刷新数据,重新执行请求。别名: execute
execute
(opts?: { dedupe?: boolean }) => Promise<void>
refresh 的别名。手动执行请求(常用于 immediate: false 时)。
clear
() => void
清空状态。将 data 设为 nullerror 设为 nullstatus 设为 'idle'

Changelog

No recent changes
Copyright © 2025 - 2026 YiXuan - MIT License