Routing

Nitro support filesystem routing as well as defining route rules for maximum flexibility and performance.

Filesystem Routing

Nitro supports file-based routing for your API routes.

Handler files inside api/ and routes/ directory will be automatically mapped to unjs/h3 routes.

api/
  test.ts      <-- /api/test
routes/
  hello.ts     <-- /hello
nitro.config.ts
Some providers like Vercel use a top-level api/ directory as a feature, therefore routes placed in /api wont work. You will have to use routes/api/.
If you are using Nuxt, move the api/ and routes/ inside the server/ directory.

Simple route

// api/hello.ts
export default defineEventHandler(() => {
  return { hello: 'world' }
})

You can now universally call this API using await $fetch('/api/hello').

Route with params

// routes/hello/[name].ts
export default defineEventHandler(event => `Hello ${event.context.params.name}!`)
/hello/nitro
Hello nitro!

To include the /, use [...name].ts:

// routes/hello/[...name].ts
export default defineEventHandler(event => `Hello ${event.context.params.name}!`)
/hello/nitro/is/hot
Hello nitro/is/hot!

Specific request method

API route with a specific HTTP request method (get, post, put, delete, options and so on).

GET
// routes/users/[id].get.ts
export default defineEventHandler(async (event) => {
  const { id } = event.context.params
  // TODO: fetch user by id
  return `User profile!`
})
POST
// routes/users.post.ts
export default defineEventHandler(async event => {
  const body = await readBody(event)
  // TODO: Handle body and add user
  return { updated: true }
})

Check out h3 JSDocs for all available utilities like readBody.

Catch all route

// routes/[...].ts
export default defineEventHandler(event => `Default page`)

Route Rules

Nitro allows you to add logic at the top-level of your configuration, useful for redirecting, proxying, caching and adding headers to routes.

It is a map from route pattern (following unjs/radix3) to route options.

When cache option is set, handlers matching pattern will be automatically wrapped with defineCachedEventHandler.

See the Cache API for all available cache options.

swr: true|number is shortcut for cache: { swr: true, maxAge: number }

Example:

nitro.config.ts
export default defineNitroConfig({
  routeRules: {
    '/blog/**': { swr: true },
    '/blog/**': { swr: 600 },
    '/blog/**': { static: true },
    '/blog/**': { cache: { /* cache options*/ } },
    '/assets/**': { headers: { 'cache-control': 's-maxage=0' } },
    '/api/v1/**': { cors: true, headers: { 'access-control-allow-methods': 'GET' } },
    '/old-page': { redirect: '/new-page' },
    '/proxy/example': { proxy: 'https://example.com' },
    '/proxy/**': { proxy: '/api/**' },
  }
})
nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/blog/**': { swr: true },
    '/blog/**': { swr: 600 },
    '/blog/**': { static: true },
    '/blog/**': { cache: { /* cache options*/ } },
    '/assets/**': { headers: { 'cache-control': 's-maxage=0' } },
    '/api/v1/**': { cors: true, headers: { 'access-control-allow-methods': 'GET' } },
    '/old-page': { redirect: '/new-page' },
    '/proxy/example': { proxy: 'https://example.com' },
    '/proxy/**': { proxy: '/api/**' },
  }
})

Route Middleware

Nitro route middleware can hook into the request lifecycle.

A middleware can modify the request before it is processed, not after.

Middleware are auto-registered within the middleware/ directory.

routes/
  hello.ts
middleware/
  auth.ts
  logger.ts
  ...
nitro.config.ts

Simple Middleware

Middleware are defined exactly like route handlers with the only exception that they should not return anything. Returning from middleware behaves like returning from a request - the value will be returned as a response and further code will not be ran.

middleware/auth.ts
export default defineEventHandler((event) => {
  // Extends or modify the event
  event.context.user = { name: 'Nitro' }
})
Returning anything from a middleware will close the request and should be avoided! Any returned value from middleware will be the response and further code will not be executed however this is not recommended to do!

Execution Order

Middleware are executed in directory listing order.

middleware/
  auth.ts <-- First
  logger.ts <-- Second
  ... <-- Third

Prefix middleware with a number to control their execution order.

middleware/
  1.logger.ts <-- First
  2.auth.ts <-- Second
  3.... <-- Third

Request Filtering

Middleware are executed on every request.

Apply custom logic to scope them to specific conditions.

For example, you can use the URL to apply a middleware to a specific route:

middleware/auth.ts
export default defineEventHandler((event) => {
  // Will only execute for /auth route
  if (getRequestURL(event).startsWith('/auth')) {
    event.context.user = { name: 'Nitro' }
  }
})