Nuxt Server Routes vs. API Routes: Choosing the Right Approach

Nuxt 3 blurs the line between frontend and backend by letting you create server logic directly inside your Nuxt app. That’s powerful—but it also raises a common question: should you use Nuxt server routes or a separate API service?

What are Nuxt server routes?

Nuxt server routes are backend endpoints defined inside your Nuxt app, powered by Nitro. They live in the /server directory and are deployed along with your frontend.

Basic example

Create a file at server/api/hello.get.ts:

export default defineEventHandler((event) => {
  return { message: 'Hello from Nuxt server route!' }
})

This automatically exposes a GET endpoint at /api/hello in your app.

Key characteristics

What are API routes (separate backend)?

By “API routes” here, we mean a separate backend service—for example, a Node/Express app, a Laravel API, a Go service, or a hosted backend like Supabase or Firebase. Your Nuxt app becomes a client of that API.

Typical architecture

In this setup, Nuxt calls the API over HTTP, just like any other client.

Comparing Nuxt server routes and external API routes

1. Architecture and complexity

2. Performance and latency

3. Scaling and team boundaries

4. Reusability across clients

5. Security and exposure

When to use Nuxt server routes

Nuxt server routes shine when your backend needs are closely tied to your Nuxt app and you want to move quickly.

Great use cases

Example: BFF wrapper around a third‑party API

server/api/weather.get.ts:

export default defineEventHandler(async (event) => {
  const query = getQuery(event)
  const city = query.city as string

  const apiKey = process.env.WEATHER_API_KEY
  const url = `https://api.example.com/weather?city=${city}&key=${apiKey}`

  const data = await $fetch(url)
  return { city, temperature: data.temp, condition: data.condition }
})

Your frontend only calls /api/weather?city=Paris and never sees the external API key or raw response shape.

When to use a separate API backend

A dedicated API service makes more sense when your system needs to outgrow a single Nuxt app.

Great use cases

Example: Nuxt consuming an external API

In a Nuxt page or composable:

const { data, error } = await useFetch('https://api.example.com/v1/posts', {
  headers: {
    Authorization: `Bearer ${token}`
  }
})

Nuxt stays focused on rendering and UX, while the external API owns the data and business rules.

Hybrid approach: the sweet spot for many teams

In practice, many real‑world apps use a hybrid approach:

This gives you the best of both worlds: a reusable backend plus a Nuxt‑specific layer optimized for your UI and SSR.