跳到主要內容
前端開發

Nuxt 3 / 4 用 Server API 做代理與轉發

整理 Nuxt 3 與 Nuxt 4 使用 server/api 轉發第三方 API 的方式,可以避免瀏覽器 CORS 問題、隱藏 API Key,也能在伺服器端加入快取。

W

Nuxt 2 時期常使用 @nuxtjs/proxy 把 Nuxt 當成代理伺服器來解決 CORS。

到了 Nuxt 3,Nuxt 已經內建 Nitro Server;Nuxt 4 也延續相同架構,所以不一定需要另外安裝 proxy 套件。

可以簡單理解成:

瀏覽器
↓
自己的 Nuxt API
↓
第三方 API

前端不直接連第三方服務,而是先請 Nuxt Server 幫忙取得資料。

建立自己的 API

例如建立:

server/api/posts.ts

內容:

export default defineEventHandler(async () => {
  return await $fetch('https://backend.example.com/posts')
})

Nuxt 會自動建立:

/api/posts

前端就可以:

const posts = await $fetch('/api/posts')

流程會變成:

瀏覽器
↓
/api/posts
↓
Nuxt Server
↓
https://backend.example.com/posts

瀏覽器只需要和自己的網站溝通。

第三方 API 的請求則由伺服器發出,因此不會受到瀏覽器 CORS 限制。

server/api 和 server/routes 的差別

Nuxt 3 與 Nuxt 4 都有:

server/
├── api/
└── routes/

放在:

server/api/posts.ts

網址會自動加上 /api:

/api/posts

放在:

server/routes/posts.ts

則會直接變成:

/posts

一般自己提供 API 給前端呼叫時,使用 server/api/ 就可以。

隱藏 API Key

如果第三方服務需要 API Key,不應該把金鑰放在前端。

例如:

export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig(event)

  return await $fetch('https://backend.example.com/posts', {
    headers: {
      Authorization: `Bearer ${config.apiKey}`
    }
  })
})

nuxt.config.ts:

export default defineNuxtConfig({
  runtimeConfig: {
    apiKey: ''
  }
})

真正部署時再透過環境變數提供金鑰。

這樣瀏覽器只會看到:

/api/posts

不會拿到真正的 API Key。

可以順便加快取

如果第三方 API 不需要每次都重新取得,也可以在 Server API 加上快取:

export default defineCachedEventHandler(
  async () => {
    return await $fetch(
      'https://backend.example.com/posts'
    )
  },
  {
    maxAge: 60 * 10
  }
)

代表:

快取 10 分鐘

這種方式很適合:

天氣資料
公開資訊
新聞列表
第三方 API
更新頻率不高的資料

可以減少重複呼叫上游 API。

不要做成任意網址代理

不要寫成:

export default defineEventHandler(async (event) => {
  const targetUrl = getQuery(event).url

  return await $fetch(targetUrl)
})

例如讓使用者可以:

/api/proxy?url=https://任意網址

這等於讓任何人都能利用你的伺服器去請求其他網站。

可能造成:

被拿來當免費代理
浪費流量
攻擊其他服務
存取原本不該公開的內部網址

比較安全的方式是:

export default defineEventHandler(async () => {
  return await $fetch(
    'https://backend.example.com/posts'
  )
})

也就是:

Nuxt Server 要連哪個 API,由你自己決定,不讓使用者直接傳完整網址。

如果真的需要動態參數,可以只接受明確的 ID 或固定範圍。

例如:

export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id')

  return await $fetch(
    `https://backend.example.com/posts/${id}`
  )
})

Nuxt 2、3、4 差在哪?

Nuxt 2Nuxt 3Nuxt 4
Server API需要另外處理Nitro 內建Nitro 內建
server/api/沒有現在這套方式支援支援
Proxy 套件常用 @nuxtjs/proxy通常不需要通常不需要
隱藏 API Key另外處理runtimeConfigruntimeConfig
API 快取另外處理Nitro CacheNitro Cache

所以從 Nuxt 3 升到 Nuxt 4:

server/api/ 的基本使用方式沒有太大改變。

Nuxt 4 最大的目錄變化是在前端程式搬進:

app/

而:

server/

仍然留在專案根目錄。

小結

如果前端需要呼叫第三方 API,可以考慮:

前端
↓
Nuxt /api
↓
第三方 API

最基本的寫法:

// server/api/posts.ts

export default defineEventHandler(async () => {
  return await $fetch(
    'https://backend.example.com/posts'
  )
})

這樣可以順便處理:

CORS
API Key
快取
資料整理

Nuxt 3 與 Nuxt 4 都可以使用相同做法。

參考資料

分享這篇