← Назад к вопросам

Как ограничить количество запросов к Endpoint?

2.0 Middle🔥 231 комментариев
#Браузер и сетевые технологии#Оптимизация и производительность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Rate Limiting в Frontend: Ограничение запросов к API

Rate limiting (ограничение частоты запросов) — критически важный паттерн для защиты API и улучшения пользовательского опыта. На frontend это реализуется несколькими способами, каждый со своей областью применения.

1. Request Debouncing и Throttling

Первый уровень защиты — предотвращение отправки множественных запросов из-за быстрых пользовательских действий (ввод текста, клики).

Debouncing откладывает запрос до окончания действия:

function useDebounce(callback, delay) {
  const timeoutRef = useRef(null);
  
  return useCallback((...args) => {
    clearTimeout(timeoutRef.current);
    timeoutRef.current = setTimeout(() => {
      callback(...args);
    }, delay);
  }, [callback, delay]);
}

// Использование в компоненте поиска
const handleSearch = useDebounce((query) => {
  api.search(query);
}, 300);

Throttling ограничивает частоту вызовов:

function useThrottle(callback, interval) {
  const lastCallRef = useRef(null);
  
  return useCallback((...args) => {
    const now = Date.now();
    if (!lastCallRef.current || now - lastCallRef.current >= interval) {
      callback(...args);
      lastCallRef.current = now;
    }
  }, [callback, interval]);
}

// Для scroll-событий
const handleScroll = useThrottle(() => {
  loadMoreData();
}, 1000);

2. Client-side Rate Limiting

Ограничиваем количество одновременных запросов к конкретному endpoint:

class RateLimiter {
  constructor(maxRequests = 5, windowMs = 60000) {
    this.maxRequests = maxRequests;
    this.windowMs = windowMs;
    this.requests = [];
  }

  async execute(fn) {
    const now = Date.now();
    this.requests = this.requests.filter(time => now - time < this.windowMs);
    
    if (this.requests.length >= this.maxRequests) {
      const oldestRequest = this.requests[0];
      const waitTime = this.windowMs - (now - oldestRequest);
      await new Promise(resolve => setTimeout(resolve, waitTime));
      return this.execute(fn);
    }
    
    this.requests.push(now);
    return fn();
  }
}

const searchLimiter = new RateLimiter(3, 1000);

async function searchQuestions(query) {
  return searchLimiter.execute(() => 
    fetch(`/api/v1/questions/search?q=${query}`)
  );
}

3. Retry Logic с Exponential Backoff

Восстановление после ошибок с растущей задержкой:

async function fetchWithRetry(url, options = {}, retries = 3) {
  const maxRetries = retries;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
      if (response.ok) return response;
      
      if (response.status === 429) {
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
    } catch (error) {
      const delay = Math.pow(2, attempt) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
      if (attempt === maxRetries - 1) throw error;
    }
  }
}

4. Server-side Rate Limiting Headers

Фронтенд должен обрабатывать заголовки rate limiting от сервера:

class APIClient {
  constructor() {
    this.rateLimitReset = null;
  }
  
  async request(url, options = {}) {
    if (this.rateLimitReset && Date.now() < this.rateLimitReset) {
      const waitTime = this.rateLimitReset - Date.now();
      await new Promise(resolve => setTimeout(resolve, waitTime));
    }
    
    const response = await fetch(url, options);
    
    // Обрабатываем Rate Limit заголовки
    const remaining = response.headers.get('X-RateLimit-Remaining');
    const reset = response.headers.get('X-RateLimit-Reset');
    
    if (response.status === 429) {
      this.rateLimitReset = parseInt(reset) * 1000;
      throw new Error('Rate limit exceeded');
    }
    
    return response;
  }
}

5. Практические Рекомендации

  • Debounce для поиска и фильтрации (300-500ms)
  • Throttle для scroll и resize (100-1000ms)
  • Client-side limits для критичных операций (загрузка файлов, платежи)
  • Retry logic с экспоненциальной задержкой для сетевых ошибок
  • Всегда обрабатывай заголовки Rate Limiting от сервера
  • Кэшируй результаты запросов (localStorage, React Query)

Распределённый подход (frontend + backend) обеспечивает надёжность и масштабируемость приложения.

Как ограничить количество запросов к Endpoint? | PrepBro