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/testroutes/  hello.ts     <-- /hellonitro.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.tsexport default defineEventHandler(() => {  return { hello: 'world' }})

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

Route with params

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

To include the /, use [].ts:

// routes/hello/[].tsexport default defineEventHandler(event => `Hello ${}!`)
Hello nitro/is/hot!

Specific request method

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

// routes/users/[id].get.tsexport default defineEventHandler(async (event) => {  const { id } = event.context.params  // TODO: fetch user by id  return `User profile!`})
// routes/ 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/[...].tsexport 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 }


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: '' },    '/proxy/**': { proxy: '/api/**' },  }})
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: '' },    '/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.tsmiddleware/  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.

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!

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:

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