Server routes и Nitro: бэкенд внутри Nuxt
Папка server/api превращает Nuxt в фулстек: файл становится эндпоинтом, а движок Nitro исполняет его как бэкенд — без отдельного сервера.
Суть: файлы в server/api/ автоматически становятся API-маршрутами с префиксом /api. Обработчик оборачивается в defineEventHandler, возвращённый объект сериализуется в JSON. Этот код исполняет движок Nitro и никогда не отдаёт в браузер.
До сих пор мы дёргали /api/..., как будто бэкенд уже существует. Пора его написать — прямо внутри Nuxt. Это и делает Nuxt фулстек-фреймворком: серверная часть живёт в том же проекте, что и фронтенд, и работает на движке Nitro.
Правило файловой маршрутизации работает и здесь. Файл в server/api/ становится эндпоинтом с префиксом /api. server/api/hello.ts отвечает на /api/hello. Обработчик оборачивают в defineEventHandler, а то, что вы вернёте, Nitro сам превратит в JSON:
// server/api/products.ts
export default defineEventHandler((event) => {
return [
{ id: 1, name: "Книга", price: 500 },
{ id: 2, name: "Ручка", price: 50 },
]
})
Теперь useFetch("/api/products") на странице получит этот массив. Здесь, на сервере, можно безопасно обращаться к базе данных, читать секретные ключи из переменных окружения, ходить во внешние API — ничего из этого не попадёт в браузер. Это принципиально: клиентский код видят все, серверный — никто.
Полный фулстек-цикл
Страница: useFetch("/api/products")
|
v
Nitro находит server/api/products.ts
|
v
defineEventHandler выполняется (БД, секреты — здесь)
|
v
возвращённый объект -> JSON -> страница
Грамотная обработка состояний — это ещё и про восприятие скорости. Пустой белый экран в ожидании данных ощущается медленнее, чем тот же интервал, но со скелетон-заглушкой, повторяющей будущую разметку. Поэтому ветку pending полезно оформлять не текстом «Загрузка...», а скелетоном карточек или строк. А ветку error стоит делать действенной: не просто «что-то пошло не так», а кнопку «Повторить», вызывающую refresh(). Эти мелочи отличают черновой прототип от продукта, которым приятно пользоваться даже на нестабильной сети.
Как работает под капотом
Nitro — это серверный движок Nuxt, построенный на библиотеке h3. При сборке он компилирует папку server/ в обработчики и поднимает их по соответствующим путям. Каждый запрос приходит как объект event, из которого хелперы h3 достают тело, параметры, заголовки. Вернули значение — Nitro сериализует его в ответ. Магия в том, что один и тот же Nitro отвечает и за SSR страниц, и за API: это единый сервер.
Смоделируем мини-роутер Nitro: сопоставление пути с обработчиком и сериализацию ответа:
// Мини-Nitro: путь файла -> эндпоинт -> JSON.
function defineEventHandler(fn) { return fn; } // как в Nuxt
// "Файлы" в server/api/
const handlers = {
"/api/products": defineEventHandler(() => [
{ id: 1, name: "Книга" },
{ id: 2, name: "Ручка" },
]),
"/api/hello": defineEventHandler((event) => ({
message: "Привет, " + (event.name || "гость"),
})),
};
function handleRequest(path, event) {
const handler = handlers[path];
if (!handler) return { status: 404, body: "Not Found" };
const result = handler(event || {});
return { status: 200, body: JSON.stringify(result) }; // -> JSON
}
console.log(handleRequest("/api/products"));
console.log(handleRequest("/api/hello", { name: "Алиса" }));
console.log(handleRequest("/api/unknown"));
Попробуй сам ▶ — путь сопоставляется с обработчиком, результат превращается в JSON, неизвестный путь даёт 404. Так же, но мощнее, работает Nitro.
Частые ошибки
- Забыть
defineEventHandler. Без обёртки Nitro не распознает обработчик. - Класть серверную логику вне
server/. Только код вserver/гарантированно не попадёт в браузер. - Возвращать несериализуемое. Ответ идёт в JSON — функции и
Mapтуда не положишь как есть.
Best practices
- Всю работу с БД, секретами и внешними API делайте в
server/api. - Возвращайте чистые JSON-объекты; статус и заголовки задавайте через хелперы h3.
- Общую серверную логику выносите в
server/utils— она тоже авто-импортируется.
Итог: server/api и Nitro дают полноценный бэкенд внутри Nuxt — это и делает его фулстек. Дальше научимся различать HTTP-методы и работать с динамическими серверными маршрутами.