API Rate Limiting: Patterns and Best Practices

Master API rate limiting with token bucket, sliding window, and fixed counter patterns. Protect your backend with NestJS throttler, Redis, and real-world configuration examples.

E
ECOSIRE Research and Development Team
|19 مارچ، 202612 منٹ پڑھیں2.6k الفاظ|

API شرح کی حد: پیٹرن اور بہترین طرز عمل

ہر عوامی API کا اختتامی نقطہ ایک ہدف ہوتا ہے — بوٹس، سکریپرز، اور برے اداکار آپ کے سرور پر اس وقت ہتھوڑا لگائیں گے جب آپ لائیو ہوں گے۔ شرح کو محدود کیے بغیر، ایک غلط برتاؤ کرنے والا کلائنٹ آپ کے ڈیٹا بیس کنکشن کو ختم کر سکتا ہے، آپ کے کلاؤڈ بل کو بڑھا سکتا ہے، اور ہر جائز صارف کے لیے سروس ختم کر سکتا ہے۔ شرح محدود کرنا اختیاری نہیں ہے۔ یہ کسی بھی پروڈکشن API کے لیے دفاع کی پہلی لائن ہے۔

یہ گائیڈ شرح کو محدود کرنے والے چار بڑے الگورتھم، ان کے تجارتی معاہدوں، اور Redis کے ساتھ NestJS میں انہیں صحیح طریقے سے لاگو کرنے کے طریقہ کے بارے میں بتاتا ہے۔ آپ عام منظرناموں کے لیے کاپی پیسٹ کنفیگریشنز اور فی اختتامی نقطہ صحیح حکمت عملی کے انتخاب کے لیے ایک ذہنی ماڈل کے ساتھ چھوڑیں گے۔

اہم ٹیک ویز

  • ٹوکن بالٹی کنٹرولڈ برسٹنگ کی اجازت دیتی ہے جبکہ فکسڈ ونڈو کاؤنٹر لاگو کرنے کے لئے سب سے سستے ہیں
  • سلائیڈنگ ونڈو لاگ سب سے زیادہ درست لیکن میموری سے بھرپور ہے۔ سلائیڈنگ ونڈو کاؤنٹر بہترین توازن ہے۔
  • جب آپ ایک سے زیادہ API ریپلیکس چلاتے ہیں تو Redis واحد درست بیکنگ اسٹور ہے۔
  • NestJS @nestjs/throttler حسب ضرورت اسٹوریج اڈاپٹر کو سپورٹ کرتا ہے - ایک کنفیگریشن تبدیلی کے ساتھ Redis میں تبدیل کریں
  • ہمیشہ Retry-After، X-RateLimit-Limit، X-RateLimit-Remaining، اور X-RateLimit-Reset ہیڈر واپس کریں
  • اختتامی نقطہ کی حساسیت کے لحاظ سے حدود میں فرق کریں: توثیق (5/منٹ) بمقابلہ پڑھیں APIs (1000/منٹ)
  • گمنام ٹریفک کے لیے IP پر مبنی حدود اور تصدیق شدہ درخواستوں کے لیے صارف پر مبنی حدود استعمال کریں
  • کبھی بھی خاموشی سے درخواستیں نہ چھوڑیں - ہمیشہ ایک مددگار پیغام کے ساتھ 429 Too Many Requests واپس کریں۔

چار بنیادی الگورتھم

فکسڈ ونڈو کاؤنٹر

سب سے آسان طریقہ: ایک مقررہ وقت کی ونڈو میں درخواستوں کو شمار کریں، باؤنڈری پر ری سیٹ کریں۔

// Fixed window: 100 requests per minute
// Window resets at :00, :01, :02 ...
const windowKey = `ratelimit:${userId}:${Math.floor(Date.now() / 60000)}`;
const count = await redis.incr(windowKey);
await redis.expire(windowKey, 60);

if (count > 100) {
  throw new TooManyRequestsException();
}

کمزوری: حدود کا استحصال۔ ایک کلائنٹ 100 درخواستیں 12:00:59 پر اور دوسری 100 12:01:00 پر بھیج سکتا ہے - مؤثر طریقے سے دو سیکنڈ میں 200 درخواستیں۔ زیادہ تر APIs کے لیے یہ قابل قبول ہے۔ توثیق کے اختتامی نکات کے لیے، ایسا نہیں ہے۔

