server
Nuxt автоматически сканирует файлы в этих директориях и регистрирует API и серверные обработчики с поддержкой Hot Module Replacement (HMR).
-| server/
---| api/
-----| hello.ts # /api/hello
---| routes/
-----| bonjour.ts # /bonjour
---| middleware/
-----| log.ts # логирование всех запросов
В каждом файле должен экспортироваться обработчик по умолчанию, определённый через defineEventHandler() или eventHandler() (алиас).
Обработчик может возвращать JSON, Promise или вызывать event.node.res.end() для отправки ответа.
export default defineEventHandler((event) => {
return {
hello: 'world',
}
})
Этот API можно вызывать из страниц и компонентов:
<script setup lang="ts">
const { data } = await useFetch('/api/hello')
</script>
<template>
<pre>{{ data }}</pre>
</template>
Серверные маршруты
Файлы в ~~/server/api автоматически получают префикс /api в URL.
Чтобы добавить серверные маршруты без префикса /api, поместите их в директорию ~~/server/routes.
Пример:
export default defineEventHandler(() => 'Привет, мир!')
В примере выше маршрут /hello будет доступен по адресу http://localhost:3000/hello.
Серверный мидлвар
Nuxt автоматически подхватывает файлы в ~~/server/middleware и создаёт серверный мидлвар для проекта.
Обработчики мидлвара выполняются при каждом запросе до любых других серверных маршрутов — для добавления или проверки заголовков, логирования запросов или расширения объекта запроса.
Примеры:
export default defineEventHandler((event) => {
console.log('Новый запрос: ' + getRequestURL(event))
})
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})
Серверные плагины
Nuxt автоматически подхватывает файлы в ~~/server/plugins и регистрирует их как плагины Nitro. Так можно расширять поведение Nitro и подключаться к событиям жизненного цикла.
Пример:
export default defineNitroPlugin((nitroApp) => {
console.log('Плагин Nitro', nitroApp)
})
Серверные утилиты
Серверные маршруты работают на базе h3js/h3, в котором есть набор вспомогательных функций.
Дополнительные хелперы можно добавлять в директории ~~/server/utils.
Например, можно определить обёртку над обработчиком, которая выполняет действия до и после основного обработчика.
Пример:
export const defineWrappedResponseHandler = <T extends EventHandlerRequest, D> (
handler: EventHandler<T, D>,
): EventHandler<T, D> =>
defineEventHandler<T>(async (event) => {
try {
// что-то до обработчика маршрута
const response = await handler(event)
// что-то после обработчика маршрута
return { response }
} catch (err) {
// обработка ошибок
return { err }
}
})
export default defineWrappedResponseHandler(event => 'hello world')
Алиас #server
Можно использовать алиас #server для импорта файлов из любой части директории server/, независимо от расположения импортирующего файла.
// Вместо относительных путей:
// import { formatUser } from '../../../utils/formatUser'
// Используйте алиас #server:
import { formatUser } from '#server/utils/formatUser'
Так импорты остаются единообразными, особенно в глубоко вложенных обработчиках маршрутов.
#server можно использовать только внутри директории server/. Импорт из #server в клиентском коде приведёт к ошибке.Типы на сервере
Автоимпорты и типы в директории server/ отличаются от app/, так как код выполняется в другом контексте.
По умолчанию Nuxt 4 генерирует tsconfig.json с project reference для папки server/, что обеспечивает корректную типизацию.
Рецепты
Параметры маршрута
В именах файлов серверных маршрутов можно использовать динамические параметры в квадратных скобках, например /api/hello/[name].ts; доступ к ним — через event.context.params.
export default defineEventHandler((event) => {
const name = getRouterParam(event, 'name')
return `Hello, ${name}!`
})
Вызов /api/hello/nuxt вернёт Hello, nuxt!.
Соответствие HTTP-методу
Имена файлов могут иметь суффикс .get, .post, .put, .delete и т.д., чтобы обрабатывать только указанный HTTP-метод.
export default defineEventHandler(() => 'Test get handler')
export default defineEventHandler(() => 'Test post handler')
В примере выше при обращении к /test:
- GET — вернётся
Test get handler - POST — вернётся
Test post handler - любой другой метод — ответ 405
Для структурирования кода можно использовать index.[method].ts внутри директории (например, для API-неймспейсов).
export default defineEventHandler((event) => {
// обработка GET для api/foo
})
export default defineEventHandler((event) => {
// обработка POST для api/foo
})
export default defineEventHandler((event) => {
// обработка GET для api/foo/bar
})
Catch-all маршрут
Catch-all маршруты удобны как запасной обработчик.
Файл ~~/server/api/foo/[...].ts регистрирует catch-all для запросов, не совпадающих с другими обработчиками, например /api/foo/bar/baz.
export default defineEventHandler((event) => {
// event.context.path — путь маршрута: '/api/foo/bar/baz'
// event.context.params._ — сегмент: 'bar/baz'
return `Default foo handler`
})
Имя для catch-all задаётся так: ~~/server/api/foo/[...slug].ts; доступ — через event.context.params.slug.
export default defineEventHandler((event) => {
// event.context.params.slug — сегмент: 'bar/baz'
return `Default foo handler`
})
Обработка тела запроса
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { body }
})
Вызов API:
<script setup lang="ts">
async function submit () {
const { body } = await $fetch('/api/submit', {
method: 'post',
body: { test: 123 },
})
}
</script>
submit.post.ts в имени файла нужен только для сопоставления с методом POST, который может принять тело запроса. Использование readBody в GET-обработчике приведёт к ошибке 405 Method Not Allowed.Query-параметры
Пример запроса: /api/query?foo=bar&baz=qux
export default defineEventHandler((event) => {
const query = getQuery(event)
return { a: query.foo, b: query.baz }
})
Обработка ошибок
Если ошибка не выбрасывается, возвращается статус 200 OK.
Любая необработанная ошибка вернёт 500 Internal Server Error.
Для других кодов ошибок используйте createError:
export default defineEventHandler((event) => {
const id = Number.parseInt(event.context.params.id) as number
if (!Number.isInteger(id)) {
throw createError({
status: 400,
statusText: 'ID должен быть целым числом',
})
}
return 'All good'
})
Коды статуса
Для возврата других кодов статуса используйте утилиту setResponseStatus.
Например, для 202 Accepted:
export default defineEventHandler((event) => {
setResponseStatus(event, 202)
})
Runtime-конфигурация
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event)
const repo = await $fetch('https://api.github.com/repos/nuxt/nuxt', {
headers: {
Authorization: `token ${config.githubToken}`,
},
})
return repo
})
export default defineNuxtConfig({
runtimeConfig: {
githubToken: '',
},
})
NUXT_GITHUB_TOKEN='<my-super-token>'
event в useRuntimeConfig необязательно, но рекомендуется — так в серверных маршрутах будет учитываться переопределение конфига переменными окружения.Куки запроса
export default defineEventHandler((event) => {
const cookies = parseCookies(event)
return { cookies }
})
Прокидывание контекста и заголовков
По умолчанию при запросах из серверных маршрутов не прокидываются ни заголовки входящего запроса, ни контекст. Используйте event.$fetch, чтобы прокинуть контекст и заголовки.
export default defineEventHandler((event) => {
return event.$fetch('/api/forwarded')
})
transfer-encoding, connection, keep-alive, upgrade, expect, host, accept.Ожидание промисов после ответа
Иногда нужно выполнить асинхронные задачи после отправки ответа клиенту (кэширование, логирование). Метод event.waitUntil позволяет ожидать промис в фоне, не задерживая ответ.
event.waitUntil принимает промис, который будет ожидаться до завершения обработчика, так что задача выполнится даже если сервер завершит обработчик сразу после отправки ответа. Это интегрируется с рантайм-провайдерами и их возможностями по отложенным задачам.
const timeConsumingBackgroundTask = async () => {
await new Promise(resolve => setTimeout(resolve, 1000))
}
export default eventHandler((event) => {
// запланировать фоновую задачу без блокировки ответа
event.waitUntil(timeConsumingBackgroundTask())
// сразу отправить ответ клиенту
return 'done'
})
Продвинутое использование
Конфигурация Nitro
В nuxt.config можно задать ключ nitro для прямой настройки Nitro.
export default defineNuxtConfig({
// https://nitro.build/config
nitro: {},
})
Вложенный роутер
import { createRouter, defineEventHandler, useBase } from 'h3'
const router = createRouter()
router.get('/test', defineEventHandler(() => 'Hello World'))
export default useBase('/api/hello', router.handler)
Отправка стримов
import fs from 'node:fs'
import { sendStream } from 'h3'
export default defineEventHandler((event) => {
return sendStream(event, fs.createReadStream('/path/to/file'))
})
Редирект
export default defineEventHandler(async (event) => {
await sendRedirect(event, '/path/redirect/to', 302)
})
Устаревший обработчик или мидлвар
export default fromNodeMiddleware((req, res) => {
res.end('Legacy handler')
})
export default fromNodeMiddleware((req, res, next) => {
console.log('Legacy middleware')
next()
})
next() с устаревшим мидлваром, который является async или возвращает Promise.Серверное хранилище
Nitro предоставляет кроссплатформенный слой хранилища. Дополнительные точки монтирования настраиваются через nitro.storage или серверные плагины.
Пример подключения Redis:
Через nitro.storage:
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
tls: {}, // tls/ssl
},
},
},
})
В обработчике API:
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')
// Remove a key with
await useStorage('redis').removeItem('foo')
return {}
})
Либо можно создать точку монтирования через серверный плагин и runtime config:
import redisDriver from 'unstorage/drivers/redis'
export default defineNitroPlugin(() => {
const storage = useStorage()
// Динамическая передача учётных данных из runtime config или других источников
const driver = redisDriver({
base: 'redis',
host: useRuntimeConfig().redis.host,
port: useRuntimeConfig().redis.port,
/* other redis connector options */
})
// Монтирование драйвера
storage.mount('redis', driver)
})
export default defineNuxtConfig({
runtimeConfig: {
redis: { // значения по умолчанию
host: '',
port: 0,
/* other redis connector options */
},
},
})