Manejadores de Ruta

Los Manejadores de Ruta (Route Handlers) le permiten crear manejadores de solicitud personalizados para una ruta determinada utilizando las APIs de Request y Response de la Web.

Archivo Especial Route.js

Nota importante: Los Manejadores de Ruta solo están disponibles dentro del directorio app. Son equivalentes a las Rutas API dentro del directorio pages, lo que significa que no necesita usar Rutas API y Manejadores de Ruta juntos.

Convención

Los Manejadores de Ruta se definen en un archivo route.js|ts dentro del directorio app:

export async function GET(request: Request) {}

Los Manejadores de Ruta pueden anidarse en cualquier lugar dentro del directorio app, similar a page.js y layout.js. Pero no puede haber un archivo route.js en el mismo segmento de ruta que page.js.

Métodos HTTP soportados

Se admiten los siguientes métodos HTTP: GET, POST, PUT, PATCH, DELETE, HEAD y OPTIONS. Si se llama a un método no admitido, Next.js devolverá una respuesta 405 Método No Permitido.

APIs extendidas NextRequest y NextResponse

Además de admitir las APIs nativas Request y Response, Next.js las extiende con NextRequest y NextResponse para proporcionar ayudantes convenientes para casos de uso avanzados.

Comportamiento

Almacenamiento en caché

Los Manejadores de Ruta no se almacenan en caché por defecto. Sin embargo, puede optar por el almacenamiento en caché para métodos GET. Otros métodos HTTP admitidos no se almacenan en caché. Para almacenar en caché un método GET, use una opción de configuración de ruta como export const dynamic = 'force-static' en su archivo de Manejador de Ruta.

export const dynamic = 'force-static'

export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY,
    },
  })
  const data = await res.json()

  return Response.json({ data })
}

Nota importante: Otros métodos HTTP admitidos no se almacenan en caché, incluso si se colocan junto a un método GET que sí lo está, en el mismo archivo.

Manejadores de Ruta especiales

Los Manejadores de Ruta especiales como sitemap.ts, opengraph-image.tsx, icon.tsx y otros archivos de metadatos permanecen estáticos por defecto a menos que utilicen APIs dinámicas u opciones de configuración dinámica.

Resolución de Rutas

Puede considerar un route como la primitiva de enrutamiento de nivel más bajo.

  • No participan en diseños (layouts) o navegaciones del lado del cliente como page.
  • No puede haber un archivo route.js en la misma ruta que page.js.
PáginaRutaResultado
app/page.jsapp/route.jsCross Icon Conflicto
app/page.jsapp/api/route.jsCheck Icon Válido
app/[user]/page.jsapp/api/route.jsCheck Icon Válido

Cada archivo route.js o page.js toma todos los verbos HTTP para esa ruta.

export default function Page() {
  return <h1>Hello, Next.js!</h1>
}

// ❌ Conflicto
// `app/route.ts`
export async function POST(request: Request) {}

Ejemplos

Los siguientes ejemplos muestran cómo combinar Manejadores de Ruta con otras APIs y características de Next.js.

Revalidación de datos en caché

Puede revalidar datos en caché usando Regeneración Estática Incremental (ISR):

export const revalidate = 60

export async function GET() {
  const data = await fetch('https://api.vercel.app/blog')
  const posts = await data.json()

  return Response.json(posts)
}

Cookies

Puede leer o establecer cookies con cookies de next/headers. Esta función de servidor se puede llamar directamente en un Manejador de Ruta o anidarse dentro de otra función.

Alternativamente, puede devolver una nueva Response usando la cabecera Set-Cookie.

import { cookies } from 'next/headers'

export async function GET(request: Request) {
  const cookieStore = await cookies()
  const token = cookieStore.get('token')

  return new Response('Hello, Next.js!', {
    status: 200,
    headers: { 'Set-Cookie': `token=${token.value}` },
  })
}

También puede usar las APIs web subyacentes para leer cookies de la solicitud (NextRequest):

import { type NextRequest } from 'next/server'

export async function GET(request: NextRequest) {
  const token = request.cookies.get('token')
}

Cabeceras

Puede leer cabeceras con headers de next/headers. Esta función de servidor se puede llamar directamente en un Manejador de Ruta o anidarse dentro de otra función.

Esta instancia de headers es de solo lectura. Para establecer cabeceras, debe devolver una nueva Response con nuevas headers.

import { headers } from 'next/headers'

export async function GET(request: Request) {
  const headersList = await headers()
  const referer = headersList.get('referer')

  return new Response('Hello, Next.js!', {
    status: 200,
    headers: { referer: referer },
  })
}

También puede usar las APIs web subyacentes para leer cabeceras de la solicitud (NextRequest):

import { type NextRequest } from 'next/server'

export async function GET(request: NextRequest) {
  const requestHeaders = new Headers(request.headers)
}

Redirecciones

import { redirect } from 'next/navigation'

export async function GET(request: Request) {
  redirect('https://nextjs.org/')
}