سلائیڈنگ ونڈو لاگ

ہر درخواست کا ٹائم اسٹیمپ اسٹور کریں۔ ہر درخواست پر، آخری ونڈو میں ٹائم اسٹیمپ شمار کریں۔

const now = Date.now();
const windowStart = now - 60_000; // 60 seconds ago

// Remove old entries, add current
await redis.zremrangebyscore(key, 0, windowStart);
await redis.zadd(key, now, now.toString());
const count = await redis.zcard(key);
await redis.expire(key, 60);

if (count > 100) {
  throw new TooManyRequestsException();
}

تجارتی بند: بالکل درست لیکن O(n) اندراجات فی صارف ذخیرہ کرتا ہے جہاں n درخواست کی گنتی ہے۔ 10,000 صارفین میں 1,000 RPS پر، آپ کی Redis میموری تیزی سے بڑھ جاتی ہے۔ کم والیوم، ہائی سیکیورٹی اینڈ پوائنٹس جیسے پاس ورڈ ری سیٹ کے لیے استعمال کریں۔

سلائیڈنگ ونڈو کاؤنٹر

دو فکسڈ ونڈوز کا استعمال کرتے ہوئے تخمینی سلائیڈنگ ونڈو — کوئی میموری دھماکہ نہیں ہے۔

const now = Date.now();
const currentWindow = Math.floor(now / 60000);
const previousWindow = currentWindow - 1;
const windowProgress = (now % 60000) / 60000; // 0.0 to 1.0

const [current, previous] = await redis.mget(
  `rl:${userId}:${currentWindow}`,
  `rl:${userId}:${previousWindow}`
);

const estimated =
  (parseInt(previous ?? '0') * (1 - windowProgress)) +
  parseInt(current ?? '0');

if (estimated >= 100) {
  throw new TooManyRequestsException();
}

await redis.incr(`rl:${userId}:${currentWindow}`);
await redis.expire(`rl:${userId}:${currentWindow}`, 120);

یہ الگورتھم ہے جو Cloudflare استعمال کرتا ہے۔ یہ کم سے کم اوور ہیڈ کے ساتھ باؤنڈری اسپائک کو ہموار کرتا ہے — فی صارف فی ونڈو دو Redis کیز۔

ٹوکن بالٹی

طویل مدتی شرح کو برقرار رکھتے ہوئے پھٹنے کی اجازت دینے کے لیے سونے کا معیار۔ ہر صارف کے پاس ایک بالٹی ہے جو مستقل شرح سے بھرتی ہے۔ درخواستیں ٹوکن استعمال کرتی ہیں۔

async function consumeToken(
  redis: Redis,
  userId: string,
  ratePerSec: number,
  capacity: number
): Promise<boolean> {
  const now = Date.now() / 1000;
  const key = `bucket:${userId}`;

  const values = await redis.hmget(key, 'tokens', 'lastRefill');
  const currentTokens = parseFloat(values[0] ?? String(capacity));
  const lastRefillTime = parseFloat(values[1] ?? String(now));

  // Refill tokens based on elapsed time
  const elapsed = now - lastRefillTime;
  const refilled = Math.min(capacity, currentTokens + elapsed * ratePerSec);

  if (refilled < 1) {
    return false; // No tokens available
  }

  await redis.hset(key, 'tokens', String(refilled - 1), 'lastRefill', String(now));
  await redis.expire(key, Math.ceil(capacity / ratePerSec) + 60);

  return true;
}

ٹوکن بالٹی APIs کے لیے مثالی ہے جنہیں مسلسل غلط استعمال کو روکنے کے دوران مختصر برسٹ (ایک ساتھ 10 فائلیں اپ لوڈ کرنے) کی اجازت دینے کی ضرورت ہوتی ہے۔


NestJS تھروٹلر کنفیگریشن

@nestjs/throttler v5 Redis اسٹوریج اڈاپٹر کے ساتھ بھیجتا ہے۔ یہاں پروڈکشن کے لیے تیار سیٹ اپ ہے:

