Nuxt4 服务端接口
Nuxt 是全栈框架,server/ 目录可以直接写后端 API 接口,前后端在一个项目里,数据层到界面层的闭环不用再拆两个仓库。
server/ 目录与 Nitro
Nuxt 的服务端基于 Nitro 引擎。在 server/ 目录下创建文件即生成接口,文件名决定了请求方法:
server/api/
├── users.get.ts → GET /api/users
├── users.post.ts → POST /api/users
├── users/[id].get.ts → GET /api/users/:id
└── users/[id].delete.ts → DELETE /api/users/:id
文件名后缀 .get、.post 等就是 HTTP 方法,没有后缀则所有方法都匹配。
第一个接口
创建 server/api/hello.get.ts:
export default defineEventHandler(() => {
return { message: '你好,Nuxt' }
})
启动开发服务器,访问 http://localhost:3000/api/hello,会返回:
{ "message": "你好,Nuxt" }
defineEventHandler 是 Nuxt 提供的接口处理函数包装器,返回值会被自动序列化为 JSON。
常用请求处理
查询参数
// server/api/search.get.ts
export default defineEventHandler((event) => {
const query = getQuery(event)
return { keyword: query.keyword }
})
访问 /api/search?keyword=nuxt 返回 { "keyword": "nuxt" }。
路径参数
// server/api/users/[id].get.ts
export default defineEventHandler((event) => {
const id = getRouterParam(event, 'id')
// 实际项目这里会查数据库
return { id, name: `用户${id}` }
})
访问 /api/users/7 返回 { "id": "7", "name": "用户7" }。
请求体
// server/api/users.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event) // 读取 JSON 请求体
return { received: body }
})
用 $fetch 测试:
const res = await $fetch('/api/users', {
method: 'POST',
body: { name: '张三' }
})
响应状态码
export default defineEventHandler((event) => {
setResponseStatus(event, 201) // 设置状态码
return { ok: true }
})
错误返回
接口出错时返回 4xx 状态码,客户端才能正确处理:
// server/api/users/[id].get.ts
export default defineEventHandler((event) => {
const id = getRouterParam(event, 'id')
if (id === '999') {
throw createError({
statusCode: 404,
statusMessage: '用户不存在'
})
}
return { id, name: `用户${id}` }
})
客户端 useFetch 会把这个错误放进 error 中,见错误处理。
完整增删改查示例
结合上面所学,一个简单的用户接口:
// server/api/users/index.get.ts
const users = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
]
export default defineEventHandler(() => {
return users
})
// server/api/users/[id].delete.ts
export default defineEventHandler((event) => {
const id = getRouterParam(event, 'id')
return { success: true, deletedId: id }
})
提示:示例中数据存在内存里,重启服务就清空了。真实项目请配合数据库使用,可以参考本博客的Supabase 教程。
客户端调用
服务端接口写好后,前端用 useFetch 直接调用站内路径即可,SSR 模式下接口在服务端请求,数据直接进页面 HTML:
<script setup>
const { data: users, refresh } = await useFetch('/api/users')
async function removeUser(id) {
await $fetch(`/api/users/${id}`, { method: 'DELETE' })
refresh() // 刷新列表
}
</script>
<template>
<ul>
<li v-for="user in users" :key="user.id">
{{ user.name }}
<button @click="removeUser(user.id)">删除</button>
</li>
</ul>
</template>
前后端共享代码
shared/ 目录下的代码前后端都能引用,适合放公共常量、工具函数。比如把用户列表的初始数据抽出来共用:
// shared/data/users.js
export const demoUsers = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
]
// server/api/users.get.ts
import { demoUsers } from '~/shared/data/users'
export default defineEventHandler(() => {
return demoUsers
})
接口速查表
| 需求 | 写法 |
|---|---|
| 查询参数 | getQuery(event) |
| 路径参数 | getRouterParam(event, 'id') |
| 请求体 | readBody(event) |
| 设置状态码 | setResponseStatus(event, 201) |
| 抛错 | throw createError({ statusCode, statusMessage }) |
| 方法限定 | 文件名后缀 .get.ts、.post.ts 等 |
一句话总结:在 server/api/ 建文件即接口,方法由文件名后缀决定,用 getQuery、getRouterParam、readBody 取数据,前端 useFetch 直接调站内路径完成闭环。