تخطَّ إلى المحتوى

الوسيط يعمل مع كل طلب، بما فيها تلك التي نسيتها

الوسيط هو الشيفرة الوحيدة في تطبيق Next.js التي لا انسحاب منها. ومُطابِق واسع قليلًا يضعه أمام كل صورة وخط وتحميل مسبق، والفاتورة تصل على هيئة زمن استجابة.

5 دقيقة قراءة

كل شيفرة أخرى في تطبيق Next.js تعمل لأن شيئًا وصل إليها. أما الوسيط فيعمل لأن طلبًا حدث. لا مسار ينسحب منه، ولا حدّ مكوّن يحتويه، ولا ذاكرة مؤقّتة أمامه.

هذه الخاصّية نفسها هي ما يجعله مفيدًا للمصادقة وتوجيه اللغات، وهي أيضًا سبب كون المُطابِق المُهمَل من أغلى الأخطاء المتاحة في الإطار.

ما الذي يطابقه المُطابِق الافتراضي فعلًا

كثير من الشيفرات تحمل هذا، منقولًا عن درس:

export const config = {
  matcher: '/:path*',
};

هذا كل طلب. لا كل صفحة - كل طلب. ورقة الأنماط. وكل ملف خط. وكل نسخة صورة يولّدها next/image. وكل حمولة .rsc يحمّلها الموجّه مسبقًا حين يدخل رابط إلى الشاشة. في صفحة بعشرين رابطًا واثنتي عشرة صورة، قد تعني نقلة واحدة ستّين استدعاءً للوسيط، ثمانية وخمسون منها لم يكن لديها ما تقرّره.

المُطابِق الذي تريده في الغالب يستثني الآليّات:

export const config = {
  matcher: [
    /*
      كل شيء عدا: أصول الإطار نفسه، ومحسّن الصور، والملفات ذات الامتداد،
      ومسارات البيانات الوصفية. كُتب كاستطلاع سلبي لأن المُطابِق يُترجَم إلى
      تعبير نمطي وقت البناء ويُقيَّم قبل أن تعمل أي شيفرة لك.
    */
    '/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml|.*\\..*).*)',
  ],
};

قِس قبل وبعد. في أحد التدقيقات، هذا التغيير وحده خفض استدعاءات الوسيط في الصفحة الرئيسية من 71 إلى 4.

التحميل المسبق هو ما يفوت الجميع

يحمّل <Link> مسبقًا افتراضيًا حين يدخل نطاق الرؤية. وكل تحميل مسبق طلبٌ حقيقي لحمولة RSC الخاصة بالمسار، وكلٌّ منها يمرّ عبر الوسيط.

فتذييل بثلاثين رابطًا يعني ثلاثين تشغيلًا للوسيط لحظة نزول الزائر إلى الأسفل، لصفحات قد لا يفتحها أبدًا. وإن كان وسيطك يستعلم من قاعدة بيانات أو ينادي خدمة مصادقة، فقد جعلتَ التمرير مكلفًا للتوّ.

القاعدة بسيطة وتستحقّ الإلزام: الوسيط لا يقوم بعمليات إدخال/إخراج. اقرأ الكعكة، وتحقّق من التوقيع محليًا، وأعد التوجيه. وكل ما يحتاج قاعدة البيانات ينتمي إلى الصفحة أو الـ action، حيث يعمل مرّة واحدة لطلبٍ قدّمه الزائر فعلًا.

import { NextResponse, type NextRequest } from 'next/server';
import { jwtVerify } from 'jose';
 
const secret = new TextEncoder().encode(process.env.SESSION_SECRET);
 
export async function middleware(request: NextRequest) {
  const token = request.cookies.get('session')?.value;
  if (!token) return NextResponse.redirect(new URL('/login', request.url));
 
  try {
    // فحص توقيع محلّي - بلا شبكة. الادّعاءات تكفي لتقرير السماح للطلب
    // بالمرور؛ أما هل يجوز لهذا المستخدم رؤية هذا المورد تحديدًا فسؤال
    // الصفحة، وقاعدة البيانات أمامه.
    await jwtVerify(token, secret);
    return NextResponse.next();
  } catch {
    return NextResponse.redirect(new URL('/login', request.url));
  }
}

التفويض في الوسيط بوّابة لا ضمانة

الوسيط موضع جيّد لإرسال زائر مجهول إلى صفحة الدخول. وهو الموضع الخطأ - والوحيد

  • لتقرير ما إذا كان لهذا المستخدم أن يقرأ هذا السجلّ.

لسببين. فهو لا يرى معاملات المسار بأي صورة منظّمة، فيصير سؤال «هل لهذا المستخدم قراءة الفاتورة 4182» تمرينًا في تحليل النصوص. ولأن الـ Server Actions ومعالِجات المسارات يمكن استدعاؤها مباشرة، ففحصٌ يعيش في الوسيط وحده هو فحصٌ لا تملكه نقطة النهاية - المصيدة نفسها في الأخطاء الثلاثة المتكرّرة مع Server Actions.

البوّابة في الوسيط، والتفويض عند الوصول إلى البيانات. الاثنان معًا لا أحدهما.

فرق بيئة التشغيل الذي لا يذكره أحد حتى ينكسر

يعمل الوسيط في بيئة مقيّدة. لا fs، ولا net، ولا وحدات Node أصلية، وحدّ لحجم الحزمة. واستيراد الـ ORM إليها لا يحذّرك - بل يفشل عند البناء، أو أسوأ، عند النشر.

ويعني هذا أيضًا أن حزمة الوسيط منفصلة عن حزم مساراتك. فجرّ مكتبة تواريخ كبيرة من أجل استدعاء تنسيق واحد يضع تلك المكتبة في مسار كل طلب، لا في صفحة واحدة.

ما نفحصه

  1. ما الذي يطابقه المُطابِق فعلًا؟ عُدّ الاستدعاءات في تحميل صفحة حقيقي، ولا تقرأ التعبير النمطي وتفترض.
  2. هل يقوم بعمليات إدخال/إخراج؟ إن نعم، فهذه الكلفة يدفعها كل طلب، بما فيه التحميل المسبق.
  3. هل يُتَّخذ أي قرار تفويض هنا وحده؟
  4. ما الموجود في الحزمة، وهل يلزم وجوده؟

وسيطٌ يقرأ كعكة ويعيد التوجيه يكلّف ميكروثوانٍ ويستحقّ وجوده. ووسيطٌ ينادي خدمة يكلّف رحلة ذهاب وإياب في كل طلب أصل - وتكتشفه الفرق عادةً على هيئة «الموقع يبدو بطيئًا» بلا مسارٍ تُلقي عليه اللوم.

النصف الآخر من الصورة هو ما يحلّ به حين تغادر المنصّة، حيث يكفّ الوسيط عن كونه مئة موقع على الحافة ويصير خادمك الواحد: استضافة Next.js ذاتيًا وما الذي يتوقّف عن العمل.

العودة إلى كل المقالات