עברית

גלו את המידלוור (middleware) של Next.js, כלי רב-עוצמה ליירוט ושינוי בקשות נכנסות. למדו כיצד ליישם אימות, הרשאות, הפניות ובדיקות A/B עם דוגמאות מעשיות.

Middleware ב-Next.js: שליטה ביירוט בקשות ליישומים דינמיים

המידלוור (middleware) של Next.js מספק דרך גמישה ועוצמתית ליירט ולשנות בקשות נכנסות לפני שהן מגיעות לראוטים (routes) שלכם. יכולת זו מאפשרת לכם ליישם מגוון רחב של תכונות, החל מאימות והרשאות ועד להפניות ובדיקות A/B, כל זאת תוך אופטימיזציה של הביצועים. מדריך מקיף זה ילווה אתכם דרך מושגי הליבה של מידלוור ב-Next.js וידגים כיצד למנף אותו ביעילות.

מהו Middleware ב-Next.js?

מידלוור ב-Next.js הוא פונקציה שרצה לפני השלמת בקשה. הוא מאפשר לכם:

פונקציות מידלוור מוגדרות בקובץ middleware.ts (או middleware.js) בשורש הפרויקט שלכם. הן מופעלות עבור כל ראוט ביישום שלכם, או עבור ראוטים ספציפיים על בסיס מתאמים (matchers) הניתנים להגדרה.

מושגי מפתח ויתרונות

אובייקט הבקשה (Request)

אובייקט ה-request מספק גישה למידע על הבקשה הנכנסת, כולל:

אובייקט התגובה (Response)

פונקציות מידלוור מחזירות אובייקט Response כדי לשלוט בתוצאת הבקשה. ניתן להשתמש בתגובות הבאות:

מתאמים (Matchers)

מתאמים מאפשרים לכם לציין על אילו ראוטים המידלוור שלכם צריך לחול. ניתן להגדיר מתאמים באמצעות ביטויים רגולריים או תבניות נתיב. זה מבטיח שהמידלוור שלכם ירוץ רק בעת הצורך, מה שמשפר את הביצועים ומפחית את התקורה.

סביבת הרצה Edge (Edge Runtime)

מידלוור ב-Next.js רץ על ה-Edge Runtime, שהיא סביבת הרצה קלת משקל של JavaScript שניתן לפרוס קרוב למשתמשים שלכם. קרבה זו ממזערת את ההשהיה (latency) ומשפרת את הביצועים הכוללים של היישום שלכם, במיוחד עבור משתמשים הפרוסים גלובלית. ה-Edge Runtime זמין ב-Edge Network של Vercel ובפלטפורמות תואמות אחרות. ל-Edge Runtime יש כמה מגבלות, במיוחד בשימוש ב-API של Node.js.

דוגמאות מעשיות: יישום תכונות מידלוור

1. אימות (Authentication)

ניתן להשתמש במידלוור אימות כדי להגן על ראוטים הדורשים מהמשתמשים להיות מחוברים. הנה דוגמה לאופן יישום אימות באמצעות קובצי cookie:


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const token = request.cookies.get('auth_token');

 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/dashboard/:path*'],
}

מידלוור זה בודק את קיומו של קובץ cookie בשם auth_token. אם ה-cookie לא נמצא, המשתמש מופנה לדף /login. ה-config.matcher מציין שמידלוור זה צריך לרוץ רק עבור ראוטים תחת הנתיב /dashboard.

פרספקטיבה גלובלית: התאימו את לוגיקת האימות לתמיכה בשיטות אימות שונות (למשל, OAuth, JWT) ושילוב עם ספקי זהויות שונים (למשל, Google, Facebook, Azure AD) כדי לתת מענה למשתמשים מאזורים מגוונים.

2. הרשאות (Authorization)

