Nuxt/docs/content/2.guide/2.features/9.server-routes.md

293 lines
7.8 KiB
Markdown
Raw Normal View History

# Server Routes
Nuxt automatically scans files inside the `~/server/api`, `~/server/routes`, and `~/server/middleware` directories to register API and server handlers with HMR support.
Each file should export a default function defined with `defineEventHandler()`.
The handler can directly return JSON data, a `Promise` or use `event.res.end()` to send response.
::ReadMore{link="https://nitro.unjs.io/guide/routing.html" title="Nitro Route Handling Docs"}
::
## Example
Create a new file in `server/api/hello.ts`:
```ts [/server/api/hello.ts]
export default defineEventHandler((event) => {
return {
api: 'works'
}
})
```
You can now universally call this API using `await $fetch('/api/hello')`.
## Server Routes
Files inside the `~/server/api` are automatically prefixed with `/api` in their route.
For adding server routes without `/api` prefix, you can instead put them into `~/server/routes` directory.
**Example:**
```ts [/server/routes/hello.ts]
export default defineEventHandler(() => 'Hello World!')
```
Given the Example above, the `/hello` route will be accessible at <http://localhost:3000/hello>.
## Server Middleware
Nuxt will automatically read in any file in the `~/server/middleware` to create server middleware for your project.
Middleware handlers will run on every request before any other server route to add check and some headers, log requests, or extend the event's request object.
::alert{type=warning}
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:**
```ts [/server/middleware/log.ts]
export default defineEventHandler((event) => {
console.log('New request: ' + event.req.url)
})
```
```ts [/server/middleware/auth.ts]
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})
```
## Server Utilities
Server routes are powered by [unjs/h3](https://github.com/unjs/h3) which comes with a handy set of helpers.
::ReadMore{link="https://www.jsdocs.io/package/h3#package-index-functions" title="Available H3 Request Helpers"}
::
You can add more helpers by yourself inside the `~/server/utils` directory.
## Usage Examples
### Matching Route Parameters
2022-04-20 15:06:27 +00:00
Server routes can use dynamic parameters within brackets in the file name like `/api/hello/[name].ts` and accessed via `event.context.params`.
**Example:**
```ts [/server/api/hello/[name].ts]
export default defineEventHandler(event => `Hello, ${event.context.params.name}!`)
```
You can now universally call this API using `await $fetch('/api/hello/nuxt')` and get `Hello, nuxt!`.
### Matching HTTP Method
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).
```ts [/server/api/test.get.ts]
export default defineEventHandler(() => 'Test get handler')
```
```ts [/server/api/test.post.ts]
export default defineEventHandler(() => 'Test post handler')
```
Given the Example above, fetching `/test` with:
- **GET** method: Returns `Test get handler`
- **POST** method: Returns `Test post handler`
- Any other method: Returns 404 error
### Catch-all Route
Catch-all routes are helpful for fallback route handling. 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`.
**Examples:**
```ts [/server/api/foo/[...].ts]
export default defineEventHandler(() => `Default foo handler`)
```
```ts [/server/api/[...].ts]
export default defineEventHandler(() => `Default api handler`)
```
### Handling Requests with Body
```ts [/server/api/submit.post.ts]
export default defineEventHandler(async (event) => {
const body = await useBody(event)
return { body }
})
```
You can now universally call this API using `$fetch('/api/submit', { method: 'post', body: { test: 123 } })`.
::alert{type=warning title=Attention}
We are using `submit.post.ts` in the filename only to match requests with `POST` method that can accept the request body. When using `useBody` within a GET request, `useBody` will throw a `405 Method Not Allowed` HTTP error.
::
### Handling Requests With Query Parameters
Sample query `/api/query?param1=a&param2=b`
```ts [/server/api/query.get.ts]
export default defineEventHandler((event) => {
const query = useQuery(event)
return { a: query.param1, b: query.param2 }
})
```
### Accessing Runtime Config
```ts [/server/api/foo.ts]
export default defineEventHandler((event) => {
const config = useRuntimeConfig()
return { key: config.KEY }
})
```
### Accessing Request Cookies
```ts
export default defineEventHandler((event) => {
const cookies = useCookies(event)
return { cookies }
})
```
## Advanced Usage Examples
### Nitro Configuration
You can use `nitro` key in `nuxt.config` to directly set [Nitro Configuration](https://nitro.unjs.io/config/).
::alert{type=warning}
This is an advanced option. Custom config can affect production deployments and we upgrade Nitro in semver-minor versions of Nuxt 3. meaning, configuration interface might be changed over the time.
::
```ts [nuxt.config.ts]
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
// https://nitro.unjs.io/config
nitro: {}
})
```
### Using a Nested Router
```ts [/server/api/hello.ts]
import { createRouter } from 'h3'
const router = createRouter()
router.get('/', () => 'Hello World')
export default router
```
### Sending Streams (Experimental)
**Note:** This is an experimental feature and available only within Node.js environments.
```ts [/server/api/foo.get.ts]
import fs from 'node:fs'
import { sendStream } from 'h3'
export default defineEventHandler((event) => {
return sendStream(event, fs.createReadStream('/path/to/file'))
})
```
### Return a Legacy Handler or Middleware
```ts [/server/api/legacy.ts]
export default (req, res) => {
res.end('Legacy handler')
}
```
::alert{type=warning}
Legacy support is possible using [unjs/h3](https://github.com/unjs/h3) but it advised to avoid legacy handlers as much as you can.
::
```ts [/server/middleware/legacy.ts]
export default (req, res, next) => {
console.log('Legacy middleware')
next()
}
```
::alert{type=warning}
Never combine `next()` callback with a legacy middleware that is `async` or returns a `Promise`!
::
### Server Storage
Nitro Provides a cross platform [Stroage Layer](https://nitro.unjs.io/guide/storage.html). In oder to configure additional storage mountpoints, you can use `nitro.storage`.
#### Example: Using Redis
```ts [nuxt.config.ts]
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
nitro: {
storage: {
'redis': {
driver: 'redis',
/* redis connector options */
port: 6379, // Redis port
host: "127.0.0.1", // Redis host
username: "", // needs Redis >= 6
password: "",
db: 0, // Defaults to 0
},
}
}
})
```
Create a new file in `server/api/test.post.ts`:
```ts [/server/api/test.post.ts]
export default defineEventHandler(async event => {
const body = await useBody(event)
await useStorage().setItem('redis:test', body)
return 'Data is set'
})
```
Create a new file in `server/api/test.get.ts`:
```ts [/server/api/test.get.ts]
export default async defineEventHandler(event => {
const data = await useStorage().getItem('redis:test')
return data
})
```
Create a new file in `app.vue`:
```vue [app.vue]
<template>
<div>
<div>Post state: {{ resDataSuccess }}</div>
<div>Get Data: {{ resData.text }}</div>
</div>
</template>
<script setup lang="ts">
const { data: resDataSuccess } = await useFetch('/api/test', {
method: 'post',
body: { text: 'Nuxt is Awesome!' }
})
const { data: resData } = await useFetch('/api/test')
</script>
```