Get a quote

API Gateway Design Patterns for MENA SaaS Platforms: Auth, Rate Limiting, and Routing at Scale

An API gateway is the first thing every request touches. Getting it right means centralized auth, per-tenant rate limiting, and clean routing. Getting it wrong means inconsistent security, invisible failures, and technical debt at the boundary of your entire system.

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

هذا الدليل يغطي أنماط بوابة API التي نبنيها لمنتجات SaaS في لبنان ومنطقة MENA، من المصادقة وتحديد المعدل إلى التوجيه والمراقبة.

لماذا تحتاج بوابة API

بوابة API تحل عدة مشاكل في مكان مركزي بدلاً من تكرار الكود في كل خدمة أو معالج:

المصادقة والتفويض: التحقق من الرمز مرة واحدة وإعادة توجيه معلومات المستخدم إلى الخدمات الداخلية.

تحديد المعدل: حماية الخلفية من إساءة الاستخدام أو الأخطاء في الكود على جانب العميل.

التوجيه والتحويل: تحويل طلبات العملاء إلى معرفات خدمة داخلية، وتسوية إصدارات API.

المراقبة: نقطة مركزية واحدة لتسجيل كل الطلبات وقياس زمن الاستجابة.

خيارات التنفيذ: مُدار مقابل مُخصص

AWS API Gateway: مُدار بالكامل، تكامل محلي مع Lambda وECS، تلقائي في توسيع النطاق. التكلفة: حوالي $3.50 لكل مليون طلب، تُصبح مكلفة عند حجم عالٍ.

Kong: بوابة API مفتوحة المصدر مع مكونات إضافية للتحقق من JWT وتحديد المعدل وتسجيل الدخول. يعمل كحاوية على ECS. أكثر تعقيداً في الإعداد لكن أقل تكلفة عند الحجم.

بوابة مخصصة في Go: ما نبنيه لمنتجات SaaS ذات متطلبات محددة. تحكم كامل، تكاليف تشغيلية منخفضة، لكنها مسؤوليتك.

لمعظم منتجات SaaS المبكرة في MENA، AWS API Gateway أو بوابة Go بسيطة هي الاختياران المنطقيان.

أنماط المصادقة

التحقق من JWT عند البوابة

نمط شائع: العميل يحمل JWT، البوابة تتحقق منه قبل إعادة التوجيه.

func (gw *Gateway) AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authHeader := r.Header.Get("Authorization")
        if authHeader == "" {
            http.Error(w, "unauthorized", http.StatusUnauthorized)
            return
        }

        tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
        claims, err := gw.jwtVerifier.Verify(tokenStr)
        if err != nil {
            http.Error(w, "invalid token", http.StatusUnauthorized)
            return
        }

        // أضف معلومات المستأجر إلى الطلب قبل إعادة التوجيه
        r = r.WithContext(context.WithValue(r.Context(), contextKeyTenant, claims.WorkspaceID))
        r.Header.Set("X-Workspace-ID", claims.WorkspaceID)
        r.Header.Set("X-Account-ID", claims.AccountID)
        r.Header.Set("X-Account-Role", claims.Role)

        next.ServeHTTP(w, r)
    })
}

الخدمات الداخلية تثق في رؤوس X-Workspace-ID وX-Account-ID. لا تُكرر منطق التحقق من JWT في كل خدمة.

مصادقة مفتاح API للعملاء B2B

كثير من منتجات SaaS تدعم مفاتيح API لعمليات التكامل B2B:

func (gw *Gateway) APIKeyMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        apiKey := r.Header.Get("X-API-Key")
        if apiKey == "" {
            // انتقل إلى مصادقة JWT
            gw.AuthMiddleware(next).ServeHTTP(w, r)
            return
        }

        // تحقق من مفتاح API في قاعدة البيانات
        // استخدم ذاكرة تخزين مؤقت بمهلة قصيرة لتجنب ضربة قاعدة البيانات على كل طلب
        workspace, err := gw.apiKeyCache.Lookup(r.Context(), apiKey)
        if err != nil {
            http.Error(w, "invalid api key", http.StatusUnauthorized)
            return
        }

        r.Header.Set("X-Workspace-ID", workspace.ID)
        r.Header.Set("X-Account-ID", workspace.ServiceAccountID)
        next.ServeHTTP(w, r)
    })
}

التخزين المؤقت لمفاتيح API حرج. بدونه، كل طلب API يضرب قاعدة البيانات. مهلة 5 دقائق عادةً كافية.

تحديد المعدل لكل مستأجر

تحديد المعدل بناءً على IP وحيد مشكلة في SaaS: كل عملاء مؤسسة يستخدمون نفس IP المؤسسي. تحديد المعدل يجب أن يكون بناءً على workspace_id.

