server/api Deep Dive

defineEventHandler + h3 Helpers

server/api Deep Dive

Server routes live under server/api/ and export defineEventHandler. h3 helpers read params, query, body, and cookies with minimal ceremony.

4 min read Level 2/5 #nuxt#server#api
What you'll learn
  • Create a dynamic route file under server/api with bracket-style params
  • Read route params with getRouterParam
  • Read JSON bodies with readBody and respond with a plain object

Every file under server/api/ becomes an HTTP route. The filename maps to the URL, and the default export — defineEventHandler — runs on every request.

A Dynamic Route

// server/api/users/[id].ts
export default defineEventHandler((event) => {
  const id = getRouterParam(event, 'id')
  return { id, name: 'Ada Lovelace' }
})

Request /api/users/42 and you’ll get { "id": "42", "name": "Ada Lovelace" }.

Filename Method Suffixes

To scope a route to a specific HTTP method, append .get.ts, .post.ts, etc:

server/api/users.get.ts    # GET /api/users
server/api/users.post.ts   # POST /api/users
server/api/users/[id].delete.ts  # DELETE /api/users/:id

Reading the Body

// server/api/users.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody<{ name: string; email: string }>(event)
  // ...persist to db...
  return { ok: true, body }
})

readBody auto-parses JSON, urlencoded, and (with helpers) multipart form data.

Reading the Query

// server/api/search.get.ts
export default defineEventHandler((event) => {
  const { q, page } = getQuery(event)
  return { results: [], q, page }
})

getQuery returns an object parsed from the URL search string.

Returning Anything

  • Plain object or array — serialized as JSON.
  • String — sent as text.
  • null — sent as empty.
  • Response — used directly.
  • Promise<T> — awaited.

Errors

Throw with createError to send a clean HTTP error:

throw createError({ statusCode: 404, statusMessage: 'User not found' })
server/middleware →