Capítulo 26 de 456

Backend for Frontend

Core Idea

Next.js pode servir como camada de API pública (não substituto de backend completo) via Route Handlers e proxy, retornando qualquer tipo de conteúdo, fazendo content negotiation, proxy para outro backend, e recebendo webhooks/callbacks.

Key Concepts

  • Route Handler: arquivo route.ts/route.js que trata requisições HTTP públicas por método (GET, POST, etc.), retorna qualquer content-type.
  • Content negotiation via rewrites: usar has: [{ type: 'header', key: 'accept', value: '...' }] em next.config.js para servir Markdown a agentes de IA e HTML a browsers na mesma URL.
  • request.clone(): necessário porque o body de um Request só pode ser lido uma vez; ler de novo sem clone lança erro.
  • NextRequest/NextResponse: extensões das Web APIs Request/Response; NextRequest.nextUrl expõe pathname/searchParams parseados; NextResponse tem helpers next(), json(), redirect(), rewrite(). Compatíveis com as APIs padrão (aceitam/retornam uma pela outra).
  • proxy (antes chamado middleware): único arquivo por projeto, roda antes da rota, usa config.matcher para escopo; pode autenticar, reescrever (NextResponse.rewrite) ou redirecionar (NextResponse.redirect).
  • Factory pattern de bibliotecas: libs terceiras costumam exportar um createHandler/createMiddleware para gerar o handler/proxy compartilhado.

Code Examples

// next.config.js — content negotiation por Accept header
module.exports = {
  async rewrites() {
    return [{
      source: '/docs/:slug*',
      destination: '/docs/md/:slug*',
      has: [{ type: 'header', key: 'accept', value: '(.*)text/markdown(.*)' }],
    }]
  },
}
  • O que demonstra: mesma URL serve HTML pra browser e Markdown pra agente de IA; sempre setar header Vary: Accept na resposta pra não poluir cache compartilhado.
// app/api/[...slug]/route.ts — proxy validado para backend externo
export async function POST(request: Request, { params }) {
  const clonedRequest = request.clone()
  const isValid = await isValidRequest(clonedRequest)
  if (!isValid) return new Response(null, { status: 400 })
  const { slug } = await params
  const proxyURL = new URL(slug.join('/'), 'https://nextjs.org')
  return fetch(new Request(proxyURL, request))
}
  • O que demonstra: validar antes de repassar; clonar request pra poder ler o body na validação e ainda usá-lo no fetch.
// proxy.ts — bloqueio de rota não autenticada
export const config = { matcher: '/api/:function*' }
export function proxy(request: Request) {
  if (!isAuthenticated(request)) {
    return Response.json({ success: false }, { status: 401 })
  }
}
  • O que demonstra: proxy intercepta antes da rota ser alcançada, ideal para auth centralizada.

Reference Tables

Caso de usoFerramenta
Endpoint público HTTP qualquer content-typeRoute Handler
Interceptar/reescrever/redirecionar antes da rotaproxy
Negociação simples por headerrewrites + has
Negociação avançadaproxy

Anti-patterns

  • Expor detalhes de erro ao cliente: nunca vazar mensagem de exceção crua na resposta.
  • Passar geo-location/dados sensíveis via GET: URL pode ser logada/cacheada; usar POST.
  • Fetch de Server Component prerenderizado apontando pro próprio Route Handler: falha o build (não há servidor escutando durante o build); buscar dados direto da fonte em Server Components.
  • Confiar só no proxy para autenticação/autorização: sempre revalidar credenciais no Route Handler também.
  • Passar headers de request recebidos direto pra resposta: pode vazar valores sensíveis ao client; ser deliberado sobre upstream headers (NextResponse.next({request:{headers}})) vs response headers.
  • Usar Server Actions para data fetching: são enfileiradas, geram execução sequencial; usar para mutação, não leitura.

Key Takeaways

  1. Route Handlers cobrem qualquer content-type (JSON, XML, RSS, arquivos), incluindo convenções de arquivo (sitemap.xml, robots.txt) e customizadas.
  2. Content negotiation via Accept header + rewrites/proxy permite servir Markdown pra LLMs e HTML pra humanos na mesma URL.
  3. proxy é o único ponto de interceptação global por projeto (matcher configurável); libs terceiras às vezes ainda chamam de "middleware".
  4. Em export mode, só GET funciona, com dynamic = 'force-static'.
  5. Deploy serverless impõe limites: sem estado compartilhado entre requests, sem WebSockets persistentes, timeouts em handlers longos.

Connects To

  • Route Handlers (route.js) API reference: detalhamento completo de convenções, cookies, headers, streaming.
  • proxy.js API reference: matcher, negative matching, exemplos completos.
  • Data Security / Authentication: autorização real por trás de qualquer endpoint público.