pnpm add @nestjs/throttler @nestjs/throttler-storage-redis ioredis
// app.module.ts
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { ThrottlerStorageRedisService } from '@nestjs/throttler-storage-redis';
import { APP_GUARD } from '@nestjs/core';

@Module({
  imports: [
    ThrottlerModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        throttlers: [
          { name: 'short',  ttl: 1000,    limit: 5    }, // 5 req/sec burst
          { name: 'medium', ttl: 60000,   limit: 300  }, // 300 req/min
          { name: 'long',   ttl: 3600000, limit: 5000 }, // 5000 req/hr
        ],
        storage: new ThrottlerStorageRedisService({
          host: config.get('REDIS_HOST'),
          port: config.get('REDIS_PORT'),
        }),
      }),
    }),
  ],
  providers: [
    {
      provide: APP_GUARD,
      useClass: ThrottlerGuard,
    },
  ],
})
export class AppModule {}

فی کنٹرولر یا روٹ کی حدوں کو اوور رائیڈ کریں:

@Controller('auth')
export class AuthController {
  // Authentication: very strict — 5 attempts per minute
  @Post('login')
  @Throttle({ medium: { ttl: 60000, limit: 5 } })
  async login(@Body() dto: LoginDto) { /* ... */ }

  // Refresh: moderate — 30 per minute
  @Post('refresh')
  @Throttle({ medium: { ttl: 60000, limit: 30 } })
  async refresh(@Body() dto: RefreshDto) { /* ... */ }

  // Skip throttling on the exchange endpoint (protected by one-time code TTL)
  @Post('exchange')
  @SkipThrottle()
  async exchange(@Body() dto: ExchangeDto) { /* ... */ }
}

حسب ضرورت کلیدی جنریٹرز

پہلے سے طے شدہ طور پر، NestJS تھروٹلر کلائنٹ IP استعمال کرتا ہے۔ Nginx/Cloudflare کے پیچھے پیداوار میں، آپ کو X-Real-IP یا CF-Connecting-IP کی ضرورت ہے۔

// throttler-behind-proxy.guard.ts
import { ThrottlerGuard } from '@nestjs/throttler';
import { Injectable, ExecutionContext } from '@nestjs/common';

@Injectable()
export class ThrottlerBehindProxyGuard extends ThrottlerGuard {
  protected async getTracker(req: Record<string, any>): Promise<string> {
    // Authenticated user — use userId for accurate per-user limits
    if (req.user?.sub) {
      return `user:${req.user.sub}`;
    }
    // Anonymous — use real IP from Cloudflare header
    return (
      req.headers['cf-connecting-ip'] ||
      req.headers['x-real-ip'] ||
      (req.headers['x-forwarded-for'] as string)?.split(',')[0] ||
      req.ip
    );
  }

  protected async throwThrottlingException(
    context: ExecutionContext,
    throttlerLimitDetail: ThrottlerLimitDetail
  ): Promise<void> {
    const response = context.switchToHttp().getResponse();
    response.header(
      'Retry-After',
      Math.ceil(throttlerLimitDetail.ttl / 1000)
    );
    await super.throwThrottlingException(context, throttlerLimitDetail);
  }
}

رسپانس ہیڈرز

RFC 6585 اور ڈرافٹ RateLimit ہیڈر کلائنٹس کو بالکل بتاتے ہیں کہ کب دوبارہ کوشش کرنی ہے:

// rate-limit.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, tap } from 'rxjs';

@Injectable()
export class RateLimitHeadersInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      tap(() => {
        const res = context.switchToHttp().getResponse();
        const req = context.switchToHttp().getRequest();

        // Values injected by ThrottlerGuard after evaluation
        if (req.rateLimit) {
          res.set({
            'X-RateLimit-Limit': req.rateLimit.limit,
            'X-RateLimit-Remaining': Math.max(
              0,
              req.rateLimit.limit - req.rateLimit.current
            ),
            'X-RateLimit-Reset': new Date(
              Date.now() + req.rateLimit.ttl
            ).toISOString(),
            'RateLimit-Policy': `${req.rateLimit.limit};w=${Math.ceil(
              req.rateLimit.ttl / 1000
            )}`,
          });
        }
      })
    );
  }
}

