Middleware

middleware.ts: redirecionamentos, reescritas, cabeçalhos e cookies antes de cada requisição, com config.matcher.

O middleware executa código antes de uma requisição ser concluída. Ele pode redirecionar, reescrever, definir cabeçalhos e cookies ou retornar uma resposta por conta própria.

Convenção

Crie middleware.ts na raiz do projeto (ou em src/), ao lado de app/. Exporte uma função middleware (ou uma exportação padrão) e um config opcional.

middleware.tsts
import { NextResponse, type NextRequest } from "@textjs/app/server";export function middleware(request: NextRequest) {  if (!request.cookies.has("session")) {    return NextResponse.redirect(new URL("/login", request.url));  }  return NextResponse.next();}export const config = {  matcher: ["/dashboard/:path*", "/api/:path*"],};

O middleware é executado antes dos manipuladores de rota, arquivos estáticos e páginas do console, em text dev, text start e em todos os adaptadores de deploy de servidor. Ele usa Request e Response da Web, então o mesmo arquivo roda em Node, Bun, Vercel Edge e Cloudflare Workers.

Correspondência de caminhos

Sem um config.matcher, o middleware é executado em todas as requisições. Os padrões de correspondência usam a mesma sintaxe do Next.js:

PadrãoCorresponde a
/about/about e /about/
/blog/:slug/blog/hello
/docs/:path*/docs, /docs/a/b
/a/:rest+/a/b, mas não /a
/user/:id(\\d+)/user/42, mas não /user/ada
/((?!api/).*)Tudo exceto /api/*

Adicione condições com has e missing (cabeçalho, cookie, consulta ou host):

middleware.tsts
export const config = {  matcher: [{ source: "/beta/:path*", has: [{ type: "cookie", key: "beta", value: "1" }] }],};

Os padrões se aplicam ao caminho sem basePath. Em text dev, as requisições de módulos do próprio Vite (/@vite/*, arquivos-fonte) nunca executam o middleware.

Cada host determina um caminho canônico antes de executar o middleware e o usa para a correspondência, request.nextUrl e a busca de rotas, manipuladores e arquivos. Os escapes percentuais são decodificados e o caminho é recodificado da forma como new URL() o escreve, os segmentos . e .. são resolvidos e as barras repetidas e finais são removidas. /ad%6din//users/ chega ao middleware como /admin/users, enquanto /café permanece /caf%C3%A9. Um padrão escrito com uma barra final, como /admin/, corresponde a /admin. Requisições cujo caminho contém uma barra ou barra invertida codificada (%2F, %5C), um caractere de controle ou uma codificação percentual inválida recebem um 400. As rotas diferenciam maiúsculas de minúsculas: /Admin não corresponde a /admin, e arquivos estáticos são servidos apenas com seu nome exato.

Faça a correspondência com as URLs em que um arquivo é servido, não apenas com seu nome. O Cloudflare Pages também serve report.html em /report, então um padrão para /report.html não protege /report. Liste ambos, matcher: ["/report", "/report.html"], ou mantenha os arquivos protegidos em um diretório e use um padrão que corresponda a ele, como /reports/:path* para arquivos em public/reports/.

NextResponse

APIEfeito
NextResponse.next()Continuar para a rota
NextResponse.redirect(url, status?)Usar 307 por padrão; url deve ser absoluta
NextResponse.rewrite(url)Servir outro caminho; acessar origens externas via proxy
NextResponse.json(body, init?)Responder com JSON
new Response(...)Responder com qualquer conteúdo

Não retorne nada para continuar sem alterações. next() e rewrite() aceitam { status } para alterar o status da resposta final. Apenas respostas criadas por NextResponse.next() / NextResponse.rewrite() direcionam o roteamento: qualquer outra Response, incluindo uma retornada por fetch(), é enviada ao cliente como está, com seus cabeçalhos x-middleware-* removidos.

request.nextUrl exclui basePath de pathname, enquanto seu href e toString() o incluem. Para permanecer dentro do app, clone-o: const url = request.nextUrl.clone(); url.pathname = "/login"; return NextResponse.redirect(url);.

middleware.tsts
export function middleware(request: NextRequest) {  if (request.nextUrl.pathname === "/status") {    return NextResponse.rewrite(new URL("/api/health", request.url));  }}

Cabeçalhos

Passe request.headers para alterar o que os manipuladores seguintes recebem e headers para adicionar cabeçalhos à resposta:

middleware.tsts
export function middleware(request: NextRequest) {  const headers = new Headers(request.headers);  headers.set("x-user", "ada");  const response = NextResponse.next({ request: { headers } });  response.headers.set("x-frame", "▼");  return response;}

Os cabeçalhos de resposta do middleware são adicionados à resposta final, a menos que o manipulador de rota defina o mesmo cabeçalho.

Cookies

request.cookies lê o cabeçalho Cookie; response.cookies escreve Set-Cookie.

middleware.tsts
export function middleware(request: NextRequest) {  const theme = request.cookies.get("theme")?.value ?? "geistDark";  const response = NextResponse.next();  response.cookies.set("theme", theme, { httpOnly: true, sameSite: "lax", maxAge: 60 * 60 * 24 });  return response;}

Cookies definidos em uma resposta de next() ou rewrite() também ficam visíveis para o manipulador de rota na mesma requisição.

Trabalho em segundo plano

O segundo argumento tem waitUntil(), que mantém os ambientes de execução serverless ativos após o envio da resposta:

middleware.tsts
import type { NextFetchEvent, NextRequest } from "@textjs/app/server";export function middleware(request: NextRequest, event: NextFetchEvent) {  event.waitUntil(fetch("https://analytics.example.com", { method: "POST", body: request.url }));}

Observações

  • Páginas renderizadas no cliente são renderizadas no navegador, então uma reescrita para outra página altera qual arquivo é servido, não qual rota o app do console renderiza. Reescritas para manipuladores de rota, arquivos estáticos e origens externas se comportam como esperado.
  • O middleware pode ler o corpo da requisição; os manipuladores de rota continuam recebendo esse corpo. Requisições excluídas por um padrão nunca têm seu corpo acessado.
  • request.url sempre tem a origem do servidor: destinos de requisição como //other.host/x são normalizados para uma única barra inicial.
  • O adaptador estático não pode executar middleware.
Editar esta página no GitHub