Capítulo 16 de 456

Route Handlers

Core Idea

Route Handlers criam handlers de request customizados por rota usando as Web APIs Request/Response, definidos em arquivos route.js|ts dentro de app. Equivalem às API Routes do pages, mas não devem ser usados junto delas na mesma rota.

Key Concepts

  • Convenção route.js|ts: exporta funções nomeadas por método HTTP (GET, POST, etc.); pode aninhar em qualquer nível de app, mas não pode coexistir com page.js no mesmo segmento de rota.
  • Métodos HTTP suportados: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS; método não suportado retorna 405 Method Not Allowed.
  • NextRequest/NextResponse: extensões do Request/Response nativos com helpers convenientes para casos avançados.
  • Caching de Route Handlers: não são cacheados por padrão; GET pode optar por cache com export const dynamic = 'force-static'; outros métodos nunca são cacheados, mesmo no mesmo arquivo de um GET cacheado.
  • Com Cache Components: GET Route Handlers seguem o mesmo modelo de prerender de rotas UI normais: rodam em request time por padrão, podem ser prerenderizados se não acessarem dado não cacheado/runtime, e podem usar use cache (extraído para uma função helper, não direto no corpo do handler) para incluir dado não cacheado na resposta estática.
  • Route Context Helper (RouteContext): tipo global gerado automaticamente (via next dev/next build/next typegen) para tipar o parâmetro context de um Route Handler tipado por rota.

Code Examples

export async function GET(request: Request) {}
  • O que demonstra: assinatura mínima de um Route Handler em app/api/route.ts.
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 })
}
  • O que demonstra: opt-in de cache para um GET handler via route config option.
import { cacheLife } from 'next/cache'

export async function GET() {
  const products = await getProducts()
  return Response.json(products)
}

async function getProducts() {
  'use cache'
  cacheLife('hours')
  return await db.query('SELECT * FROM products')
}
  • O que demonstra: com Cache Components, use cache extraído para função helper permite que dado não cacheado (query de DB) entre na resposta prerenderizada.
import type { NextRequest } from 'next/server'

export async function GET(_req: NextRequest, ctx: RouteContext<'/users/[id]'>) {
  const { id } = await ctx.params
  return Response.json({ id })
}
  • O que demonstra: tipagem do context com o helper global RouteContext para uma rota dinâmica.

Reference Tables

PageRouteResultado
app/page.jsapp/route.jsConflito
app/page.jsapp/api/route.jsVálido
app/[user]/page.jsapp/api/route.jsVálido

Anti-patterns

  • route.js no mesmo segmento de um page.js: conflito, não permitido (cada segmento pertence a um ou outro).
  • Chamar use cache diretamente no corpo do Route Handler: não suportado; extrair para uma função helper separada.
  • Esperar que POST/PUT/etc. sejam cacheados por estarem no mesmo arquivo de um GET com force-static: só GET pode ser cacheado; os demais métodos nunca são.

Key Takeaways

  1. Route Handlers substituem API Routes dentro de app; não use os dois padrões juntos na mesma rota.
  2. GET pode ser cacheado, e só com opt-in explícito (force-static ou, com Cache Components, ausência de acesso a dado não cacheado/runtime).
  3. Com Cache Components, o mesmo raciocínio de prerender de páginas (dado estático vs. Math.random()/headers() vs. use cache) se aplica a GET handlers.
  4. Route Handlers não participam de layouts nem navegação client-side: são o nível de roteamento mais baixo.
  5. RouteContext<'/path/[param]'> tipa context.params automaticamente a partir da rota, gerado por next dev/build/typegen.

Connects To

  • Caching (ch009): o comportamento de prerender de GET handlers sob Cache Components é literalmente o mesmo modelo descrito para páginas.
  • Metadata and OG images (ch015): Route Handlers especiais (sitemap.ts, opengraph-image.tsx, icon.tsx) seguem a mesma regra de estático-por-padrão salvo uso de API de request-time.