اختتامی نقطہ سے متعلق مخصوص حکمت عملی

مختلف اختتامی نقطے مختلف حدود کی ضمانت دیتے ہیں۔ یہاں عام پیٹرن کے لئے ایک حوالہ جدول ہے:

اختتامی نقطہ کی قسمالگورتھمحدکھڑکی
لاگ ان / پاس ورڈ ری سیٹسلائیڈنگ ونڈو لاگ515 منٹ
OTP / 2FA تصدیقفکسڈ ونڈو310 منٹ
پبلک ریڈ APIٹوکن بالٹی1000 برسٹ، 100/s فل-
Mutation API (توثیق شدہ)سلائیڈنگ ونڈو کاؤنٹر3001 منٹ
ویب ہُک ادخالفکسڈ ونڈو10,0001 منٹ
فائل اپ لوڈٹوکن بالٹی10 برسٹ، 1/s بھرنا-
AI / LLM اینڈ پوائنٹسفکسڈ ونڈو201 منٹ
تلاش کریں (گمنام)فکسڈ ونڈو301 منٹ

تقسیم شدہ حفاظت کے لیے اٹامک لوا اسکرپٹس

جب آپ کے پاس ایک سے زیادہ API ریپلیکس ہوتے ہیں، تو انکریمنٹ چیک سیکونسز میں ریس کے حالات حد سے زیادہ پھٹنے کی اجازت دے سکتے ہیں۔ چیک اینڈ انکریمنٹ کو ایٹم بنانے کے لیے redis.defineCommand کے ذریعے بھری ہوئی Lua اسکرپٹ کا استعمال کریں:

// rate-limit.service.ts
import { Injectable } from '@nestjs/common';
import Redis from 'ioredis';

@Injectable()
export class RateLimitService {
  constructor(private readonly redis: Redis) {
    // Define atomic increment+check as a custom Redis command
    this.redis.defineCommand('rateLimitCheck', {
      numberOfKeys: 1,
      lua: `
        local key   = KEYS[1]
        local limit = tonumber(ARGV[1])
        local ttlMs = tonumber(ARGV[2])
        local count = redis.call('INCR', key)
        if count == 1 then
          redis.call('PEXPIRE', key, ttlMs)
        end
        if count > limit then
          return {0, redis.call('PTTL', key)}
        end
        return {1, -1}
      `,
    });
  }

  async isAllowed(
    key: string,
    limit: number,
    windowMs: number
  ): Promise<{ allowed: boolean; retryAfterMs: number }> {
    const result = await (this.redis as any).rateLimitCheck(
      key, limit, windowMs
    ) as [number, number];

    return {
      allowed: result[0] === 1,
      retryAfterMs: result[1] > 0 ? result[1] : 0,
    };
  }
}

مکرم انحطاط اور بائی پاس کی حکمت عملی

شرح کو محدود کرنے سے اندرونی صحت کی جانچ، نگرانی کرنے والے ایجنٹوں، یا قابل اعتماد شراکت داروں کو روکنا نہیں چاہیے۔

// trusted-bypass.guard.ts
@Injectable()
export class RateLimitWithBypassGuard extends ThrottlerBehindProxyGuard {
  private readonly trustedTokens = new Set([
    process.env.MONITORING_TOKEN,
    process.env.PARTNER_API_TOKEN,
  ].filter(Boolean));

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const req = context.switchToHttp().getRequest();

    // Internal health checks bypass all rate limits
    if (req.path === '/health' || req.path === '/ready') {
      return true;
    }

    // Trusted API tokens bypass
    const token = req.headers['x-bypass-token'];
    if (token && this.trustedTokens.has(token)) {
      return true;
    }

    return super.canActivate(context);
  }
}

ترقی پسند شرح کو محدود کرنے کے لیے (ہارڈ بلاک سے پہلے انتباہ)، 429 کو Retry-After ہیڈر کے ساتھ 80% حد کو عبور کرنے کے بعد واپس کریں:

// In your custom guard, after counting requests:
if (count > limit * 0.9 && count <= limit) {
  response.set('X-RateLimit-Warning', 'approaching limit');
}
if (count > limit) {
  response.set('Retry-After', retryAfterSeconds.toString());
  throw new HttpException('Rate limit exceeded', 429);
}

