项目结构

View source
按业务领域组织 API 接口、类型定义和目录结构

按业务领域组织

推荐按业务领域(domain)而非技术层级来组织代码。当项目有 10+ 个接口时,扁平化的 api/ 目录会变得难以维护。

Nuxt 4 引入了 shared/ 目录(v3.14+),其中 shared/types/shared/utils/ 会被自动导入到 Vue app 和 Nitro server 两侧,适合放置前后端共享的类型和工具函数。

shared/ 目录中的代码不能导入任何 Vue 或 Nitro 专有代码(如 refdefineEventHandler 等)。非 shared/utils/shared/types/ 下的文件不会被自动导入,需通过 #shared 别名手动导入。

完整示例

类型定义

前后端共享的业务实体和通用类型放在 shared/types/(会被自动导入到 app 和 server):

shared/types/common.ts
export interface User {
  id: string
  name: string
  email: string
  role: UserRole
}

export type UserRole = 'admin' | 'editor' | 'viewer'
shared/types/api.ts
/** 分页查询参数 */
export interface PageQuery {
  page?: number
  pageSize?: number
}

/** 分页响应 */
export interface PageResult<T> {
  items: T[]
  total: number
}

/** 通用 API 响应包装 */
export interface ApiResponse<T> {
  code: number
  data: T
  message: string
}

仅 app 侧使用的接口入参/出参类型放在各模块的 types.ts

app/api/modules/user/types.ts
// User、UserRole 等实体类型已在 shared/types/ 中,自动导入,无需 import

export interface CreateUserRequest {
  name: string
  email: string
  role: UserRole
}

export interface UserListQuery extends PageQuery {
  keyword?: string
}

// 直接复用 shared 中的通用分页类型
export type UserListResponse = PageResult<User>

API 方法

每个模块的 index.ts 封装该领域的所有 API 调用。使用 Movk Nuxt 提供的 $api 实例,无需手动创建 $fetch.create()

app/api/modules/user/index.ts
import type { CreateUserRequest, UserListQuery, UserListResponse } from './types'
// User 类型已在 shared/types/ 中定义,自动导入

export function fetchUsers(query: UserListQuery) {
  return useNuxtApp().$api<UserListResponse>('/users', { query })
}

export function fetchUser(id: string) {
  return useNuxtApp().$api<User>(`/users/${id}`)
}

export function createUser(data: CreateUserRequest) {
  return useNuxtApp().$api<User>('/users', { method: 'POST', body: data })
}

export function deleteUser(id: string) {
  return useNuxtApp().$api(`/users/${id}`, { method: 'DELETE' })
}

在 server route 中,也可以直接使用 shared/types/ 中的类型(自动导入):

server/api/users/index.get.ts
// User、PageResult 等类型自动导入,无需 import
export default defineEventHandler(async (): Promise<PageResult<User>> => {
  const users = await db.user.findMany()
  const total = await db.user.count()
  return { items: users, total }
})

在 Composable 中使用

app/composables/useUserActions.ts
import { createUser, deleteUser } from '~/api/modules/user'
import type { CreateUserRequest } from '~/api/modules/user/types'

export function useUserActions() {
  async function remove(id: string) {
    await deleteUser(id)
    await refreshNuxtData()
  }

  async function create(data: CreateUserRequest) {
    return await createUser(data)
  }

  return { remove, create }
}

或者直接使用 useApiFetch 进行响应式数据获取:

app/composables/useUserList.ts
import type { UserListResponse } from '~/api/modules/user/types'

export function useUserList() {
  const query = ref({ page: 1, pageSize: 20, keyword: '' })

  // useApiFetch 自动基于 URL + query 生成缓存 key
  const { data, status, refresh } = useApiFetch<UserListResponse>('/users', {
    query,
    watch: [query],
  })

  return {
    users: computed(() => data.value?.items ?? []),
    total: computed(() => data.value?.total ?? 0),
    status,
    refresh,
    query,
  }
}

类型复用策略

类型位置适用范围示例
shared/types/common.ts前后端共享的业务实体UserOrderFileInfo
shared/types/api.ts前后端共享的 API 通用结构PageQueryPageResult<T>ApiResponse<T>
app/api/modules/user/types.ts仅 app 侧使用的接口入参/出参CreateUserRequestUserListQuery
组件文件内或 app/Vue 专有类型(依赖 Vue)PropsEmits、组件类型

与 Movk Nuxt 的集成

如果不使用 Movk Nuxt,你需要手动搭建以下基础设施:

plugins/api.ts (手动搭建)
// 1. 插件中创建自定义 $fetch 实例
export default defineNuxtPlugin(() => {
  const api = $fetch.create({
    baseURL: useRuntimeConfig().public.apiBase,
    onRequest({ options }) {
      // 手动注入 auth token
      const token = useCookie('token')
      if (token.value) {
        options.headers.set('Authorization', `Bearer ${token.value}`)
      }
    },
    onResponseError({ response }) {
      // 手动处理 401
      if (response.status === 401) {
        navigateTo('/login')
      }
      // 手动显示 toast
      if (import.meta.client) {
        useToast().error(response._data?.message ?? '请求失败')
      }
    },
  })
  return { provide: { api } }
})

使用 Movk Nuxt 后,上述代码全部由模块配置替代:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@movk/nuxt'],
  movk: {
    api: {
      endpoints: {
        default: { baseURL: '/api' },
      },
      auth: { enabled: true, tokenPath: 'user.accessToken' },
      toast: { success: true, error: true },
    },
  },
})
Copyright © 2025 - 2026 YiXuan - MIT License