nuxt logo

Traducción de Documentación (No Oficial)

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

Estructura del directorio
-| 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.

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

Ahora puedes llamar universalmente a esta API en tus páginas y componentes:

pages/index.vue
<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:

server/routes/hello.ts
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:

server/middleware/log.ts
export default defineEventHandler((event) => {
  console.log('Nueva solicitud: ' + getRequestURL(event))
})
server/middleware/auth.ts
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:

server/plugins/nitroPlugin.ts
export default defineNitroPlugin((nitroApp) => {
  console.log('Plugin de Nitro', nitroApp)
})
Ver también Plugins de Nitro

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 Disponibles

Puedes 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:

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 {
      // 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:

server/tsconfig.json
{
  "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.

server/api/hello/[name\
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.

server/api/test.get.ts
export default defineEventHandler(() => 'Controlador de prueba GET')
server/api/test.post.ts
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.

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

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

server/api/submit.post.ts
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:

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

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

server/api/validation/[id\
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

server/api/validation/[id\
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

server/api/cookies.ts
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.

server/api/forward.ts
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.

server/api/background-task.ts
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.

nuxt.config.ts
export default defineNuxtConfig({
  // https://nitro.build/config
  nitro: {}
})
Ver también guide > concepts > server-engine

Router Anidado

server/api/hello/[...slug\
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.

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

Envío de Redirección

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

Controlador o Middleware Legacy

server/api/legacy.ts
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.

server/middleware/legacy.ts
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:

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

server/api/storage/test.ts
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 {}
})
Ver también nitro.build > guide > storage

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