Изучите Nuxt с коллекцией из 100+ советов!

server

Директория server/ используется для регистрации API и серверных обработчиков в вашем приложении.

Nuxt автоматически сканирует файлы внутри этих директорий для регистрации API и серверных обработчиков с поддержкой горячей замены модулей (HMR).

Структура директории
-| server/
---| api/
-----| hello.ts      # /api/hello
---| routes/
-----| bonjour.ts    # /bonjour
---| middleware/
-----| log.ts        # логирование всех запросов

Каждый файл должен экспортировать функцию по умолчанию, определенную с помощью defineEventHandler() или eventHandler() (псевдоним).

Обработчик может напрямую возвращать данные JSON, Promise или использовать event.node.res.end() для отправки ответа.

server/api/hello.ts
export default 
defineEventHandler
((
event
) => {
return {
hello
: 'мир'
} })

Теперь вы можете повсеместно вызывать этот API на страницах и в компонентах:

pages/index.vue
<script setup lang="ts">
const { data } = await useFetch('/api/hello')
</script>

<template>
  <pre>{{ data }}</pre>
</template>

Роуты сервера

Файлы внутри ~/server/api автоматически получают префикс /api в своем роуте.

Чтобы добавить серверные роуты без префикса /api, поместите их в директорию ~/server/routes.

Пример:

server/routes/hello.ts
export default defineEventHandler(() => 'Привет мир!')

Учитывая приведенный выше пример, маршрут /hello будет доступен по адресу http://localhost:3000/hello.

Обратите внимание, что в настоящее время серверные маршруты не поддерживают полную функциональность динамических маршрутов, как это делают страницы.

Серверные middleware

Nuxt автоматически прочитает любой файл в ~/server/middleware, чтобы создать серверную middleware для проекта.

Обработчики middleware будут запускаться для каждого запроса до того, как любой другой серверный маршрут будет добавлен или проверен - для добавления заголовков, регистрации запросов или расширения объекта запроса события.

Обработчики middleware не должны ничего возвращать (закрывать или отвечать на запрос), а только проверять или расширять контекст запроса или выдавать ошибку.

Примеры:

server/middleware/log.ts
export default defineEventHandler((event) => {
  console.log('Новый запрос: ' + getRequestURL(event))
})
server/middleware/auth.ts
export default defineEventHandler((event) => {
  event.context.auth = { user: 123 }
})

Серверные плагины

Nuxt автоматически прочитает все файлы в директории ~/server/plugins и зарегистрирует их как плагины Nitro. Это позволяет расширить рантайм-поведение Nitro и подключиться к событиям жизненного цикла.

Пример:

server/plugins/nitroPlugin.ts
export default defineNitroPlugin((nitroApp) => {
  console.log('Плагин Nitro', nitroApp)
})
Узнать больше Плагины Nitro.

Серверные утилиты

Роуты сервера работают на основе unjs/h3, который поставляется с удобным набором хелперов.

Узнать больше Доступные хелперы запросов H3.

Вы можете самостоятельно добавить больше хелперов в директорию ~/server/utils.

Например, вы можете определить пользовательскую утилиту-обработчик, которая оборачивает исходный обработчик и выполняет дополнительные операции перед возвратом окончательного ответа.

Пример:

server/utils/handler.ts
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 }
    }
  })

Серверные типы

Эта функция доступна с Nuxt >= 3.5

Чтобы улучшить ясность в вашей IDE между автоматическим импортом из 'nitro' and 'vue', вы можете добавить ~/server/tsconfig.json со следующим содержимым:

server/tsconfig.json
{
  "extends": "../.nuxt/tsconfig.server.json"
}

В настоящее время эти значения не будут учитываться при проверке типов (nuxi typecheck), но вы должны получить более точные подсказки по типам в своей IDE.

Рецепты

Параметры роута

Роуты сервера могут использовать динамические параметры в квадратных скобках в имени файла, например /api/hello/[name].ts, и к ним можно получить доступ через event.context.params.

