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.
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' })