servidor
El directorio server/ se utiliza para registrar controladores de API y del servidor en tu aplicación.
Nuxt escanea automáticamente los archivos dentro de estos directorios para registrar controladores de API y del servidor con soporte para Hot Module Replacement (HMR).
-| server/
---| api/
-----| hello.ts # /api/hello
---| routes/
-----| bonjour.ts # /bonjour
---| middleware/
-----| log.ts # registrar todas las solicitudes
Cada archivo debe exportar una función por defecto definida con defineEventHandler()
o eventHandler()
(alias).
El controlador puede devolver directamente datos JSON, una Promise
, o usar event.node.res.end()
para enviar una respuesta.
export default defineEventHandler((event) => {
return {
hello: 'world'
}
})
Ahora puedes llamar universalmente a esta API en tus páginas y componentes:
<script setup lang="ts">
const { data } = await useFetch('/api/hello')
</script>
<template>
<pre>{{ data }}</pre>
</template>
Rutas del Servidor
Los archivos dentro de ~/server/api
se prefijan automáticamente con /api
en su ruta.
Para agregar rutas del servidor sin el prefijo /api
, colócalos en el directorio ~/server/routes
.
Ejemplo:
export default defineEventHandler(() => 'Hello World!')
Dado el ejemplo anterior, la ruta /hello
será accesible en http://localhost:3000/hello.
Ten en cuenta que actualmente las rutas del servidor no soportan toda la funcionalidad de rutas dinámicas como lo hacen las páginas.
Middleware del Servidor
Nuxt leerá automáticamente cualquier archivo en ~/server/middleware
para crear middleware del servidor para tu proyecto.
Los controladores de middleware se ejecutarán en cada solicitud antes de cualquier otra ruta del servidor para agregar o verificar encabezados, registrar solicitudes, o extender el objeto de solicitud del evento.
Los controladores de middleware no deben devolver nada (ni cerrar o responder a la solicitud) y solo deben inspeccionar o extender el contexto de la solicitud o lanzar un error.
Ejemplos:
export default defineEventHandler((event) => {
console.log('Nueva solicitud: ' + getRequestURL(event))
})
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})
Plugins del Servidor
Nuxt leerá automáticamente cualquier archivo en el directorio ~/server/plugins
y los registrará como plugins de Nitro. Esto permite extender el comportamiento en tiempo de ejecución de Nitro y engancharse a eventos del ciclo de vida.
Ejemplo:
export default defineNitroPlugin((nitroApp) => {
console.log('Plugin de Nitro', nitroApp)
})
Utilidades del Servidor
Las rutas del servidor son impulsadas por h3js/h3 que viene con un conjunto útil de ayudantes.
Ver también Ayudantes de Solicitud H3 DisponiblesPuedes agregar más ayudantes tú mismo dentro del directorio ~/server/utils
.
Por ejemplo, puedes definir una utilidad de controlador personalizada que envuelva al controlador original y realice operaciones adicionales antes de devolver la respuesta final.
Ejemplo:
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 {
// hacer algo antes del controlador de ruta
const response = await handler(event)
// hacer algo después del controlador de ruta
return { response }
} catch (err) {
// Manejo de errores
return { err }
}
})
Tipos del Servidor
Esta característica está disponible desde Nuxt >= 3.5
Para mejorar la claridad dentro de tu IDE entre las importaciones automáticas de 'nitro' y 'vue', puedes agregar un ~/server/tsconfig.json
con el siguiente contenido:
{
"extends": "../.nuxt/tsconfig.server.json"
}
Actualmente, estos valores no serán respetados al verificar tipos (nuxt typecheck
), pero deberías obtener mejores sugerencias de tipo en tu IDE.
Recetas
Parámetros de Ruta
Las rutas del servidor pueden usar parámetros dinámicos dentro de corchetes en el nombre del archivo como /api/hello/[name].ts
y ser accedidos a través de event.context.params
.
export default defineEventHandler((event) => {
const name = getRouterParam(event, 'name')
return `Hello, ${name}!`
})
Alternativamente, usa getValidatedRouterParams
con un validador de esquema como Zod para seguridad en tiempo de ejecución y de tipo.
Ahora puedes llamar universalmente a esta API en /api/hello/nuxt
y obtener Hello, nuxt!
.
Coincidencia de Método HTTP
Los nombres de archivo de los controladores pueden tener el sufijo .get
, .post
, .put
, .delete
, ... para coincidir con el Método HTTP de la solicitud.
export default defineEventHandler(() => 'Controlador de prueba GET')
export default defineEventHandler(() => 'Controlador de prueba POST')
Dado el ejemplo anterior, al obtener /test
con:
- Método GET: Devuelve
Controlador de prueba GET
- Método POST: Devuelve
Controlador de prueba POST
- Cualquier otro método: Devuelve error 405
También puedes usar index.[method].ts
dentro de un directorio para estructurar tu código de manera diferente, esto es útil para crear espacios de nombres de API.
export default defineEventHandler((event) => {
// manejar solicitudes GET para el endpoint `api/foo`
})
Ruta Catch-all
Las rutas catch-all son útiles para el manejo de rutas de respaldo.
Por ejemplo, crear un archivo llamado ~/server/api/foo/[...].ts
registrará una ruta catch-all para todas las solicitudes que no coincidan con ningún controlador de ruta, como /api/foo/bar/baz
.
export default defineEventHandler((event) => {
// event.context.path para obtener la ruta: '/api/foo/bar/baz'
// event.context.params._ para obtener el segmento de la ruta: 'bar/baz'
return `Controlador por defecto de foo`
})
Puedes establecer un nombre para la ruta catch-all usando ~/server/api/foo/[...slug].ts
y acceder a él a través de event.context.params.slug
.
export default defineEventHandler((event) => {
// event.context.params.slug para obtener el segmento de la ruta: 'bar/baz'
return `Controlador por defecto de foo`
})
Manejo del Cuerpo
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { body }
})
Alternativamente, usa readValidatedBody
con un validador de esquema como Zod para seguridad en tiempo de ejecución y de tipo.
Ahora puedes llamar universalmente a esta API usando:
async function submit() {
const { body } = await $fetch('/api/submit', {
method: 'post',
body: { test: 123 }
})
}
Estamos usando submit.post.ts
en el nombre del archivo solo para coincidir con solicitudes con el método POST
que pueden aceptar el cuerpo de la solicitud. Al usar readBody
dentro de una solicitud GET, readBody
lanzará un error HTTP 405 Method Not Allowed
.
Parámetros de Consulta
Consulta de ejemplo /api/query?foo=bar&baz=qux
export default defineEventHandler((event) => {
const query = getQuery(event)
return { a: query.foo, b: query.baz }
})
Alternativamente, usa getValidatedQuery
con un validador de esquema como Zod para seguridad en tiempo de ejecución y de tipo.
Manejo de Errores
Si no se lanzan errores, se devolverá un código de estado 200 OK
.
Cualquier error no capturado devolverá un error HTTP 500 Internal Server Error
.
Para devolver otros códigos de error, lanza una excepción con createError
:
export default defineEventHandler((event) => {
const id = parseInt(event.context.params.id) as number
if (!Number.isInteger(id)) {
throw createError({
statusCode: 400,
statusMessage: 'ID debería ser un entero',
})
}
return 'Todo bien'
})
Códigos de Estado
Para devolver otros códigos de estado, usa la utilidad setResponseStatus
.
Por ejemplo, para devolver 202 Accepted
export default defineEventHandler((event) => {
setResponseStatus(event, 202)
})
Configuración en Tiempo de Ejecución
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
})
Dar el event
como argumento a useRuntimeConfig
es opcional, pero se recomienda pasarlo para obtener la configuración en tiempo de ejecución sobrescrita por variables de entorno en tiempo de ejecución para rutas del servidor.
Cookies de Solicitud
export default defineEventHandler((event) => {
const cookies = parseCookies(event)
return { cookies }
})
Reenvío de Contexto y Encabezados
Por defecto, ni los encabezados de la solicitud entrante ni el contexto de la solicitud se reenvían al hacer solicitudes fetch en rutas del servidor. Puedes usar event.$fetch
para reenviar el contexto de la solicitud y los encabezados al hacer solicitudes fetch en rutas del servidor.
export default defineEventHandler((event) => {
return event.$fetch('/api/forwarded')
})
Los encabezados que no están destinados a ser reenviados no serán incluidos en la solicitud. Estos encabezados incluyen, por ejemplo:
transfer-encoding
, connection
, keep-alive
, upgrade
, expect
, host
, accept
Esperando Promesas Después de la Respuesta
Al manejar solicitudes del servidor, es posible que necesites realizar tareas asincrónicas que no deberían bloquear la respuesta al cliente (por ejemplo, almacenamiento en caché y registro). Puedes usar event.waitUntil
para esperar una promesa en segundo plano sin retrasar la respuesta.
El método event.waitUntil
acepta una promesa que se esperará antes de que el controlador termine, asegurando que la tarea se complete incluso si el servidor de otro modo terminaría el controlador justo después de que se envíe la respuesta. Esto se integra con los proveedores de tiempo de ejecución para aprovechar sus capacidades nativas para manejar operaciones asincrónicas después de que se envía la respuesta.
const timeConsumingBackgroundTask = async () => {
await new Promise((resolve) => setTimeout(resolve, 1000))
};
export default eventHandler((event) => {
// programa una tarea en segundo plano sin bloquear la respuesta
event.waitUntil(timeConsumingBackgroundTask())
// envía inmediatamente la respuesta al cliente
return 'hecho'
});
Uso Avanzado
Configuración de Nitro
Puedes usar la clave nitro
en nuxt.config
para establecer directamente la configuración de Nitro.
Esta es una opción avanzada. La configuración personalizada puede afectar las implementaciones en producción, ya que la interfaz de configuración podría cambiar con el tiempo cuando Nitro se actualice en versiones semver-minor de Nuxt.
export default defineNuxtConfig({
// https://nitro.build/config
nitro: {}
})
Router Anidado
import { createRouter, defineEventHandler, useBase } from 'h3'
const router = createRouter()
router.get('/test', defineEventHandler(() => 'Hello World'))
export default useBase('/api/hello', router.handler)
Envío de Streams
Esta es una característica experimental y está disponible en todos los entornos.
import fs from 'node:fs'
import { sendStream } from 'h3'
export default defineEventHandler((event) => {
return sendStream(event, fs.createReadStream('/path/to/file'))
})
Envío de Redirección
export default defineEventHandler(async (event) => {
await sendRedirect(event, '/path/redirect/to', 302)
})
Controlador o Middleware Legacy
export default fromNodeMiddleware((req, res) => {
res.end('Controlador legacy')
})
El soporte legacy es posible usando h3js/h3, pero se aconseja evitar controladores legacy tanto como sea posible.
export default fromNodeMiddleware((req, res, next) => {
console.log('Middleware legacy')
next()
})
Nunca combines la devolución de llamada next()
con un middleware legacy que sea async
o devuelva una Promise
.
Almacenamiento del Servidor
Nitro proporciona una capa de almacenamiento multiplataforma. Para configurar puntos de montaje de almacenamiento adicionales, puedes usar nitro.storage
, o plugins del servidor.
Ejemplo de agregar un almacenamiento Redis:
Usando nitro.storage
:
export default defineNuxtConfig({
nitro: {
storage: {
redis: {
driver: 'redis',
/* opciones del conector redis */
port: 6379, // Puerto de Redis
host: "127.0.0.1", // Host de Redis
username: "", // necesita Redis >= 6
password: "",
db: 0, // Por defecto es 0
tls: {} // tls/ssl
}
}
}
})
Luego en tu controlador de API:
export default defineEventHandler(async (event) => {
// Lista todas las claves con
const keys = await useStorage('redis').getKeys()
// Establece una clave con
await useStorage('redis').setItem('foo', 'bar')
// Elimina una clave con
await useStorage('redis').removeItem('foo')
return {}
})
Alternativamente, puedes crear un punto de montaje de almacenamiento usando un plugin del servidor y configuración en tiempo de ejecución:
import redisDriver from 'unstorage/drivers/redis'
export default defineNitroPlugin(() => {
const storage = useStorage()
// Pasa dinámicamente las credenciales desde la configuración en tiempo de ejecución, u otras fuentes
const driver = redisDriver({
base: 'redis',
host: useRuntimeConfig().redis.host,
port: useRuntimeConfig().redis.port,
/* otras opciones del conector redis */
})
// Monta el driver
storage.mount('redis', driver)
})
※Esta página es una traducción no oficial de la documentación oficial de Nuxt.js.
La página correspondiente en la documentación oficial está aquí:
https://nuxt.com/docs/3.x/guide/directory-structure/server