Segmentos de Ruta Dinámicos

Los Manejadores de Ruta pueden usar Segmentos Dinámicos para crear manejadores de solicitud a partir de datos dinámicos.

export async function GET(
  request: Request,
  { params }: { params: Promise<{ slug: string }> }
) {
  const { slug } = await params // 'a', 'b', o 'c'
}
RutaURL ejemploparams
app/items/[slug]/route.js/items/aPromise<{ slug: 'a' }>
app/items/[slug]/route.js/items/bPromise<{ slug: 'b' }>
app/items/[slug]/route.js/items/cPromise<{ slug: 'c' }>

Parámetros de consulta URL

El objeto de solicitud pasado al Manejador de Ruta es una instancia de NextRequest, que incluye algunos métodos adicionales convenientes, como aquellos para manejar más fácilmente los parámetros de consulta.

import { type NextRequest } from 'next/server'

export function GET(request: NextRequest) {
  const searchParams = request.nextUrl.searchParams
  const query = searchParams.get('query')
  // query es "hello" para /api/search?query=hello
}

Streaming

El streaming se usa comúnmente en combinación con Modelos de Lenguaje Grande (LLMs), como OpenAI, para contenido generado por IA. Aprenda más sobre el AI SDK.

import { openai } from '@ai-sdk/openai'
import { StreamingTextResponse, streamText } from 'ai'

export async function POST(req: Request) {
  const { messages } = await req.json()
  const result = await streamText({
    model: openai('gpt-4-turbo'),
    messages,
  })

  return new StreamingTextResponse(result.toAIStream())
}

Estas abstracciones utilizan las APIs web para crear un flujo. También puede usar las APIs web subyacentes directamente.

// https://developer.mozilla.org/docs/Web/API/ReadableStream#convert_async_iterator_to_stream
function iteratorToStream(iterator: any) {
  return new ReadableStream({
    async pull(controller) {
      const { value, done } = await iterator.next()

      if (done) {
        controller.close()
      } else {
        controller.enqueue(value)
      }
    },
  })
}

function sleep(time: number) {
  return new Promise((resolve) => {
    setTimeout(resolve, time)
  })
}

const encoder = new TextEncoder()

async function* makeIterator() {
  yield encoder.encode('<p>One</p>')
  await sleep(200)
  yield encoder.encode('<p>Two</p>')
  await sleep(200)
  yield encoder.encode('<p>Three</p>')
}

export async function GET() {
  const iterator = makeIterator()
  const stream = iteratorToStream(iterator)

  return new Response(stream)
}

Cuerpo de la solicitud

Puede leer el cuerpo de la Request utilizando los métodos estándar de la API web:

export async function POST(request: Request) {
  const res = await request.json()
  return Response.json({ res })
}

Cuerpo de la solicitud FormData

Puede leer FormData usando la función request.formData():

export async function POST(request: Request) {
  const formData = await request.formData()
  const name = formData.get('name')
  const email = formData.get('email')
  return Response.json({ name, email })
}

Dado que los datos de formData son todos strings, es posible que desee usar zod-form-data para validar la solicitud y recuperar los datos en el formato que prefiera (por ejemplo, number).

CORS

Puede establecer cabeceras CORS para un Manejador de Ruta específico utilizando los métodos estándar de la API web:

export async function GET(request: Request) {
  return new Response('Hello, Next.js!', {
    status: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    },
  })
}

Nota importante:

  • Para agregar cabeceras CORS a múltiples Manejadores de Ruta, puede usar Middleware o el archivo next.config.js.
  • Alternativamente, consulte nuestro paquete de ejemplo CORS.

Webhooks

Puede utilizar un Manejador de Ruta (Route Handler) para recibir webhooks de servicios de terceros:

export async function POST(request: Request) {
  try {
    const text = await request.text()
    // Procesar el payload del webhook
  } catch (error) {
    return new Response(`Webhook error: ${error.message}`, {
      status: 400,
    })
  }

  return new Response('Success!', {
    status: 200,
  })
}

Cabe destacar que, a diferencia de las Rutas API con el Enrutador de Páginas (Pages Router), no es necesario usar bodyParser ni ninguna configuración adicional.

Respuestas sin Interfaz de Usuario

Puede utilizar Manejadores de Ruta para devolver contenido que no sea de interfaz de usuario. Tenga en cuenta que sitemap.xml, robots.txt, íconos de la aplicación e imágenes para open graph tienen soporte incorporado.

export async function GET() {
  return new Response(
    `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">

<channel>
  <title>Next.js Documentation</title>
  <link>https://nextjs.org/docs</link>
  <description>The React Framework for the Web</description>
</channel>

</rss>`,
    {
      headers: {
        'Content-Type': 'text/xml',
      },
    }
  )
}

Opciones de Configuración de Segmento

Los Manejadores de Ruta utilizan la misma configuración de segmento de ruta que las páginas y diseños.

export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'

Consulte la referencia de API para más detalles.