# Rate Limiting Communication Guide ## INT-013: Add API rate limiting communication **Date**: 2025-12-25 **Status**: Completed ## Overview This guide documents how rate limiting is communicated between the Veza backend API and frontend clients. It covers response formats, headers, error handling, and best practices. ## Backend Rate Limiting ### Rate Limit Configuration The Veza API implements multiple rate limiting strategies: 1. **IP-based rate limiting** (unauthenticated users) - Default: 100 requests per minute - Burst: 10 requests 2. **User-based rate limiting** (authenticated users) - Default: 1000 requests per minute - Burst: 100 requests 3. **Endpoint-specific rate limiting** - Login attempts: Configurable (default: 5 attempts per 15 minutes) - Upload endpoints: 10 uploads per hour per user ### Rate Limit Response Format When a rate limit is exceeded, the backend returns: **HTTP Status**: `429 Too Many Requests` **Headers**: ``` X-RateLimit-Limit: 100 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1703509200 Retry-After: 60 ``` **Response Body** (Standardized APIResponse format): ```json { "success": false, "error": { "code": 429, "message": "Rate limit exceeded. Please try again later.", "details": [ { "field": "rate_limit", "message": "You have exceeded the rate limit of 100 requests per minute" } ], "retry_after": 60, "limit": 100, "remaining": 0, "reset": 1703509200 } } ``` ### Response Fields - **`code`**: Error code (429 for rate limiting) - **`message`**: Human-readable error message - **`details`**: Array of validation/error details - **`retry_after`**: Number of seconds to wait before retrying - **`limit`**: Maximum number of requests allowed - **`remaining`**: Number of requests remaining (0 when limit exceeded) - **`reset`**: Unix timestamp when the rate limit resets ### Headers Explained - **`X-RateLimit-Limit`**: Maximum number of requests allowed in the time window - **`X-RateLimit-Remaining`**: Number of requests remaining in the current window - **`X-RateLimit-Reset`**: Unix timestamp when the rate limit window resets - **`Retry-After`**: Number of seconds to wait before retrying (RFC 7231) ## Frontend Rate Limit Handling ### Error Detection The frontend detects rate limit errors by: 1. **HTTP Status Code**: `429` 2. **Response Format**: Standardized `APIResponse` with `success: false` 3. **Error Code**: `429` in error object ### Error Parsing The frontend parses rate limit errors in `apiErrorHandler.ts`: ```typescript if (status === 429) { // Extract rate limit information from headers const rateLimitLimit = headers['x-ratelimit-limit']; const rateLimitRemaining = headers['x-ratelimit-remaining']; const rateLimitReset = headers['x-ratelimit-reset']; const retryAfter = headers['retry-after'] || data?.error?.retry_after || 60; // Calculate time until reset const resetTime = rateLimitReset ? new Date(rateLimitReset * 1000) : undefined; const secondsUntilReset = resetTime ? Math.max(0, Math.ceil((resetTime.getTime() - Date.now()) / 1000)) : retryAfter; return { code: 429, message: data?.error?.message || 'Trop de requêtes. Veuillez patienter avant de réessayer.', retry_after: secondsUntilReset, rate_limit: { limit: rateLimitLimit, remaining: rateLimitRemaining, reset: resetTime?.toISOString(), }, }; } ``` ### User Notification When a rate limit error occurs: 1. **Toast Notification**: Shows error message with retry information ```typescript toast.error(errorMessage, { duration: 8000, // Longer duration for rate limit errors }); ``` 2. **Error Message**: User-friendly message in French ``` "Trop de requêtes. Veuillez patienter quelques instants" ``` 3. **Retry Information**: Includes time until reset ``` "Réessayez dans 60 secondes" ``` ### Automatic Retry The frontend implements automatic retry for rate limit errors: 1. **Retry Configuration**: Rate limit errors are included in retryable status codes ```typescript retryableStatusCodes: [429, 500, 502, 503, 504] ``` 2. **Exponential Backoff**: Retries use exponential backoff ```typescript // For 429 errors, use retry_after if available if (status === 429 && retryAfter) { delay = retryAfter * 1000; // Use retry_after in seconds } ``` 3. **Retry Limits**: Maximum retries configured to prevent infinite loops ```typescript maxRetries: 3 ``` ## Best Practices ### For Backend Developers 1. **Always Include Headers**: Include all rate limit headers in responses ```go c.Header("X-RateLimit-Limit", strconv.Itoa(limit)) c.Header("X-RateLimit-Remaining", strconv.Itoa(remaining)) c.Header("X-RateLimit-Reset", strconv.FormatInt(resetTime, 10)) c.Header("Retry-After", strconv.Itoa(retryAfter)) ``` 2. **Use Standardized Format**: Use `APIResponse` format for consistency ```go c.JSON(http.StatusTooManyRequests, gin.H{ "success": false, "error": gin.H{ "code": 429, "message": "Rate limit exceeded. Please try again later.", "retry_after": retryAfter, "limit": limit, "remaining": 0, "reset": resetTime, }, }) ``` 3. **Calculate Accurate Reset Time**: Use actual window reset time, not fixed values ```go resetTime := time.Now().Add(window).Unix() ``` 4. **Provide Clear Messages**: Include helpful error messages ```go "message": fmt.Sprintf("You have exceeded the rate limit of %d requests per %v", limit, window) ``` ### For Frontend Developers 1. **Check Headers First**: Always check rate limit headers before parsing body ```typescript const rateLimitLimit = headers['x-ratelimit-limit']; const rateLimitRemaining = headers['x-ratelimit-remaining']; ``` 2. **Use Retry-After**: Respect the `Retry-After` header for retry timing ```typescript const retryAfter = headers['retry-after'] || 60; ``` 3. **Show User-Friendly Messages**: Display clear messages to users ```typescript "Trop de requêtes. Veuillez patienter quelques instants" ``` 4. **Implement Exponential Backoff**: Use exponential backoff for retries ```typescript delay = Math.min(delay * 2, maxDelay); ``` 5. **Track Rate Limit State**: Store rate limit information for UI display ```typescript rate_limit: { limit: rateLimitLimit, remaining: rateLimitRemaining, reset: resetTime, } ``` ## Rate Limit Headers Usage ### Reading Headers in Frontend ```typescript // Get rate limit information from response headers const getRateLimitInfo = (response: AxiosResponse) => { const headers = response.headers; return { limit: parseInt(headers['x-ratelimit-limit'] || '0', 10), remaining: parseInt(headers['x-ratelimit-remaining'] || '0', 10), reset: parseInt(headers['x-ratelimit-reset'] || '0', 10), retryAfter: parseInt(headers['retry-after'] || '0', 10), }; }; ``` ### Displaying Rate Limit Status ```typescript // Show rate limit status in UI const RateLimitIndicator = ({ rateLimit }) => { if (!rateLimit) return null; const resetTime = new Date(rateLimit.reset * 1000); const timeUntilReset = Math.ceil((resetTime.getTime() - Date.now()) / 1000); return (