2022-09-13 12:54:31 +00:00
---
title: server
2022-11-21 15:51:39 +00:00
head.title: 'server/'
2022-10-06 09:15:30 +00:00
description: The server/ directory is used to register API and server handlers to your application.
2023-10-18 10:59:43 +00:00
navigation.icon: i-ph-folder-duotone
2022-09-13 12:54:31 +00:00
---
2023-10-18 10:59:43 +00:00
Nuxt automatically scans files inside these directories to register API and server handlers with HMR support.
2022-04-09 11:36:51 +00:00
2023-10-18 10:59:43 +00:00
```bash [Directory structure]
-| server/
---| api/
-----| hello.ts # /api/hello
---| routes/
-----| bonjour.ts # /bonjour
---| middleware/
-----| log.ts # log all requests
```
2022-04-09 11:36:51 +00:00
2023-05-16 11:16:09 +00:00
Each file should export a default function defined with `defineEventHandler()` or `eventHandler()` (alias).
2022-04-09 11:36:51 +00:00
2023-08-23 07:30:53 +00:00
The handler can directly return JSON data, a `Promise` , or use `event.node.res.end()` to send a response.
2022-04-09 11:36:51 +00:00
2022-08-16 08:14:03 +00:00
```ts [server/api/hello.ts]
2022-04-09 11:36:51 +00:00
export default defineEventHandler((event) => {
return {
2023-05-16 11:16:09 +00:00
hello: 'world'
2022-04-09 11:36:51 +00:00
}
})
```
2023-05-16 11:16:09 +00:00
You can now universally call this API in your pages and components:
```vue [pages/index.vue]
2023-07-18 10:31:45 +00:00
< script setup lang = "ts" >
2023-05-16 11:16:09 +00:00
const { data } = await useFetch('/api/hello')
< / script >
< template >
< pre > {{ data }}< / pre >
< / template >
```
2022-04-09 11:36:51 +00:00
## Server Routes
Files inside the `~/server/api` are automatically prefixed with `/api` in their route.
2023-05-16 11:16:09 +00:00
To add server routes without `/api` prefix, put them into `~/server/routes` directory.
2022-04-09 11:36:51 +00:00
**Example:**
2022-08-16 08:14:03 +00:00
```ts [server/routes/hello.ts]
2022-04-09 11:36:51 +00:00
export default defineEventHandler(() => 'Hello World!')
```
2022-08-16 08:14:03 +00:00
Given the example above, the `/hello` route will be accessible at < http: // localhost:3000 / hello > .
2022-04-09 11:36:51 +00:00
2023-10-18 10:59:43 +00:00
::callout
Note that currently server routes do not support the full functionality of dynamic routes as [pages ](/docs/guide/directory-structure/pages#dynamic-routes ) do.
2023-07-03 16:43:18 +00:00
::
2022-04-09 11:36:51 +00:00
## Server Middleware
Nuxt will automatically read in any file in the `~/server/middleware` to create server middleware for your project.
2022-08-16 08:14:03 +00:00
Middleware handlers will run on every request before any other server route to add or check headers, log requests, or extend the event's request object.
2022-04-09 11:36:51 +00:00
2023-10-18 10:59:43 +00:00
::callout
2022-04-09 11:36:51 +00:00
Middleware handlers should not return anything (nor close or respond to the request) and only inspect or extend the request context or throw an error.
::
**Examples:**
2022-08-16 08:14:03 +00:00
```ts [server/middleware/log.ts]
2022-04-09 11:36:51 +00:00
export default defineEventHandler((event) => {
2023-05-16 11:16:09 +00:00
console.log('New request: ' + getRequestURL(event))
2022-04-09 11:36:51 +00:00
})
```
2022-08-16 08:14:03 +00:00
```ts [server/middleware/auth.ts]
2022-04-09 11:36:51 +00:00
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})
```
2022-09-26 09:24:03 +00:00
## Server Plugins
Nuxt will automatically read any files in the `~/server/plugins` directory and register them as Nitro plugins. This allows extending Nitro's runtime behavior and hooking into lifecycle events.
**Example:**
```ts [server/plugins/nitroPlugin.ts]
export default defineNitroPlugin((nitroApp) => {
console.log('Nitro plugin', nitroApp)
})
```
2023-10-18 10:59:43 +00:00
:read-more{to="https://nitro.unjs.io/guide/plugins" title="Nitro Plugins" target="_blank"}
2022-09-26 09:24:03 +00:00
2022-04-22 16:11:25 +00:00
## Server Utilities
Server routes are powered by [unjs/h3 ](https://github.com/unjs/h3 ) which comes with a handy set of helpers.
2023-10-18 10:59:43 +00:00
:read-more{to="https://www.jsdocs.io/package/h3#package-index-functions" title="Available H3 Request Helpers" target="_blank"}
2022-04-22 16:11:25 +00:00
2022-08-16 08:14:03 +00:00
You can add more helpers yourself inside the `~/server/utils` directory.
2022-04-22 16:11:25 +00:00
2023-06-07 09:28:27 +00:00
For example, you can define a custom handler utility that wraps the original handler and performs additional operations before returning the final response.
**Example:**
```ts [server/utils/handler.ts]
2023-09-04 08:22:52 +00:00
import type { EventHandler, EventHandlerRequest } from 'h3'
2023-06-07 09:28:27 +00:00
2023-09-04 08:22:52 +00:00
export const defineWrappedResponseHandler = < T extends EventHandlerRequest , D > (
handler: EventHandler< T , D >
): EventHandler< T , D > =>
defineEventHandler< T > (async event => {
2023-06-07 09:28:27 +00:00
try {
// do something before the route handler
const response = await handler(event)
// do something after the route handler
return { response }
} catch (err) {
// Error handling
return { err }
}
})
```
2023-05-16 11:16:09 +00:00
## Server Types
2023-10-18 10:59:43 +00:00
::callout
2023-05-16 11:16:09 +00:00
This feature is available from Nuxt >= 3.5
::
To improve clarity within your IDE between the auto-imports from 'nitro' and 'vue', you can add a `~/server/tsconfig.json` with the following content:
```json [server/tsconfig.json]
{
"extends": "../.nuxt/tsconfig.server.json"
}
```
2023-10-18 10:59:43 +00:00
Although right now these values won't be respected when type checking ([`nuxi typecheck`](/docs/api/commands/typecheck)), you should get better type hints in your IDE.
2023-05-16 11:16:09 +00:00
2023-10-18 10:59:43 +00:00
## Recipes
2022-04-22 16:11:25 +00:00
2023-10-18 10:59:43 +00:00
### Route Parameters
2022-04-09 11:36:51 +00:00
2022-08-16 08:14:03 +00:00
Server routes can use dynamic parameters within brackets in the file name like `/api/hello/[name].ts` and be accessed via `event.context.params` .
2022-04-09 11:36:51 +00:00
2023-08-03 11:05:29 +00:00
```ts [server/api/hello/[name\\].ts]
2023-08-11 08:43:05 +00:00
export default defineEventHandler((event) => {
const name = getRouterParam(event, 'name')
2023-10-18 10:59:43 +00:00
2023-08-11 08:43:05 +00:00
return `Hello, ${name}!`
})
2022-04-09 11:36:51 +00:00
```
2023-10-18 10:59:43 +00:00
You can now universally call this API on `/api/hello/nuxt` and get `Hello, nuxt!` .
2022-04-09 11:36:51 +00:00
2022-04-22 16:11:25 +00:00
### Matching HTTP Method
2022-04-09 11:36:51 +00:00
Handle file names can be suffixed with `.get` , `.post` , `.put` , `.delete` , ... to match request's [HTTP Method ](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods ).
2022-08-16 08:14:03 +00:00
```ts [server/api/test.get.ts]
2022-04-09 11:36:51 +00:00
export default defineEventHandler(() => 'Test get handler')
```
2022-08-16 08:14:03 +00:00
```ts [server/api/test.post.ts]
2022-04-09 11:36:51 +00:00
export default defineEventHandler(() => 'Test post handler')
```
2022-08-16 08:14:03 +00:00
Given the example above, fetching `/test` with:
2022-04-09 11:36:51 +00:00
- **GET** method: Returns `Test get handler`
- **POST** method: Returns `Test post handler`
2022-09-22 13:22:46 +00:00
- Any other method: Returns 405 error
2022-04-09 11:36:51 +00:00
2023-10-18 10:59:43 +00:00
You can also use `index.[method].ts` inside a directory for structuring your code differently, this is useful to create API namespaces.
2023-09-27 14:13:03 +00:00
2023-10-18 10:59:43 +00:00
::code-group
2023-09-27 14:13:03 +00:00
```ts [server/api/foo/index.get.ts]
2023-09-27 15:16:38 +00:00
export default defineEventHandler((event) => {
2023-09-27 14:13:03 +00:00
// handle GET requests for the `api/foo` endpoint
})
```
```ts [server/api/foo/index.post.ts]
2023-09-27 15:16:38 +00:00
export default defineEventHandler((event) => {
2023-09-27 14:13:03 +00:00
// handle POST requests for the `api/foo` endpoint
})
```
```ts [server/api/foo/bar.get.ts]
2023-09-27 15:16:38 +00:00
export default defineEventHandler((event) => {
2023-09-27 14:13:03 +00:00
// handle GET requests for the `api/foo/bar` endpoint
})
```
2023-10-18 10:59:43 +00:00
::
2023-09-27 14:13:03 +00:00
2022-08-13 07:27:04 +00:00
### Catch-all Route
2022-04-09 11:36:51 +00:00
2023-10-18 10:59:43 +00:00
Catch-all routes are helpful for fallback route handling.
2022-04-09 11:36:51 +00:00
2023-10-18 10:59:43 +00:00
For example, creating a file named `~/server/api/foo/[...].ts` will register a catch-all route for all requests that do not match any route handler, such as `/api/foo/bar/baz` .
2022-04-09 11:36:51 +00:00
2023-08-03 11:05:29 +00:00
```ts [server/api/foo/[...\\].ts]
2023-10-18 10:59:43 +00:00
export default defineEventHandler((event) => {
// event.context.path to get the route path: '/api/foo/bar/baz'
// event.context.params._ to get the route segment: 'bar/baz'
return `Default foo handler`
})
2022-04-09 11:36:51 +00:00
```
2023-10-18 10:59:43 +00:00
You can set a name for the catch-all route by using `~/server/api/foo/[...slug].ts` and access it via `event.context.params.slug` .
```ts [server/api/foo/[...slug\\].ts]
export default defineEventHandler((event) => {
// event.context.params.slug to get the route segment: 'bar/baz'
return `Default foo handler`
})
2022-04-09 11:36:51 +00:00
```
2023-10-18 10:59:43 +00:00
### Body Handling
2022-04-09 11:36:51 +00:00
2022-08-16 08:14:03 +00:00
```ts [server/api/submit.post.ts]
2022-04-09 11:36:51 +00:00
export default defineEventHandler(async (event) => {
2023-10-18 10:59:43 +00:00
const body = await readBody(event)
return { body }
2022-04-09 11:36:51 +00:00
})
```
2023-10-18 10:59:43 +00:00
You can now universally call this API using:
```vue [app.vue]
< script setup >
async function submit() {
const { body } = await $fetch('/api/submit', {
method: 'post',
body: { test: 123 }
})
}
< / script >
```
2022-04-09 11:36:51 +00:00
2023-10-18 10:59:43 +00:00
::callout
2022-10-18 08:46:47 +00:00
We are using `submit.post.ts` in the filename only to match requests with `POST` method that can accept the request body. When using `readBody` within a GET request, `readBody` will throw a `405 Method Not Allowed` HTTP error.
2022-04-09 11:36:51 +00:00
::
2023-10-18 10:59:43 +00:00
### Query Parameters
2022-04-22 16:11:25 +00:00
2023-10-18 10:59:43 +00:00
Sample query `/api/query?foo=bar&baz=qux`
2022-04-22 16:11:25 +00:00
2022-08-16 08:14:03 +00:00
```ts [server/api/query.get.ts]
2022-04-22 16:11:25 +00:00
export default defineEventHandler((event) => {
2022-10-08 09:32:20 +00:00
const query = getQuery(event)
2023-10-18 10:59:43 +00:00
return { a: query.foo, b: query.baz }
2022-04-22 16:11:25 +00:00
})
```
2023-10-18 10:59:43 +00:00
### Error Handling
If no errors are thrown, a status code of `200 OK` will be returned.
2023-01-19 12:08:39 +00:00
2023-10-18 10:59:43 +00:00
Any uncaught errors will return a `500 Internal Server Error` HTTP Error.
2023-01-19 12:08:39 +00:00
2023-10-18 10:59:43 +00:00
To return other error codes, throw an exception with [`createError` ](/docs/api/utils/create-error ):
2023-01-19 12:08:39 +00:00
2023-08-03 11:05:29 +00:00
```ts [server/api/validation/[id\\].ts]
2023-01-19 12:08:39 +00:00
export default defineEventHandler((event) => {
const id = parseInt(event.context.params.id) as number
2023-10-18 10:59:43 +00:00
2023-01-19 12:08:39 +00:00
if (!Number.isInteger(id)) {
throw createError({
statusCode: 400,
statusMessage: 'ID should be an integer',
})
}
return 'All good'
})
```
2023-10-18 10:59:43 +00:00
### Status Codes
2023-01-19 12:08:39 +00:00
2023-10-18 10:59:43 +00:00
To return other status codes, use the [`setResponseStatus` ](/docs/api/utils/set-response-status ) utility.
2023-01-19 12:08:39 +00:00
For example, to return `202 Accepted`
2023-08-03 11:05:29 +00:00
```ts [server/api/validation/[id\\].ts]
2023-01-19 12:08:39 +00:00
export default defineEventHandler((event) => {
setResponseStatus(event, 202)
})
```
2023-10-18 10:59:43 +00:00
### Runtime Config
2022-04-22 16:11:25 +00:00
2023-10-18 10:59:43 +00:00
::code-group
2022-08-16 08:14:03 +00:00
```ts [server/api/foo.ts]
2023-10-18 10:59:43 +00:00
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event)
2022-04-22 16:11:25 +00:00
2023-10-18 10:59:43 +00:00
const repo = await $fetch('https://api.github.com/repos/nuxt/nuxt', {
headers: {
Authorization: `token ${config.githubToken}`
}
})
return repo
2022-04-22 16:11:25 +00:00
})
```
2023-10-18 10:59:43 +00:00
```ts [nuxt.config.ts]
export default defineNuxtConfig({
runtimeConfig: {
githubToken: ''
}
})
```
```bash [.env]
NUXT_GITHUB_TOKEN='< my-super-token > '
```
::
::callout
Giving the `event` as argument to `useRuntimeConfig` is optional, but it is recommended to pass it to get the runtime config overwritten by [environment variables ](/docs/guide/going-further/runtime-config#environment-variables ) at runtime for server routes.
::
2022-04-22 16:11:25 +00:00
2023-10-18 10:59:43 +00:00
### Request Cookies
2022-04-09 11:36:51 +00:00
2023-10-18 10:59:43 +00:00
```ts [server/api/cookies.ts]
2022-04-09 11:36:51 +00:00
export default defineEventHandler((event) => {
2022-08-22 08:49:27 +00:00
const cookies = parseCookies(event)
2023-10-18 10:59:43 +00:00
2022-04-09 11:36:51 +00:00
return { cookies }
})
```
2023-10-18 10:59:43 +00:00
## Advanced Usage
2022-04-22 16:11:25 +00:00
2023-10-18 10:59:43 +00:00
### Nitro Config
2022-08-15 14:29:41 +00:00
2022-08-16 17:29:46 +00:00
You can use `nitro` key in `nuxt.config` to directly set [Nitro configuration ](https://nitro.unjs.io/config ).
2022-08-15 14:29:41 +00:00
2023-10-18 10:59:43 +00:00
::callout{color="amber" icon="i-ph-warning-duotone"}
2022-08-16 08:14:03 +00:00
This is an advanced option. Custom config can affect production deployments, as the configuration interface might change over time when Nitro is upgraded in semver-minor versions of Nuxt.
2022-08-15 14:29:41 +00:00
::
```ts [nuxt.config.ts]
export default defineNuxtConfig({
// https://nitro.unjs.io/config
nitro: {}
})
```
2023-10-18 10:59:43 +00:00
:read-more{to="/docs/guide/concepts/server-engine"}
### Nested Router
2022-04-09 11:36:51 +00:00
2023-08-03 11:05:29 +00:00
```ts [server/api/hello/[...slug\\].ts]
2023-01-16 22:41:38 +00:00
import { createRouter, defineEventHandler, useBase } from 'h3'
2022-04-09 11:36:51 +00:00
const router = createRouter()
2023-01-16 22:41:38 +00:00
router.get('/test', defineEventHandler(() => 'Hello World'))
2022-04-09 11:36:51 +00:00
2023-01-16 22:41:38 +00:00
export default useBase('/api/hello', router.handler)
2022-04-09 11:36:51 +00:00
```
2023-10-18 10:59:43 +00:00
### Sending Streams
2022-04-22 16:11:25 +00:00
2023-10-18 10:59:43 +00:00
::callout
This is an experimental feature and is only available in all environments.
::
2022-04-22 16:11:25 +00:00
2022-08-16 08:14:03 +00:00
```ts [server/api/foo.get.ts]
2022-04-22 16:11:25 +00:00
import fs from 'node:fs'
import { sendStream } from 'h3'
export default defineEventHandler((event) => {
return sendStream(event, fs.createReadStream('/path/to/file'))
})
```
2023-02-16 14:53:23 +00:00
### Sending Redirect
```ts [server/api/foo.get.ts]
2023-09-22 07:20:03 +00:00
export default defineEventHandler(async (event) => {
await sendRedirect(event, '/path/redirect/to', 302)
2023-02-16 14:53:23 +00:00
})
```
2023-10-18 10:59:43 +00:00
### Legacy Handler or Middleware
2022-04-09 11:36:51 +00:00
2022-08-16 08:14:03 +00:00
```ts [server/api/legacy.ts]
2023-03-19 14:35:51 +00:00
export default fromNodeMiddleware((req, res) => {
2022-04-09 11:36:51 +00:00
res.end('Legacy handler')
2023-03-19 14:35:51 +00:00
})
2022-04-09 11:36:51 +00:00
```
2023-10-18 10:59:43 +00:00
::callout{color="amber" icon="i-ph-warning-duotone"}
2022-08-16 08:14:03 +00:00
Legacy support is possible using [unjs/h3 ](https://github.com/unjs/h3 ), but it is advised to avoid legacy handlers as much as you can.
2022-04-09 11:36:51 +00:00
::
2022-08-16 08:14:03 +00:00
```ts [server/middleware/legacy.ts]
2023-03-19 14:35:51 +00:00
export default fromNodeMiddleware((req, res, next) => {
2022-04-09 11:36:51 +00:00
console.log('Legacy middleware')
next()
2023-03-19 14:35:51 +00:00
})
2022-04-09 11:36:51 +00:00
```
2023-10-18 10:59:43 +00:00
::callout{color="amber" icon="i-ph-warning-duotone"}
Never combine `next()` callback with a legacy middleware that is `async` or returns a `Promise` .
2022-04-09 11:36:51 +00:00
::
2022-08-15 14:29:41 +00:00
### Server Storage
2023-07-18 10:25:47 +00:00
Nitro provides a cross-platform [storage layer ](https://nitro.unjs.io/guide/storage ). In order to configure additional storage mount points, you can use `nitro.storage` , or [server plugins ](#server-plugins ).
2022-08-15 14:29:41 +00:00
2023-10-18 10:59:43 +00:00
**Example of adding a Redis storage:**
2022-08-15 14:29:41 +00:00
2023-07-18 10:25:47 +00:00
Using `nitro.storage` :
2022-08-15 14:29:41 +00:00
```ts [nuxt.config.ts]
export default defineNuxtConfig({
nitro: {
storage: {
2023-10-18 10:59:43 +00:00
redis: {
2022-08-15 14:29:41 +00:00
driver: 'redis',
/* redis connector options */
port: 6379, // Redis port
host: "127.0.0.1", // Redis host
username: "", // needs Redis >= 6
password: "",
2022-11-11 11:12:30 +00:00
db: 0, // Defaults to 0
tls: {} // tls/ssl
2022-08-16 08:14:03 +00:00
}
2022-08-15 14:29:41 +00:00
}
}
})
```
2023-10-18 10:59:43 +00:00
Then in your API handler:
2023-07-18 10:25:47 +00:00
2023-10-18 10:59:43 +00:00
```ts [server/api/storage/test.ts]
export default defineEventHandler(async (event) => {
// List all keys with
const keys = await useStorage('redis').getKeys()
// Set a key with
await useStorage('redis').setItem('foo', 'bar')
2023-07-18 10:25:47 +00:00
2023-10-18 10:59:43 +00:00
// Remove a key with
await useStorage('redis').removeItem('foo')
return {}
})
```
::read-more{to="https://nitro.unjs.io/guide/storage" target="_blank"}
Read more about Nitro Storage Layer.
::
Alternatively, you can create a storage mount point using a server plugin and runtime config:
::code-group
2023-07-18 10:25:47 +00:00
```ts [server/plugins/storage.ts]
import redisDriver from 'unstorage/drivers/redis'
export default defineNitroPlugin(() => {
const storage = useStorage()
// Dynamically pass in credentials from runtime configuration, or other sources
const driver = redisDriver({
base: 'redis',
host: useRuntimeConfig().redis.host,
port: useRuntimeConfig().redis.port,
/* other redis connector options */
})
// Mount driver
storage.mount('redis', driver)
})
```
``` ts [nuxt.config.ts]
export default defineNuxtConfig({
runtimeConfig: {
redis: { // Default values
host: '',
port: 0,
/* other redis connector options */
}
}
})
```
::