Middleware
middleware.ts: reindirizzamenti, riscritture, header e cookie prima di ogni richiesta, con config.matcher.
Il middleware esegue codice prima che una richiesta sia completata. Può reindirizzare, riscrivere, impostare header e cookie oppure restituire direttamente una risposta.
Convenzione
Crea middleware.ts nella directory radice del progetto (o in src/), accanto ad app/. Esporta una funzione middleware (o un export predefinito) e un config facoltativo.
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*"],};Il middleware viene eseguito prima dei route handler, dei file statici e delle pagine della console, con text dev, text start e ogni adattatore di deploy server. Usa le API Web Request e Response, quindi lo stesso file funziona su Node, Bun, Vercel Edge e Cloudflare Workers.
Corrispondenza dei percorsi
Senza config.matcher, il middleware viene eseguito per ogni richiesta. I matcher usano la stessa sintassi di Next.js:
| Matcher | Corrispondenze |
|---|---|
/about | /about e /about/ |
/blog/:slug | /blog/hello |
/docs/:path* | /docs, /docs/a/b |
/a/:rest+ | /a/b, escluso /a |
/user/:id(\\d+) | /user/42, escluso /user/ada |
/((?!api/).*) | Tutto tranne /api/* |
Aggiungi condizioni con has e missing (header, cookie, query o host):
export const config = { matcher: [{ source: "/beta/:path*", has: [{ type: "cookie", key: "beta", value: "1" }] }],};I matcher si applicano al pathname senza basePath. In text dev, le richieste dei moduli di Vite (/@vite/*, file sorgente) non eseguono mai il middleware.
Ogni host determina un unico pathname canonico prima di eseguire il middleware e lo usa per la corrispondenza, per request.nextUrl e per la ricerca di route, handler e file. Le sequenze di escape percentuali vengono decodificate e il percorso viene ricodificato come lo scrive new URL(), i segmenti . e .. vengono risolti e le barre ripetute e finali vengono rimosse. /ad%6din//users/ raggiunge il middleware come /admin/users, mentre /café rimane /caf%C3%A9. Un matcher scritto con una barra finale, come /admin/, corrisponde a /admin. Le richieste il cui percorso contiene una barra o una barra inversa codificata (%2F, %5C), un carattere di controllo o una codifica percentuale non valida ricevono un 400. Le route distinguono tra maiuscole e minuscole: /Admin non corrisponde a /admin e i file statici vengono serviti solo con il loro nome esatto.
Fai corrispondere gli URL a cui un file viene servito, non soltanto il suo nome. Cloudflare Pages serve report.html anche su /report, quindi un matcher per /report.html non protegge /report. Elencali entrambi, matcher: ["/report", "/report.html"], oppure mantieni i file protetti in una directory e crea un matcher per quella directory, come /reports/:path* per i file in public/reports/.
NextResponse
| API | Effetto |
|---|---|
NextResponse.next() | Prosegue verso la route |
NextResponse.redirect(url, status?) | 307 per impostazione predefinita; url deve essere assoluto |
NextResponse.rewrite(url) | Serve un altro percorso; le origini esterne passano tramite proxy |
NextResponse.json(body, init?) | Risponde con JSON |
new Response(...) | Risponde con qualsiasi contenuto |
Non restituire nulla per proseguire senza modifiche. next() e rewrite() accettano { status } per cambiare lo stato della risposta finale. Solo le risposte create da NextResponse.next() / NextResponse.rewrite() guidano il routing: qualsiasi altra Response, inclusa una restituita da fetch(), viene inviata al client così com’è, dopo aver rimosso i suoi header x-middleware-*.
request.nextUrl esclude basePath da pathname, mentre i suoi href e toString() lo includono. Per rimanere all’interno dell’app, clonalo: const url = request.nextUrl.clone(); url.pathname = "/login"; return NextResponse.redirect(url);.
export function middleware(request: NextRequest) { if (request.nextUrl.pathname === "/status") { return NextResponse.rewrite(new URL("/api/health", request.url)); }}Header
Passa request.headers per cambiare ciò che ricevono gli handler a valle e headers per aggiungere header alla risposta:
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;}Gli header di risposta del middleware vengono aggiunti alla risposta finale, a meno che il route handler non imposti lo stesso header.
Cookie
request.cookies legge l’header Cookie; response.cookies scrive Set-Cookie.
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;}I cookie impostati su una risposta next() o rewrite() sono visibili anche al route handler nella stessa richiesta.
Attività in background
Il secondo argomento dispone di waitUntil(), che mantiene attivi i runtime serverless dopo l’invio della risposta:
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 }));}Note
- Le pagine renderizzate lato client vengono renderizzate nel browser, quindi una riscrittura verso un’altra pagina cambia il file servito, non la route che l’app della console renderizza. Le riscritture verso route handler, file statici e origini esterne si comportano come previsto.
- Il middleware può leggere il corpo della richiesta; i route handler continuano a riceverlo. Il corpo delle richieste escluse da un matcher non viene mai toccato.
request.urlha sempre l’origine del server: le destinazioni delle richieste come//other.host/xvengono normalizzate a una sola barra iniziale.- L’adattatore statico non può eseguire middleware.