Server Routes in Nuxt

Learn how to create full backend APIs within the Nuxt 3 framework using server routes. This guide covers endpoint creation, request handling, response types, and the underlying technologies like Nitro and h3.

Michael Thiessen
Nuxt 3

The Mastering Nuxt FullStack Unleashed Course is here!

Get notified when we release new tutorials, lessons, and other expert Nuxt content.

Click here to view course

With server routes, we can build a full backend API for our app without leaving the Nuxt framework at all.

In this article, you'll learn:

  • How to create a basic server route
  • What the server/routes and server/api folders are for
  • How to process request data
  • How to return different types of responses
  • How server routes work under the hood
  • What Nitro and h3 are

So, let's get started!

What Are Server Routes?

Server routes in Nuxt let you create API endpoints directly in your project. You can build a complete backend without switching to a separate framework or technology.

These routes live in either the server/routes or server/api folders, with the latter being prefixed with /api in the URL.

Building Your First Server Route

Let's build our server route step by step. We'll start with a simple endpoint and then improve it.

Step 1: Create a Basic Endpoint

First, we'll create a new file in the server/routes directory:

// server/routes/ai.ts
export default defineEventHandler(() => {
  return {
    message: "Hello from the API"
  }
})

This creates a basic endpoint that returns a simple JSON object. If we restart our Nuxt server and navigate to /ai in the browser, we'll see this JSON response.

Step 2: Moving to the API Folder

But we want this endpoint to be prefixed with /api. To do this, we need to move our file to the server/api folder:

// server/api/ai.ts
export default defineEventHandler(() => {
  return {
    message: "Hello from the API"
  }
})

Now our endpoint is available at /api/ai.

Step 3: Adding Request Processing

Let's improve our endpoint to process incoming data. We'll use the event parameter to access request details and the readBody function to extract data from the request:

// server/api/ai.ts
export default defineEventHandler(async (event) => {
  // Extract data from the request body
  const body = await readBody(event)
  const { messages } = body

  // Process the extracted data
  const id = messages.length.toString()
  const lastMessage = messages[messages.length - 1]

  // Return a response that mimics an AI assistant
  return {
    id,
    role: 'assistant',
    content: `(server) You said: ${lastMessage.content}`,
  }
})

This version extracts messages from the request body, identifies the last message, and returns a response that echoes back that message. We're mocking what an AI assistant response would look like, which we'll replace with a real AI integration later.

Working with Different Response Types

Server routes can return various types of responses:

// server/api/ai.ts
export default defineEventHandler(async (event) => {
  // Different response types examples below

  // Get request data if needed
  const body = await readBody(event)
  const { messages } = body

  const id = messages.length.toString()
  const lastMessage = messages[messages.length - 1]

  return {
    id,
    role: 'assistant',
    content: `(server) You said: ${lastMessage.content}`,
  }
})

JSON Objects (Most Common)

You'll most often return JSON objects from your API endpoints. This is perfect for structured data:

return {
  id: "message-123",
  timestamp: Date.now(),
  data: {
    name: "User",
    messages: ["Hello", "How are you?"]
  }
}

JSON is the standard format for web APIs and works seamlessly with frontend JavaScript.

Error Responses

For error handling, use the createError function to return specific status codes:

// Authentication error
return createError({
  statusCode: 401,
  statusMessage: 'Unauthorized access'
})

// Resource not found
return createError({
  statusCode: 404,
  statusMessage: 'Resource not found'
})

// Server error
return createError({
  statusCode: 500,
  statusMessage: 'Internal server error'
})

This creates proper HTTP error responses that your frontend can effectively process and react to.

String Responses

Sometimes a simple string is all you need:

// Plain text response
return "Success!"

// Or dynamically generated text
return `Hello, ${user.name}!`

Strings are automatically sent with the correct content-type headers for text responses.

Empty Responses

When you don't need to return any data:

// Returns a 204 No Content status
return null

This is useful for operations like deleting a resource or other actions where you simply need to indicate success without returning data.

Binary Data

You can also serve binary data like images or files:

// Setting headers and returning binary data
setResponseHeaders(event, {
  'content-type': 'image/png'
})

// Get image using unstorage
const storage = useStorage()
return await storage.getItemRaw(path);

How Server Routes Work

The defineEventHandler function is what creates our API endpoint. It takes a function that receives an event parameter, which represents the HTTP request and response.

Inside this function, we can:

  1. Extract data from the request using readBody(event) (or other methods from h3)
  2. Process that data in any way we need
  3. Return a response

The response can be:

  • A JSON object (most common)
  • A string
  • A status code
  • Null (returns a 204 No Content status)
  • and many others

Running our code on the server instead of in the browser gives us several advantages:

  1. Security — Sensitive operations happen on our server, not in the client
  2. Performance — Server-side processing is often faster
  3. Advanced Features — We can implement things like streaming responses (which we'll see in a future lesson)

Under the hood, Nuxt uses a server toolkit called Nitro, which in turn uses an HTTP server called h3. Nuxt, Nitro and h3 are part of the unjs ecosystem, which includes many tools that work great together.

Michael Thiessen
Michael is a passionate full time Vue.js and Nuxt.js educator. His weekly newsletter is sent to over 11,000 Vue developers, and he has written over a hundred articles for his blog and VueSchool.

Follow MasteringNuxt on