func (gw *Gateway) RateLimitMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        workspaceID := r.Header.Get("X-Workspace-ID")
        if workspaceID == "" {
            next.ServeHTTP(w, r)
            return
        }

        // خطة كل مستأجر تحدد حد المعدل
        plan := gw.planCache.Get(workspaceID)
        limiter := gw.getLimiter(workspaceID, plan.RequestsPerMinute)

        if !limiter.Allow() {
            w.Header().Set("X-RateLimit-Limit", strconv.Itoa(plan.RequestsPerMinute))
            w.Header().Set("Retry-After", "60")
            http.Error(w, "rate limit exceeded", http.StatusTooManyRequests)
            return
        }

        // أضف رؤوس المعدل المتبقي
        remaining := limiter.Remaining()
        w.Header().Set("X-RateLimit-Remaining", strconv.Itoa(remaining))

        next.ServeHTTP(w, r)
    })
}

تحديد المعدل الموزع يحتاج إلى Redis حتى يعمل عبر عدة نسخ من البوابة:

// مثال: Redis sliding window rate limiter
const script = `
    local key = KEYS[1]
    local limit = tonumber(ARGV[1])
    local window = tonumber(ARGV[2])
    local now = tonumber(ARGV[3])

    redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
    local count = redis.call('ZCARD', key)

    if count < limit then
        redis.call('ZADD', key, now, now)
        redis.call('EXPIRE', key, window)
        return 1
    end
    return 0
`

توجيه المستأجرين

في SaaS متعدد المستأجرين، يمكنك أحياناً توجيه مستأجرين مختلفين إلى نسخ مختلفة من الخدمة. هذا مفيد لـ:

  • توجيه العملاء الكبار إلى نسخ مخصصة
  • A/B testing لمجموعات مستأجرين مختلفة
  • الترحيل التدريجي إلى بنية تحتية جديدة
func (gw *Gateway) TenantRoutingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        workspaceID := r.Header.Get("X-Workspace-ID")

        // تحقق من هل لهذا المستأجر توجيه مخصص
        if backendURL, ok := gw.tenantRoutes[workspaceID]; ok {
            gw.proxyTo(w, r, backendURL)
            return
        }

        next.ServeHTTP(w, r)
    })
}

إصدارات API

إصدارات API ضرورية لـ SaaS B2B. العملاء لا يمكنهم دائماً الترقية فوراً:

func (gw *Gateway) VersionMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // دعم الإصدارات في المسار: /v1/..., /v2/...
        path := r.URL.Path
        version := "v1" // افتراضي

        if strings.HasPrefix(path, "/v2/") {
            version = "v2"
            r.URL.Path = strings.TrimPrefix(path, "/v2")
        } else if strings.HasPrefix(path, "/v1/") {
            r.URL.Path = strings.TrimPrefix(path, "/v1")
        }

        r.Header.Set("X-API-Version", version)
        next.ServeHTTP(w, r)
    })
}

المراقبة والمقاييس

البوابة هي النقطة المثالية لجمع المقاييس:

func (gw *Gateway) MetricsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        rec := &statusRecorder{ResponseWriter: w, status: 200}

        next.ServeHTTP(rec, r)

        duration := time.Since(start)
        workspaceID := r.Header.Get("X-Workspace-ID")

        // سجل: المسار، الرمز، المدة، المستأجر
        gw.metrics.RecordRequest(
            r.URL.Path,
            rec.status,
            duration,
            workspaceID,
        )
    })
}

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

ترتيب الوسيطات

ترتيب الوسيطات في البوابة مهم. الترتيب الصحيح:

  1. المراقبة: يجب أن يلتقط كل الطلبات بما فيها تلك المرفوضة
  2. المصادقة: رفض الطلبات غير المصرح بها مبكراً
  3. تحديد المعدل: تطبيق لكل مستأجر بعد التعرف على المستأجر
  4. التوجيه: توجيه المستأجرين المخصصين
  5. الإصدارات: تسوية المسار قبل إعادة التوجيه
  6. الوكيل: إعادة التوجيه إلى الخدمة الداخلية

الدروس الرئيسية

ركز المصادقة في البوابة وارسل معلومات المستأجر كرؤوس للخدمات الداخلية. طبق تحديد المعدل بناءً على workspace_id وليس IP. استخدم Redis لتحديد المعدل الموزع. خزن مؤقتاً مفاتيح API وخطط المستأجرين لتجنب ضربات قاعدة البيانات. جمع المقاييس من البوابة لرؤية شاملة لكل الطلبات.


هل تحتاج مساعدة في تصميم بوابة API لمنتجك؟

Voxire تُصمم وتبني بنيات API لمنتجات SaaS في لبنان ومنطقة MENA. إذا كنت تواجه مشاكل في الأمان أو الأداء أو التوجيه عند حدود نظامك، تواصل معنا.

https://voxire.com/get-a-quote/

Free PDF Download

Enjoying this article?

Enter your email and get a clean, formatted PDF of this article - free, no spam.

Free. No spam. Unsubscribe any time.

Back to blog
Chat on WhatsApp