⁦middleware⁩

⁦middleware.ts⁩: הפניות, שכתובים, כותרות וקובצי Cookie לפני כל בקשה, עם config.matcher.

⁦middleware⁩ מריץ קוד לפני השלמת הבקשה. הוא יכול להפנות, לשכתב, להגדיר כותרות וקובצי Cookie, או להחזיר תגובה בעצמו.

מוסכמות

יש ליצור את middleware.ts בשורש הפרויקט (או בספרייה src/), לצד app/. יש לייצא פונקציית middleware (או ייצוא ברירת מחדל) ובאופן אופציונלי גם config.

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*"],};

⁦middleware⁩ רץ לפני מטפלי נתיבים, קבצים סטטיים ודפי מסוף, בעת הפעלת text dev, text start וכל מתאם פריסה לשרת. הוא משתמש ב-Request וב-Response של Web, כך שאותו קובץ רץ ב-Node, ב-Bun, ב-⁦Vercel Edge⁩ וב-⁦Cloudflare Workers⁩.

התאמת נתיבים

ללא config.matcher, middleware רץ עבור כל בקשה. כללי ההתאמה משתמשים באותו תחביר כמו Next.js:

כלל התאמההתאמות
/about/about וגם /about/
/blog/:slug/blog/hello
/docs/:path*/docs, /docs/a/b
/a/:rest+/a/b, לא /a
/user/:id(\\d+)/user/42, לא /user/ada
/((?!api/).*)הכול מלבד /api/*

יש להוסיף תנאים באמצעות has ו-missing (כותרת, קובץ Cookie, שאילתה או מארח):

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

כללי ההתאמה חלים על שם הנתיב ללא basePath. ב-text dev, בקשות המודולים של Vite עצמו (/@vite/*, קובצי מקור) לעולם אינן מפעילות middleware.

כל מארח מחשב שם נתיב קנוני אחד לפני הפעלת middleware, ומשתמש בו להתאמה, ל-request.nextUrl ולחיפוש הנתיב, המטפל והקובץ. רצפי קידוד באחוזים מפוענחים והנתיב מקודד מחדש באופן שבו new URL() כותב אותו, מקטעי . ו-.. נפתרים, ולוכסנים חוזרים וסופיים מוסרים. /ad%6din//users/ מגיע ל-middleware בתור /admin/users, ואילו /café נשאר /caf%C3%A9. כלל התאמה שנכתב עם לוכסן סופי, כגון /admin/, מתאים ל-/admin. בקשות שהנתיב שלהן מכיל לוכסן או לוכסן הפוך מקודד (%2F, %5C), תו בקרה או קידוד אחוזים לא תקין מקבלות 400. נתיבים רגישים לרישיות: /Admin אינו מתאים ל-/admin, וקבצים סטטיים מוגשים רק בשם המדויק שלהם.

יש להתאים את כתובות ה-URL שבהן הקובץ מוגש, ולא רק את שם הקובץ שלו. ⁦Cloudflare Pages⁩ מגיש את report.html גם ב-/report, ולכן כלל התאמה עבור /report.html אינו מגן על /report. יש לציין את שניהם, matcher: ["/report", "/report.html"], או לשמור קבצים מוגנים בספרייה ולהתאים אותה, למשל /reports/:path* עבור קבצים בספרייה public/reports/.

NextResponse

⁦API⁩השפעה
NextResponse.next()המשך לנתיב
NextResponse.redirect(url, status?)307 כברירת מחדל; url חייב להיות מוחלט
NextResponse.rewrite(url)הגשת נתיב אחר; מקורות חיצוניים מועברים דרך שרת מתווך
NextResponse.json(body, init?)החזרת תגובה עם JSON
new Response(...)החזרת תגובה עם כל תוכן

כדי להמשיך ללא שינוי, אין להחזיר דבר. next() ו-rewrite() מקבלות { status } לשינוי קוד המצב של התגובה הסופית. רק תגובות שנוצרו באמצעות NextResponse.next() / NextResponse.rewrite() מכוונות את הניתוב: כל Response אחר, כולל כזה שהוחזר מ-fetch(), נשלח ללקוח כפי שהוא, לאחר הסרת כותרות ה-x-middleware-* שלו.

request.nextUrl אינו כולל את basePath בתוך pathname, ואילו href ו-toString() שלו כוללים אותו. כדי להישאר בתוך האפליקציה, יש לשכפל אותו: 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));  }}

כותרות

יש להעביר את request.headers כדי לשנות את מה שהמטפלים בהמשך מקבלים, ואת headers כדי להוסיף כותרות תגובה:

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;}

כותרות תגובה מ-middleware מתווספות לתגובה הסופית, אלא אם מטפל הנתיב מגדיר את אותה כותרת.

קובצי Cookie

request.cookies קורא את הכותרת Cookie; response.cookies כותב את 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;}

קובצי Cookie שמוגדרים בתגובת next() או rewrite() זמינים גם למטפל הנתיב באותה בקשה.

עבודה ברקע

הארגומנט השני כולל את waitUntil(), שמשאיר סביבות ריצה ללא שרת פעילות לאחר שליחת התגובה:

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 }));}

הערות

  • דפים עם רינדור בצד הלקוח מרונדרים בדפדפן, ולכן שכתוב לדף אחר משנה את הקובץ המוגש, ולא את הנתיב שאפליקציית המסוף מרנדרת. שכתובים למטפלי נתיבים, לקבצים סטטיים ולמקורות חיצוניים פועלים כמצופה.
  • middleware יכול לקרוא את גוף הבקשה; מטפלי נתיבים עדיין מקבלים אותו. גוף הבקשות שכלל התאמה מחריג נשאר ללא שינוי.
  • request.url מכיל תמיד את מקור השרת: יעדי בקשה כגון //other.host/x מנורמלים ללוכסן מוביל יחיד.
  • המתאם הסטטי אינו יכול להריץ middleware.
עריכת הדף ב-GitHub