server
Nuxt автоматически сканирует файлы внутри этих директорий для регистрации API и серверных обработчиков с поддержкой горячей замены модулей (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: 'мир'
}
})
Теперь вы можете повсеместно вызывать этот 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
будет доступен по адресу http://localhost:3000/hello.
Серверные middleware
Nuxt автоматически прочитает любой файл в ~/server/middleware
, чтобы создать серверную middleware для проекта.
Обработчики 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)
})
Серверные утилиты
Роуты сервера работают на основе unjs/h3, который поставляется с удобным набором хелперов.
Вы можете самостоятельно добавить больше хелперов в директорию ~/server/utils
.
Например, вы можете определить пользовательскую утилиту-обработчик, которая оборачивает исходный обработчик и выполняет дополнительные операции перед возвратом окончательного ответа.
Пример:
import type { EventHandler, EventHandlerRequest } from 'h3'
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 }
}
})
Серверные типы
Чтобы улучшить ясность в вашей IDE между автоматическим импортом из 'nitro' and 'vue', вы можете добавить ~/server/tsconfig.json
со следующим содержимым:
{
"extends": "../.nuxt/tsconfig.server.json"
}
В настоящее время эти значения не будут учитываться при проверке типов (nuxi typecheck
), но вы должны получить более точные подсказки по типам в своей IDE.
Рецепты
Параметры роута
Роуты сервера могут использовать динамические параметры в квадратных скобках в имени файла, например /api/hello/[name].ts
, и к ним можно получить доступ через event.context.params
.
export default defineEventHandler((event) => {
const name = getRouterParam(event, 'name')
return `Привет, ${name}!`
})
getValidatedRouterParams
с валидатором схемы, таким как Zod, для обеспечения безопасности рантайма и безопасности типов.Теперь вы можете повсеместно вызвать этот API по адресу /api/hello/nuxt
и получить Привет, 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`
})
Универсальные роуты
Универсальные роуты полезны для обработки всех остальных маршрутов.
Например, создание файла с именем ~/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 = 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
})
event
в качестве аргумента useRuntimeConfig
необязательно, но рекомендуется передать его, чтобы перезаписать конфигурацию рантайма переменными окружения во время выполнения для серверных роутов.Запрос Cookies
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
Awaiting Promises After Response
When handling server requests, you might need to perform asynchronous tasks that shouldn't block the response to the client (for example, caching and logging). You can use event.waitUntil
to await a promise in the background without delaying the response.
The event.waitUntil
method accepts a promise that will be awaited before the handler terminates, ensuring the task is completed even if the server would otherwise terminate the handler right after the response is sent. This integrates with runtime providers to leverage their native capabilities for handling asynchronous operations after the response is sent.
const timeConsumingBackgroundTask = async () => {
await new Promise((resolve) => setTimeout(resolve, 1000))
};
export default eventHandler((event) => {
// schedule a background task without blocking the response
event.waitUntil(timeConsumingBackgroundTask())
// immediately send the response to the client
return 'done'
});
Расширенное использование
Конфиг Nitro
Вы можете использовать ключ nitro
в nuxt.config
, чтобы напрямую задать конфигурацию Nitro.
export default defineNuxtConfig({
// https://nitro.unjs.io/config
nitro: {}
})
Вложенный роутер
import { createRouter, defineEventHandler, useBase } from 'h3'
const router = createRouter()
router.get('/test', defineEventHandler(() => 'Привет мир'))
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()
с устаревшей 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)
})