ניתן להשתמש במידלוור הרשאות כדי לשלוט בגישה למשאבים על בסיס תפקידי משתמשים או הרשאות. לדוגמה, ייתכן שיש לכם לוח מחוונים למנהל מערכת שרק משתמשים ספציפיים יכולים לגשת אליו.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 const token = request.cookies.get('auth_token');

 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 // דוגמה: שליפת תפקידי משתמש מ-API (החליפו בלוגיקה האמיתית שלכם)
 const userResponse = await fetch('https://api.example.com/userinfo', {
 headers: {
 Authorization: `Bearer ${token}`,
 },
 });
 const userData = await userResponse.json();

 if (userData.role !== 'admin') {
 return NextResponse.redirect(new URL('/unauthorized', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/admin/:path*'],
}

מידלוור זה שולף את תפקיד המשתמש ובודק אם יש לו תפקיד admin. אם לא, הוא מופנה לדף /unauthorized. דוגמה זו משתמשת בנקודת קצה (endpoint) של API לדוגמה. החליפו את `https://api.example.com/userinfo` בנקודת הקצה של שרת האימות האמיתי שלכם.

פרספקטיבה גלובלית: היו מודעים לתקנות פרטיות נתונים (למשל, GDPR, CCPA) בעת טיפול בנתוני משתמשים. ישמו אמצעי אבטחה מתאימים כדי להגן על מידע רגיש ולהבטיח תאימות לחוקים המקומיים.

3. הפניה (Redirection)

ניתן להשתמש במידלוור הפניה כדי להפנות משתמשים על בסיס מיקומם, שפתם או קריטריונים אחרים. לדוגמה, תוכלו להפנות משתמשים לגרסה מותאמת מקומית של האתר שלכם על בסיס כתובת ה-IP שלהם.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const country = request.geo?.country || 'US'; // ברירת מחדל לארה"ב אם זיהוי המיקום נכשל

 if (country === 'DE') {
 return NextResponse.redirect(new URL('/de', request.url))
 }

 if (country === 'FR') {
 return NextResponse.redirect(new URL('/fr', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/'],
}

מידלוור זה בודק את ארץ המשתמש על בסיס כתובת ה-IP שלו ומפנה אותו לגרסה המקומית המתאימה של האתר (/de לגרמניה, /fr לצרפת). אם זיהוי המיקום נכשל, הוא חוזר כברירת מחדל לגרסה האמריקאית. שימו לב שהדבר תלוי בזמינות המאפיין geo (למשל, בעת פריסה ב-Vercel).

פרספקטיבה גלובלית: ודאו שהאתר שלכם תומך במספר שפות ומטבעות. ספקו למשתמשים את האפשרות לבחור ידנית את השפה או האזור המועדפים עליהם. השתמשו בפורמטים מתאימים של תאריך ושעה עבור כל אזור (locale).

4. בדיקות A/B

ניתן להשתמש במידלוור ליישום בדיקות A/B על ידי הקצאה אקראית של משתמשים לגרסאות שונות של דף ומעקב אחר התנהגותם. הנה דוגמה פשוטה:


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

function getRandomVariant() {
 return Math.random() < 0.5 ? 'A' : 'B';
}

export function middleware(request: NextRequest) {
 let variant = request.cookies.get('variant')?.value;

 if (!variant) {
 variant = getRandomVariant();
 const response = NextResponse.next();
 response.cookies.set('variant', variant);
 return response;
 }

 if (variant === 'B') {
 return NextResponse.rewrite(new URL('/variant-b', request.url));
 }

 return NextResponse.next();
}

export const config = {
 matcher: ['/'],
}

מידלוור זה מקצה משתמשים לגרסה 'A' או 'B'. אם למשתמש אין עדיין קובץ cookie בשם variant, אחד מוקצה ונקבע באופן אקראי. משתמשים שהוקצו לגרסה 'B' משוכתבים לדף /variant-b. לאחר מכן, תצטרכו לעקוב אחר ביצועי כל גרסה כדי לקבוע איזו מהן יעילה יותר.

פרספקטיבה גלובלית: קחו בחשבון הבדלים תרבותיים בעת תכנון בדיקות A/B. מה שעובד היטב באזור אחד עשוי לא להדהד בקרב משתמשים באזור אחר. ודאו שפלטפורמת בדיקות ה-A/B שלכם תואמת לתקנות הפרטיות באזורים שונים.

5. דגלי פיצ'רים (Feature Flags)

דגלי פיצ'רים מאפשרים לכם להפעיל או להשבית תכונות ביישום שלכם מבלי לפרוס קוד חדש. ניתן להשתמש במידלוור כדי לקבוע אם למשתמש צריכה להיות גישה לתכונה ספציפית על בסיס מזהה המשתמש, מיקומו או קריטריונים אחרים.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 // דוגמה: שליפת דגלי פיצ'רים מ-API
 const featureFlagsResponse = await fetch('https://api.example.com/featureflags', {
 headers: {
 'X-User-Id': 'user123',
 },
 });
 const featureFlags = await featureFlagsResponse.json();

 if (featureFlags.new_feature_enabled) {
 // הפעל את הפיצ'ר החדש
 return NextResponse.next();
 } else {
 // השבת את הפיצ'ר החדש (למשל, הפנה לדף חלופי)
 return NextResponse.redirect(new URL('/alternative-page', request.url));
 }
}

export const config = {
 matcher: ['/new-feature'],
}

מידלוור זה שולף דגלי פיצ'רים מ-API ובודק אם הדגל new_feature_enabled מוגדר. אם כן, המשתמש יכול לגשת לדף /new-feature. אחרת, הוא מופנה לדף /alternative-page.

פרספקטיבה גלובלית: השתמשו בדגלי פיצ'רים כדי להשיק תכונות חדשות בהדרגה למשתמשים באזורים שונים. זה מאפשר לכם לנטר ביצועים ולטפל בכל בעיה לפני שחרור התכונה לקהל רחב יותר. כמו כן, ודאו שמערכת דגלי הפיצ'רים שלכם נדרשת (scales) גלובלית ומספקת תוצאות עקביות ללא קשר למיקום המשתמש. שקלו אילוצים רגולטוריים אזוריים להשקת פיצ'רים.

טכניקות מתקדמות

שירשור מידלוורים (Chaining Middleware)

ניתן לשרשר מספר פונקציות מידלוור יחד כדי לבצע סדרה של פעולות על בקשה. זה יכול להיות שימושי לפירוק לוגיקה מורכבת למודולים קטנים וניתנים יותר לניהול.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const response = NextResponse.next();

 // פונקציית המידלוור הראשונה
 const token = request.cookies.get('auth_token');
 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 // פונקציית המידלוור השנייה
 response.headers.set('x-middleware-custom', 'value');

 return response;
}

export const config = {
 matcher: ['/dashboard/:path*'],
}

דוגמה זו מציגה שני מידלוורים באחד. הראשון מבצע אימות והשני מגדיר כותרת מותאמת אישית.

שימוש במשתני סביבה

אחסנו מידע רגיש, כגון מפתחות API ופרטי התחברות למסד נתונים, במשתני סביבה במקום לקודד אותם ישירות בפונקציות המידלוור שלכם. זה משפר את האבטחה ומקל על ניהול תצורת היישום שלכם.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const API_KEY = process.env.API_KEY;

export async function middleware(request: NextRequest) {
 const response = await fetch('https://api.example.com/data', {
 headers: {
 'X-API-Key': API_KEY,
 },
 });

 // ...
}

export const config = {
 matcher: ['/data'],
}

בדוגמה זו, ה-API_KEY נשלף ממשתנה סביבה.

טיפול בשגיאות

ישמו טיפול חזק בשגיאות בפונקציות המידלוור שלכם כדי למנוע משגיאות בלתי צפויות לקרוס את היישום. השתמשו בבלוקי try...catch כדי לתפוס חריגות ולרשום שגיאות כראוי.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 try {
 const response = await fetch('https://api.example.com/data');
 // ...
 } catch (error) {
 console.error('שגיאה בשליפת נתונים:', error);
 return NextResponse.error(); // או הפניה לדף שגיאה
 }
}

export const config = {
 matcher: ['/data'],
}

שיטות עבודה מומלצות (Best Practices)

פתרון בעיות נפוצות

סיכום

המידלוור של Next.js הוא כלי רב-עוצמה לבניית יישומי אינטרנט דינמיים ומותאמים אישית. על ידי שליטה ביירוט בקשות, תוכלו ליישם מגוון רחב של תכונות, החל מאימות והרשאות ועד להפניות ובדיקות A/B. על ידי ביצוע השיטות המומלצות המתוארות במדריך זה, תוכלו למנף את המידלוור של Next.js ליצירת יישומים בעלי ביצועים גבוהים, מאובטחים וניתנים להרחבה (scalable) העונים על צרכי בסיס המשתמשים הגלובלי שלכם. אמצו את העוצמה של מידלוור כדי לפתוח אפשרויות חדשות בפרויקטי Next.js שלכם ולספק חוויות משתמש יוצאות דופן.