جانچ کی شرح کی حد

// rate-limit.spec.ts
describe('Rate Limiting', () => {
  it('should block after limit exceeded', async () => {
    const app = moduleRef.createNestApplication();
    await app.init();

    // Hit the endpoint 5 times (limit for login)
    for (let i = 0; i < 5; i++) {
      await request(app.getHttpServer())
        .post('/auth/login')
        .send({ email: '[email protected]', password: 'wrongpassword' })
        .expect((res) => expect(res.status).toBeLessThan(429));
    }

    // 6th request should be blocked
    const response = await request(app.getHttpServer())
      .post('/auth/login')
      .send({ email: '[email protected]', password: 'wrongpassword' });

    expect(response.status).toBe(429);
    expect(response.headers['retry-after']).toBeDefined();
    expect(response.body.message).toContain('rate limit');
  });
});

نگرانی اور انتباہ

شرح کی حد کے واقعات قیمتی سگنل ہیں۔ انہیں اپنے مشاہداتی پلیٹ فارم پر لاگ ان کریں:

@Injectable()
export class RateLimitMetricsService {
  async recordRateLimitHit(userId: string, endpoint: string, ip: string) {
    await this.metricsService.increment('rate_limit.hits', {
      endpoint,
      user_type: userId ? 'authenticated' : 'anonymous',
    });

    // Alert on sustained attacks (>100 hits in 1 min from same IP)
    const alertKey = `rl_alert:${ip}`;
    const recentHits = await this.redis.incr(alertKey);
    if (recentHits === 1) {
      await this.redis.expire(alertKey, 60);
    }

    if (recentHits === 100) {
      await this.alertService.send({
        severity: 'high',
        message: `Rate limit attack detected from IP ${ip}`,
        endpoint,
      });
    }
  }
}

ڈیش بورڈز ٹریکنگ بنائیں:

  • ریٹ کی حد ہٹ فی اینڈ پوائنٹ (p95, p99)
  • سرفہرست IPs/صارفین حدوں کو مار رہے ہیں۔
  • بلاک شدہ درخواستوں کا فیصد بمقابلہ پیش کیا گیا۔
  • غلط کنفیگر شدہ کلائنٹس کا پتہ لگانے کے لیے دورانیے کے بعد دوبارہ کوشش کریں۔

اکثر پوچھے گئے سوالات

کیا مجھے IP یا صارف ID کے ذریعہ حد کی درجہ بندی کرنی چاہئے؟

دونوں استعمال کریں۔ غیر تصدیق شدہ اختتامی پوائنٹس کے لیے، IP واحد شناخت کنندہ دستیاب ہے۔ تصدیق شدہ اینڈ پوائنٹس کے لیے، ہمیشہ صارف ID کو ترجیح دیں — یہ زیادہ درست ہے اور ایک مشترکہ IP (جیسے کارپوریٹ NAT) کو تمام ملازمین کو بلاک کرنے سے روکتا ہے۔ دو درجے کی جانچ کو لاگو کریں: Nginx سطح پر IP کی حد اور درخواست کی سطح پر صارف-ID کی حد۔

ریٹ محدود کرنے کے لیے صحیح HTTP اسٹیٹس کوڈ کیا ہے؟

ہمیشہ 429 Too Many Requests فی RFC 6585۔ کبھی بھی 503 Service Unavailable (انفراسٹرکچر کی ناکامی کا مطلب ہے) یا 403 Forbidden (مطلب اجازت کی ناکامی) کا استعمال نہ کریں۔ Retry-After کو ہیڈر کے بطور سیکنڈ میں شامل کریں تاکہ کلائنٹس کو معلوم ہو کہ کب دوبارہ کوشش کرنی ہے۔

میں Cloudflare یا لوڈ بیلنسر کے پیچھے ریٹ کی حد کو کیسے ہینڈل کروں؟

