server
Nuxt автоматически сканирует файлы в этих каталогах и регистрирует API и серверные обработчики с поддержкой горячей замены модулей (HMR).
-| server/
---| api/
-----| hello.ts # /api/hello
---| routes/
-----| bonjour.ts # /bonjour
---| middleware/
-----| log.ts # log all requests
Каждый файл должен экспортировать функцию по умолчанию, объявленную через 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 в своем роуте.
Чтобы добавить серверные роуты без префикса /api, поместите их в каталог ~~/server/routes.
Пример:
export default defineEventHandler(() => 'Hello World!')
В примере выше маршрут /hello доступен по адресу http://localhost:3000/hello.
Серверные middleware
Nuxt автоматически подхватывает файлы в ~~/server/middleware и регистрирует их как серверный middleware проекта.
Обработчики выполняются для каждого входящего запроса до сопоставления с конкретным маршрутом: так можно добавлять заголовки, логировать запросы или расширять контекст события.
Примеры:
export default defineEventHandler((event) => {
console.log('New request: ' + getRequestURL(event))
})
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})
Серверные плагины
Nuxt автоматически прочитает все файлы в каталоге ~~/server/plugins и зарегистрирует их как плагины Nitro. Это позволяет расширить рантайм-поведение Nitro и подключиться к событиям жизненного цикла.
Пример:
export default defineNitroPlugin((nitroApp) => {
console.log('Nitro plugin', nitroApp)
})
Серверные утилиты
Роуты сервера работают на основе h3js/h3, который поставляется с удобным набором хелперов.
Вы можете самостоятельно добавить больше хелперов в директорию ~~/server/utils.
Например, вы можете определить пользовательскую утилиту-обработчик, которая оборачивает исходный обработчик и выполняет дополнительные операции перед возвратом окончательного ответа.
Пример:
export const defineWrappedResponseHandler = <T extends EventHandlerRequest, D> (
handler: EventHandler<T, D>,
): EventHandler<T, D> =>
defineEventHandler<T>(async (event) => {
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 }
}
})
export default defineWrappedResponseHandler(event => 'hello world')
Серверный алиас #server
С помощью алиаса #server можно импортировать файлы из любого места внутри каталога server/, независимо от того, где находится импортирующий файл.
// Вместо относительных путей вроде:
// import { formatUser } from '../../../utils/formatUser'
// используйте алиас #server:
import { formatUser } from '#server/utils/formatUser'
Так импорты в серверном коде остаются единообразными — это особенно удобно во вложенных обработчиках маршрутов.
#server допустим только внутри каталога server/. Импорт из #server в клиентском коде приведёт к ошибке.Серверные типы
Чтобы в IDE было проще различать автоматический импорт из Nitro и из Vue, добавьте ~/server/tsconfig.json со следующим содержимым:
{
"extends": "../.nuxt/tsconfig.server.json"
}
В настоящее время эти значения не будут учитываться при проверке типов (nuxt typecheck), но вы должны получить более точные подсказки по типам в своей IDE.
Рецепты
Параметры роута
Роуты сервера могут использовать динамические параметры в квадратных скобках в имени файла, например /api/hello/[name].ts, и к ним можно получить доступ через event.context.params.
export default defineEventHandler((event) => {
const name = getRouterParam(event, 'name')
return `Hello, ${name}!`
})
getValidatedRouterParams с валидатором схемы, таким как Zod, для обеспечения безопасности рантайма и безопасности типов.Запрос к /api/hello/nuxt вернёт ответ Hello, nuxt!.
Соответствие метода HTTP
Имена файлов обработчиков могут иметь суффиксы .get, .post, .put, .delete, … в соответствии с методом HTTP запроса.
export default defineEventHandler(() => 'Тестовый обработчик get')
export default defineEventHandler(() => 'Тестовый обработчик post')
Учитывая пример выше, выборка /test с помощью:
- Метода GET: Возвращает
Тестовый обработчик get - Метода POST: Возвращает
Тестовый обработчик post - Любого другого метода: Возвращает ошибку 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`
})
Универсальные роуты
Универсальные роуты полезны для обработки всех остальных маршрутов.
Например, создание файла с именем ~/server/api/foo/[...].ts зарегистрирует универсальный роут для всех запросов, которые не соответствуют ни одному обработчику, например /api/foo/bar/baz.
export default defineEventHandler((event) => {
// event.context.path чтобы получить путь роута: '/api/foo/bar/baz'
// event.context.params._ чтобы получить сегмент роута: 'bar/baz'
return `Обработчик foo по умолчанию`
})
Вы можете задать имя для универсального роута с помощью ~/server/api/foo/[...slug].ts и получить к нему доступ через event.context.params.slug.
export default defineEventHandler((event) => {
// event.context.params.slug чтобы получить сегмент роута: 'bar/baz'
return `Обработчик foo по умолчанию`
})
Обработка тела запроса
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { body }
})
readValidatedBody с валидатором схемы, таким как Zod, для обеспечения безопасности рантайма и безопасности типов.Пример вызова 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, readBody выдаст ошибку HTTP 405 Method Not Allowed.Параметры запроса
Пример запроса /api/query?foo=bar&baz=qux
export default defineEventHandler((event) => {
const query = getQuery(event)
return { a: query.foo, b: query.baz }
})
getValidatedQuery с валидатором схемы, таким как Zod, для обеспечения безопасности рантайма и безопасности типов.Обработка ошибок
Если ошибок не возникло, будет возвращен код состояния 200 OK.
Любые неперехваченные ошибки вернут HTTP-ошибку 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({
statusCode: 400,
statusMessage: 'ID должен быть целым числом',
})
}
return 'Все хорошо'
})
Коды статуса
Чтобы вернуть другие коды статуса, используйте утилиту setResponseStatus.
Например, чтобы вернуть 202 Accepted
export default defineEventHandler((event) => {
setResponseStatus(event, 202)
})
Конфигурация рантайма
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 необязательно, но рекомендуется передать его, чтобы перезаписать конфигурацию рантайма переменными окружения во время выполнения для серверных роутов.Работа с cookie
export default defineEventHandler((event) => {
const cookies = parseCookies(event)
return { cookies }
})
Передача контекста и заголовков
По умолчанию ни заголовки входящего запроса, ни контекст запроса не передаются при выполнении fetch-запросов в серверных маршрутах. Вы можете использовать event.$fetch, чтобы передать контекст запроса и заголовки при выполнении 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
Вы можете использовать ключ nitro в nuxt.config, чтобы напрямую задать конфигурацию 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)
})
Устаревший обработчик или middleware
export default fromNodeMiddleware((req, res) => {
res.end('Устаревший обработчик')
})
export default fromNodeMiddleware((req, res, next) => {
console.log('Устаревшая middleware')
next()
})
next() с legacy-middleware, объявленной как async или возвращающей Promise.Серверное хранилище
Nitro предоставляет кроссплатформенный слой хранения. Для настройки дополнительных точек монтирования хранилища можно использовать nitro.storage или серверные плагины.
Пример добавления хранилища Redis:
Использование nitro.storage:
export default defineNuxtConfig({
nitro: {
storage: {
redis: {
driver: 'redis',
/* параметры коннектора redis */
port: 6379, // порт Redis
host: '127.0.0.1', // хост Redis
username: '', // для Redis >= 6
password: '',
db: 0, // по умолчанию 0
tls: {}, // tls/ssl
},
},
},
})
Затем в вашем обработчике API:
export default defineEventHandler(async (event) => {
// Список всех ключей
const keys = await useStorage('redis').getKeys()
// Установка ключа
await useStorage('redis').setItem('foo', 'bar')
// Удаление ключа
await useStorage('redis').removeItem('foo')
return {}
})
В качестве альтернативы вы можете создать точку монтирования хранилища с помощью серверного плагина и конфигурации рантайма:
import redisDriver from 'unstorage/drivers/redis'
export default defineNitroPlugin(() => {
const storage = useStorage()
// Динамическая передача учетных данных из рантайм-конфигурации или других источников.
const driver = redisDriver({
base: 'redis',
host: useRuntimeConfig().redis.host,
port: useRuntimeConfig().redis.port,
/* другие опции коннектора redis */
})
// Монтирование драйвера
storage.mount('redis', driver)
})
export default defineNuxtConfig({
runtimeConfig: {
redis: { // Значения по умолчанию
host: '',
port: 0,
/* другие опции коннектора redis */
},
},
})