server/api/hello/[name].ts
export default defineEventHandler((event) => {
  const name = getRouterParam(event, 'name')

  return `Привет, ${name}!`
})
В качестве альтернативы используйте getValidatedRouterParams с валидатором схемы, таким как Zod, для обеспечения безопасности рантайма и безопасности типов.

Теперь вы можете повсеместно вызвать этот API по адресу /api/hello/nuxt и получить Привет, nuxt!.

Соответствие метода HTTP

Имена файлов дескрипторов могут иметь суффиксы .get, .post, .put, .delete, ... для соответствия методу HTTP запроса.

server/api/test.get.ts
export default defineEventHandler(() => 'Тестовый обработчик get')
server/api/test.post.ts
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.

server/api/foo/[...].ts
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.

server/api/foo/[...slug].ts
export default defineEventHandler((event) => {
  // event.context.params.slug чтобы получить сегмент роута: 'bar/baz'
  return `Обработчик foo по умолчанию`
})

Обработка тела запроса

server/api/submit.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  return { body }
})
В качестве альтернативы используйте readValidatedBody с валидатором схемы, таким как Zod, для обеспечения безопасности рантайма и безопасности типов.

Теперь вы можете повсеместно вызывать этот API, используя:

app.vue
<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

server/api/query.get.ts
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:

server/api/validation/[id].ts
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

server/api/validation/[id].ts
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

server/api/cookies.ts
export default defineEventHandler((event) => {
  const cookies = parseCookies(event)

  return { cookies }
})

Расширенное использование

Конфиг Nitro

Вы можете использовать ключ nitro в nuxt.config, чтобы напрямую задать конфигурацию Nitro.

Это продвинутая опция. Пользовательская конфигурация может повлиять на продакшен-развертывания, поскольку интерфейс конфигурации может со временем измениться при обновлении Nitro в младших версиях Nuxt.
nuxt.config.ts
export default defineNuxtConfig({
  // https://nitro.unjs.io/config
  nitro: {}
})
Узнать больше Docs > Guide > Concepts > Server Engine.

Вложенный роутер

server/api/hello/[...slug].ts
import { createRouter, defineEventHandler, useBase } from 'h3'

const router = createRouter()

router.get('/test', defineEventHandler(() => 'Привет мир'))

export default useBase('/api/hello', router.handler)

Отправка стримов

Это экспериментальная функция, доступная во всех окружениях.
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'))
})

Отправка редиректа

server/api/foo.get.ts
export default defineEventHandler(async (event) => {
  await sendRedirect(event, '/path/redirect/to', 302)
})

Устаревший обработчик или middleware

server/api/legacy.ts
export default fromNodeMiddleware((req, res) => {
  res.end('Устаревший обработчик')
})
Поддержка устаревших версий возможна с использованием unjs/h3, но рекомендуется по возможности избегать устаревших обработчиков.
server/middleware/legacy.ts
export default fromNodeMiddleware((req, res, next) => {
  console.log('Устаревшая middleware')
  next()
})
Никогда не объединяйте коллбэк next() с устаревшей middleware, которая является async или возвращает Promise.

Серверное хранилище

Nitro предоставляет кроссплатформенный слой хранения. Для настройки дополнительных точек монтирования хранилища можно использовать nitro.storage или серверные плагины.

Пример добавления хранилища Redis:

Использование nitro.storage:

nuxt.config.ts
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:

server/api/storage/test.ts
export default defineEventHandler(async (event) => {
  // Список всех ключей
  const keys = await useStorage('redis').getKeys()

  // Установка ключа
  await useStorage('redis').setItem('foo', 'bar')

  // Удаление ключа
  await useStorage('redis').removeItem('foo')

  return {}
})
Узнайте больше о слое хранения Nitro.

В качестве альтернативы вы можете создать точку монтирования хранилища с помощью серверного плагина и конфигурации рантайма:

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)
})