اپنی پراکسی کو X-Real-IP یا CF-Connecting-IP سیٹ کرنے کے لیے کنفیگر کریں اور صرف اپنی پراکسی کی IP رینج پر بھروسہ کریں۔ Nginx میں: set_real_ip_from 103.21.244.0/22; real_ip_header CF-Connecting-IP;۔ NestJS میں، app.set('trust proxy', 1) سیٹ کریں اور req.ip پڑھیں جسے NestJS قابل اعتماد پراکسی ہیڈر سے حل کرتا ہے۔

ریٹ کو محدود کرنے کے لیے کون سا Redis ڈیٹا ڈھانچہ بہترین ہے؟

فکسڈ/سلائیڈنگ ونڈو کاؤنٹرز کے لیے، سٹرنگ کلید پر INCR + EXPIRE استعمال کریں — O(1) فی درخواست۔ سلائیڈنگ ونڈو لاگ کے لیے، ایک ترتیب شدہ سیٹ استعمال کریں (ZADD, ZREMRANGEBYSCORE, ZCARD) — O(log n)۔ ٹوکن بالٹی کے لیے، ایک ہیش استعمال کریں (HSET, HGET) — O(1)۔ Lua اسکرپٹس تمام کارروائیوں کو تینوں نمونوں میں ایٹمی بناتی ہیں۔

< خلاصہ

پٹی، GitHub، اور اسی طرح کے فراہم کنندگان معروف IP رینجز سے ویب ہکس بھیجتے ہیں۔ ان کی CIDR رینجز کی اجازت یافتہ فہرست کو برقرار رکھیں اور آپ کے ویب ہک انجیکشن اینڈ پوائنٹ پر ان IPs کے لیے بائی پاس ریٹ کو محدود کریں۔ پہلے ویب ہک کے دستخط کی تصدیق کریں — دستخط کی توثیق وہاں آپ کی اصل حفاظتی پرت ہے، شرح کو محدود نہیں کرنا۔

کیا میں اس کے بجائے Nginx لیول پر ریٹ محدود کرنے کو لاگو کر سکتا ہوں؟

ہاں، اور آپ کو بنیادی DDoS تحفظ کے لیے کرنا چاہیے۔ موٹے IP پر مبنی حدود (1000 req/min) کے لیے Nginx میں limit_req_zone استعمال کریں۔ دانے دار فی صارف، فی-اینڈ پوائنٹ کنٹرول کے لیے سب سے اوپر پر حد اطلاق کی سطح کی شرح۔ دونوں پرتیں ایک دوسرے کی تکمیل کرتی ہیں: Nginx آپ کی ایپلی کیشن کو مارے بغیر حجم کے حملوں کو سستے طریقے سے ہینڈل کرتا ہے، اور NestJS کاروباری منطق کی اہم حدود کو سنبھالتا ہے۔


اگلے اقدامات

مضبوط شرح کو محدود کیے بغیر پروڈکشن API بنانا اپنے سامنے کے دروازے کو کھلا چھوڑنے کے مترادف ہے۔ اس گائیڈ میں پیٹرن — پھٹے ہوئے ٹریفک کے لیے ٹوکن بالٹی، ہموار نفاذ کے لیے سلائیڈنگ ونڈو کاؤنٹر، Redis کی حمایت یافتہ تقسیم شدہ اسٹوریج، اور مناسب 429 جوابات — ایک محفوظ، توسیع پذیر API کی ریڑھ کی ہڈی کی تشکیل کرتے ہیں۔

ECOSIRE انٹرپرائز گریڈ NestJS بیک اینڈز بناتا ہے جس میں شرح کی حد بندی، Redis کیشنگ، اور پہلے دن سے مکمل مشاہدہ ہوتا ہے۔ اگر آپ ایک نیا API شروع کر رہے ہیں یا کسی موجودہ کو سخت کر رہے ہیں، تو ہماری بیک اینڈ انجینئرنگ سروسز کو دریافت کریں یہ دیکھنے کے لیے کہ ہم آپ کی ترسیل کو کیسے تیز کر سکتے ہیں۔

E

تحریر

ECOSIRE Research and Development Team

ECOSIRE میں انٹرپرائز گریڈ ڈیجیٹل مصنوعات بنانا۔ Odoo انٹیگریشنز، ای کامرس آٹومیشن، اور AI سے چلنے والے کاروباری حل پر بصیرت شیئر کرنا۔

Chat on WhatsApp