前言
在开发 SonicToolLab 的“汇率转换”和“每日一言”工具时,我遇到了典型的“前端困境”:
- 跨域报错 (CORS): 直接在 Vue 组件里
fetch('https://some-api.com'),浏览器直接报红,因为对方服务器不允许跨域。 - 裸奔的 Key: 如果我想调用 OpenAI 或其他收费 API,把 Key 写在前端代码里,无异于把钱包交给路人。
Nuxt 4 不仅仅是一个前端框架,它内置的 Nitro 引擎让它同时具备了后端能力。今天就来聊聊如何用 server/api 充当我们的“中间人”。
🛡️ 1. 原理:为什么需要中间层?
浏览器的同源策略限制了跨域,但服务器与服务器之间是没有跨域限制的。
- 错误做法: 浏览器 -> 外部 API (被拦截 ❌)
- 正确做法: 浏览器 -> Nuxt Server (同源 ✅) -> 外部 API (服务器请求 ✅) -> 返回数据
在 Nuxt 中,不需要配置复杂的 Nginx,只需要在 server/api 目录下新建文件即可。
🔐 2. 实战:隐藏你的 API Key
假设我们要开发一个“天气查询”工具,需要用到第三方的 API Key。
第一步:配置环境变量
千万不要把 Key 硬编码在代码里!在项目根目录创建 .env 文件:
# .env
NUXT_WEATHER_API_KEY=your_secret_key_here
然后在 nuxt.config.ts 中暴露它(注意:只在 server 端暴露,不在 public 中暴露):
TypeScript
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
// 私有配置,只能在服务端获取
weatherApiKey: '',
// public: { ... } // 公开配置,前端可以获取
}
})
第二步:编写服务端接口
创建 server/api/weather.get.ts。这个文件会自动映射为 /api/weather 路由。
TypeScript
// server/api/weather.get.ts
export default defineEventHandler(async (event) => {
// 1. 获取前端传来的参数
const query = getQuery(event)
const city = query.city || 'Beijing'
// 2. 获取配置中的 Key
const config = useRuntimeConfig()
const apiKey = config.weatherApiKey
// 3. 服务端发起请求(这里可以使用 $fetch)
try {
const data = await $fetch(`https://api.thirdparty.com/v1/weather`, {
params: {
q: city,
key: apiKey // Key 在这里使用,前端永远看不到
}
})
return data
} catch (e) {
throw createError({
statusCode: 500,
statusMessage: '天气服务暂时不可用'
})
}
})
第三步:前端调用
在 Vue 页面中,我们可以像调用本地函数一样调用这个 API:
Code snippet
<script setup>
const { data } = await useFetch('/api/weather', {
params: { city: 'Shanghai' }
})
</script>
效果: 用户在浏览器控制台的网络请求中,只能看到发往 /api/weather 的请求,完全看不到第三方的真实 URL 和那个珍贵的 API Key。
⚡️ 3. 进阶:利用缓存节省配额 (Cached Event Handler)
独立开发者的 API 额度通常有限(比如免费版一天只能调 1000 次)。如果用户频繁刷新页面,额度很快就用完了。
Nuxt 4 (Nitro) 提供了一个神级功能:服务端缓存。
我们只需要把 defineEventHandler 改为 defineCachedEventHandler:
TypeScript
// server/api/currency.get.ts
export default defineCachedEventHandler(async (event) => {
const config = useRuntimeConfig()
// 请求汇率接口
const data = await $fetch('[https://api.exchangerate.com/v1/latest](https://api.exchangerate.com/v1/latest)', {
headers: { Authorization: config.currencyKey }
})
return data
}, {
// 🔥 核心配置
maxAge: 60 * 60, // 缓存 1 小时
name: 'currency-rates',
getKey: (event) => 'global-rates' // 缓存键名
})
发生了什么?
- 第一个用户请求接口,Nuxt 服务器去第三方拉取数据,存入缓存,返回给用户。
- 接下来的 1 小时内,无论有多少用户访问,Nuxt 直接返回缓存的数据,不会向第三方发起任何请求。
- 这对于汇率、天气这种不需要秒级更新的数据,简直是省钱神器!
⚠️ 4. 避坑指南:Cloudflare 环境下的 Fetch
如果你像我一样部署在 Cloudflare Pages,底层的 Node.js 环境其实是 workerd (V8 Isolate)。
在极少数情况下,某些 Node 原生模块(如 axios 的某些适配器)可能不兼容。
建议: 在 Nuxt 服务端接口中,始终使用 Nuxt 内置的 $fetch 工具,它针对各种运行时(Node, Deno, Cloudflare Workers)都做了完美兼容。
总结
通过 Nuxt 4 的服务端路由,我们不需要额外部署一个后端服务(Go/Python/Java),就能在一个项目中解决:
- 跨域代理
- 隐藏 API Key
- 接口缓存
这让 SonicToolLab 这样的纯前端项目,也能拥有处理复杂业务的能力。
下一篇,我们将进入“优化篇”,聊聊 《Nuxt 4 项目结构优化:如何自动导入与模块化管理,告别满屏 import》 。