API Rate Limiter
Run ID: 69cacb68eff1ba2b79624de82026-03-30Development
PantheraHive BOS
BOS Dashboard

Step 1: API Rate Limiter - Architecture Plan

1. Introduction and Overview

An API Rate Limiter is a critical component in modern microservices architectures and public-facing APIs. Its primary purpose is to control the rate at which clients can access a server or API, preventing abuse, ensuring fair usage, protecting backend services from overload, and maintaining system stability and availability. This document outlines a detailed architecture plan for a robust, scalable, and highly available API Rate Limiter.

2. Key Requirements and Goals

The API Rate Limiter must address the following key requirements:

3. Core Components and Modules

The API Rate Limiter will consist of the following core components:

4. Rate Limiting Algorithms

The architecture should support multiple rate limiting algorithms, with the ability to configure them per rule:

* Mechanism: A counter increments for each request within a fixed time window (e.g., 100 requests per minute).

* Pros: Simple to implement, low overhead.

* Cons: Prone to "bursty" traffic at the window edges, allowing twice the limit within a short period around the window reset.

* Mechanism: Stores a timestamp for each request. When a new request arrives, it counts requests within the sliding window (e.g., last 60 seconds) and removes expired timestamps.

* Pros: Very accurate, smooth rate limiting.

* Cons: High memory usage due to storing all timestamps, computationally more expensive.

* Mechanism: A hybrid approach. Divides the time into fixed-size windows. For a new request, it calculates a weighted average of the current window's count and the previous window's count, considering how much of the current window has passed.

* Pros: Better accuracy than Fixed Window, lower memory footprint than Sliding Window Log.

* Cons: Still an approximation, can be complex to implement correctly.

* Mechanism: Clients are given a "bucket" of tokens. Tokens are added to the bucket at a fixed rate. Each request consumes one token. If the bucket is empty, the request is denied.

* Pros: Allows for bursts of traffic up to the bucket size, then enforces a steady rate.

* Cons: Requires careful tuning of bucket size and refill rate.

Recommendation: Start with Fixed Window Counter for simplicity and performance for most cases. Implement Token Bucket for scenarios requiring burst tolerance. Consider Sliding Window Counter for more precise control with reasonable overhead, especially if Redis is used.

5. Data Storage/Persistence

The rate limiter requires a fast, distributed, and in-memory data store for counters and timestamps.

* Pros: Excellent performance, supports atomic operations (INCR, EXPIRE), rich data structures (hashes, sorted sets), built-in replication, clustering for scalability and high availability. Ideal for distributed rate limiting.

* Cons: Requires careful management of memory and persistence if used as a primary store for other data.

* Pros: Very fast key-value store, simpler than Redis.

* Cons: Lacks advanced data structures and atomic operations like Redis, less suitable for complex rate limiting algorithms.

* Pros: Extremely fast, no network overhead.

* Cons: Not suitable for distributed systems (each instance would have its own counter), data loss on restart, difficult to manage consistency.

Recommendation: Redis Cluster is the recommended choice for its performance, atomic operations, data structures, and distributed capabilities, making it ideal for implementing all common rate limiting algorithms at scale.

6. System Architecture (High-Level)

text • 1,944 chars
+----------------+       +---------------------+       +---------------------------+
| Client/User    | ----> | API Gateway/Proxy   | ----> | Rate Limiting Service (N) |
| (Web/Mobile App)|       | (e.g., Nginx, Envoy)|       | (e.g., Go/Java/Node.js)   |
+----------------+       +---------------------+       +---------------------------+
                                   ^     |                        |
                                   |     |                        | Allow/Deny
                                   |     |                        v
                                   |     |             +-------------------------+
                                   |     |             | Distributed Cache/Store |
                                   |     |             | (e.g., Redis Cluster)   |
                                   |     |             +-------------------------+
                                   |     |                        ^
                                   |     |                        | Updates
                                   |     |                        v
                                   |     |             +-------------------------+
                                   |     +-----------> | Configuration Service   |
                                   |                   | (e.g., etcd, Consul)    |
                                   |                   +-------------------------+
                                   |                                |
                                   |                                v
                                   +---------------------+       +-------------------------+
                                                         |       | Monitoring & Alerting   |
                                                         |       | (Prometheus, Grafana)   |
                                                         +-----> +-------------------------+
Sandboxed live preview

Workflow:

  1. A client makes a request to the API Gateway.
  2. The API Gateway (or a dedicated proxy layer) intercepts the request and extracts relevant client identifiers (API Key, User ID, IP, etc.) and endpoint information.
  3. The API Gateway forwards this information to the Rate Limiting Service.
  4. The Rate Limiting Service fetches the applicable rate limiting rules from the Configuration Service.
  5. It queries the Distributed Cache/Store (Redis) for the current state (counters, timestamps) associated with the client and rule.
  6. Based on the chosen algorithm and current state, the Rate Limiting Service determines if the request should be allowed or denied.
  7. It updates the state in the Distributed Cache/Store (e.g., increments a counter, adds a timestamp).
  8. If allowed, the API Gateway forwards the request to the target backend service.
  9. If denied, the API Gateway returns an HTTP 429 Too Many Requests response to the client.
  10. All actions (allow/deny) and relevant metrics are sent to the Monitoring & Alerting system.

7. Detailed Design Considerations

7.1. Concurrency and Distributed Nature

  • Atomic Operations: Utilize Redis's atomic operations (e.g., INCR, SETNX, Lua scripts) to ensure thread-safety and consistency across multiple instances of the Rate Limiting Service.
  • Distributed Locks: For complex scenarios or custom algorithms, distributed locks (e.g., Redlock with Redis) might be considered, though careful design can often avoid them for performance.
  • Eventual Consistency: While rate limiting requires strong consistency for counters within a short window, the overall system can tolerate eventual consistency for rule propagation from the Configuration Service.

7.2. Scalability

  • Horizontal Scaling: The Rate Limiting Service should be stateless (aside from its interaction with Redis) and horizontally scalable. Multiple instances can run in parallel behind a load balancer.
  • Redis Cluster: Use Redis Cluster for sharding data and distributing load across multiple Redis nodes, enabling horizontal scaling of the data store.
  • API Gateway/Proxy: Choose a highly scalable API Gateway (e.g., Nginx, Envoy, Kong, AWS API Gateway) that can handle high throughput.

7.3. High Availability

  • Redundancy: Deploy multiple instances of the Rate Limiting Service across different availability zones.
  • Redis Replication: Use Redis Sentinels or Redis Cluster for automatic failover and replication to prevent a single point of failure in the data store.
  • Configuration Service: Use a highly available configuration store (e.g., etcd, Consul, Zookeeper) with replication.
  • API Gateway: Utilize a highly available API Gateway setup with load balancing and health checks.

7.4. Fault Tolerance

  • Circuit Breakers: Implement circuit breakers in the API Gateway to prevent cascading failures if the Rate Limiting Service or Redis becomes unresponsive.
  • Timeouts and Retries: Configure appropriate timeouts and retry mechanisms for communication between components.
  • Graceful Degradation: In extreme cases, if the Rate Limiting Service is unavailable, the system could temporarily allow all requests (fail-open) or deny all requests (fail-closed), depending on the business criticality and acceptable risk. Fail-open might be preferred to avoid a complete outage, but it exposes the backend to potential overload.

7.5. Security

  • Secure Communication: All internal and external communication should use TLS/SSL.
  • Access Control: Implement strong access control for the Configuration Service and the Distributed Cache/Store.
  • Input Validation: Ensure all configuration rules and client identifiers are properly validated.
  • DDoS Protection: While the rate limiter helps, it should be complemented by higher-level DDoS protection services (e.g., Cloudflare, AWS Shield).

7.6. Monitoring & Logging

  • Metrics: Collect and expose metrics such as:

* Total requests processed

* Requests allowed/denied

* Latency of the Rate Limiting Service

* Redis connection health, memory usage, and operations per second

* Rate limit breaches per client/rule

  • Logging: Log all critical events, errors, and rate limit decisions. Use a centralized logging system (e.g., ELK Stack, Splunk).
  • Alerting: Set up alerts for:

* High error rates in the Rate Limiting Service

* Redis node failures or high latency

* Sustained high denial rates for specific clients or endpoints

* Configuration update failures

7.7. API Gateway Integration

  • Pluggable Architecture: The rate limiter should be designed to be easily integrated with various API Gateways or proxies.
  • Header-Based Communication: Pass client identifiers and other context via HTTP headers (e.g., X-Forwarded-For, X-Client-ID).
  • Standard Responses: Return standard HTTP 429 Too Many Requests with Retry-After headers.

7.8. Configuration Management

  • Dynamic Configuration: Rules should be dynamically configurable without requiring service restarts.
  • Centralized Configuration Store: Use a distributed key-value store (e.g., etcd, Consul, AWS Parameter Store, Kubernetes ConfigMaps) to store rate limiting rules.
  • Rule Structure: Define a clear JSON/YAML schema for rate limiting rules, including:

* id: Unique rule identifier

* target: What to limit (e.g., ip, user_id, api_key, endpoint)

* match_pattern: Regular expression or exact match for target

* limit: Maximum requests

* window: Time window (e.g., 1m, 1h)

* algorithm: fixed_window, token_bucket, sliding_window_counter

* burst: (for token bucket) maximum burst capacity

* action: deny, throttle (e.g., delay response)

* priority: For overlapping rules

* enabled: Boolean to activate/deactivate

  • Version Control: Store rules in a version-controlled system (e.g., Git) before pushing to the Configuration Service.

8. Technology Stack Recommendations

  • API Gateway/Proxy: Nginx (with Lua/OpenResty for custom logic), Envoy Proxy, Kong Gateway, AWS API Gateway, Google Cloud Endpoints.
  • Rate Limiting Service:

* Language: Go (for high performance, concurrency, small footprint), Java (Spring Boot for enterprise features), Node.js (for event-driven, non-blocking I/O).

* Frameworks: Go-Gin, Spring WebFlux, Express.js.

  • Distributed Cache/Store: Redis Cluster.
  • Configuration Service: etcd, Consul, Kubernetes ConfigMaps.
  • Monitoring: Prometheus (metrics collection), Grafana (dashboarding), Loki/Elasticsearch (log aggregation).
  • Container Orchestration: Kubernetes (for deployment, scaling, and management of services).

9. High-Level Implementation Steps

  1. Define Rate Limiting Rules Schema: Establish a clear and flexible structure for defining rate limits.
  2. Set up Redis Cluster: Provision and configure a highly available Redis Cluster.
  3. Develop Rate Limiting Service:

* Implement core logic for selected algorithms (Fixed Window, Token Bucket).

* Integrate with Redis for state management using atomic operations.

* Implement rule fetching from a Configuration Service.

* Expose a clear API for the API Gateway to query.

  1. Integrate with API Gateway:

* Configure the API Gateway to intercept requests.

* Add logic to extract client identifiers and call the Rate Limiting Service.

* Handle responses (allow/deny) and return appropriate HTTP status codes.

  1. Develop Configuration Service:

* Create an interface for managing rate limiting rules.

* Implement rule storage and distribution (e.g., push to etcd).

  1. Implement Monitoring & Logging:

* Add metrics collection to the Rate Limiting Service and API Gateway.

* Configure logging to a centralized system.

* Set up dashboards and alerts.

  1. Testing: Thoroughly test various scenarios, including:

* Normal traffic within limits.

* Burst traffic exceeding limits.

* Edge cases around window resets.

* Service failures and failover.

* Performance and latency under load.

10. Future Enhancements

  • More Advanced Algorithms: Implement Sliding Window Log or Sliding Window
gemini Output

This document provides a comprehensive and professional output for the "API Rate Limiter" step, focusing on generating production-ready code and detailed explanations. This deliverable is designed to be directly actionable for implementation.


API Rate Limiter: Code Generation and Implementation Guide

This deliverable focuses on providing robust, well-commented, and production-ready code for implementing API Rate Limiters. We will cover two widely used algorithms: the Fixed Window Counter and the Sliding Window Log. Both implementations leverage Redis as a high-performance, atomic, and distributed store for managing rate limiting states, making them suitable for scalable microservice architectures.

1. Introduction to API Rate Limiting

API Rate Limiting is a critical component for protecting your services from abuse, ensuring fair usage, and maintaining stability under high load. It prevents malicious actors from overwhelming your APIs with too many requests and helps manage resource consumption.

Key Benefits:

  • Preventing DDoS Attacks: Mitigates the impact of denial-of-service attacks.
  • Resource Protection: Safeguards backend services from being overloaded.
  • Cost Management: Controls resource usage, especially for cloud-based services.
  • Fair Usage: Ensures all consumers have reasonable access to the API.
  • Monetization: Enables differentiated service tiers (e.g., higher limits for premium users).

2. Core Concepts and Algorithms

Several algorithms exist for rate limiting, each with its own characteristics.

  • Fixed Window Counter: Divides time into fixed-size windows. Each request increments a counter within the current window. If the counter exceeds the limit, the request is denied.

* Pros: Simple to implement, low memory usage.

* Cons: Can allow a "burst" of requests at the window boundary (e.g., N requests at the very end of window 1 and N requests at the very beginning of window 2, effectively 2N requests in a short period).

  • Sliding Window Log: Stores a timestamp for each request made by a user. When a new request arrives, it removes all timestamps older than the current time minus the window duration. If the remaining count of timestamps exceeds the limit, the request is denied.

* Pros: Very accurate, handles bursty traffic well, offers a smoother rate limit.

* Cons: Higher memory usage as it stores individual timestamps.

  • Token Bucket: A bucket holds a fixed capacity of "tokens." Tokens are added to the bucket at a constant rate. Each request consumes one token. If the bucket is empty, the request is denied.

* Pros: Allows for some burstiness (up to the bucket capacity), smooths out traffic.

* Cons: More complex to implement, especially in a distributed environment.

  • Leaky Bucket: Similar to Token Bucket but focuses on smoothing out outgoing requests. Requests are added to a bucket, and they "leak" out at a constant rate. If the bucket overflows, new requests are denied.

* Pros: Excellent for smoothing out traffic, preventing downstream systems from being overwhelmed.

* Cons: Can introduce latency if the leak rate is slower than the arrival rate.

For this deliverable, we will provide code for Fixed Window Counter and Sliding Window Log using Redis, as they offer a good balance of simplicity, efficiency, and accuracy for many common use cases.

3. Implementation Strategy: Python with Redis

We will implement the rate limiters using Python and Redis.

  • Python: A versatile language widely used for backend services, offering excellent libraries.
  • Redis: An in-memory data structure store, used as a database, cache, and message broker. Its atomic operations, high performance, and support for various data structures (strings, sorted sets) make it ideal for distributed rate limiting.

Prerequisites:

  • Python 3.x installed.
  • Redis server running and accessible.
  • redis-py library installed: pip install redis

4. Code Implementation

Below are the Python implementations for the Fixed Window Counter and Sliding Window Log algorithms using Redis.

4.1 Fixed Window Counter Rate Limiter

This algorithm counts requests within a fixed time window.


import redis
import time
from typing import Optional

class FixedWindowRateLimiter:
    """
    Implements a Fixed Window Counter rate limiting algorithm using Redis.

    This algorithm divides time into fixed-size windows (e.g., 60 seconds).
    Each request increments a counter within the current window. If the counter
    exceeds the allowed limit for that window, the request is denied.

    Pros: Simple to implement, low memory usage.
    Cons: Can suffer from a "burst" problem at window boundaries.
          For example, if the limit is 100 requests/minute, a user could make
          100 requests at 0:59 and another 100 requests at 1:00, effectively
          making 200 requests within a two-second span.
    """

    def __init__(self, redis_client: redis.Redis, limit: int, window_seconds: int):
        """
        Initializes the FixedWindowRateLimiter.

        Args:
            redis_client: An initialized Redis client instance.
            limit: The maximum number of requests allowed within the window_seconds.
            window_seconds: The duration of the fixed window in seconds.
        """
        if not isinstance(redis_client, redis.Redis):
            raise TypeError("redis_client must be an instance of redis.Redis")
        if not isinstance(limit, int) or limit <= 0:
            raise ValueError("limit must be a positive integer")
        if not isinstance(window_seconds, int) or window_seconds <= 0:
            raise ValueError("window_seconds must be a positive integer")

        self.redis = redis_client
        self.limit = limit
        self.window_seconds = window_seconds

    def _get_window_key(self, user_id: str) -> str:
        """
        Generates a Redis key for the current window for a given user.
        The key incorporates the user_id and the current window start timestamp
        to ensure unique keys for each user and window.
        """
        current_window_start = int(time.time() // self.window_seconds) * self.window_seconds
        return f"rate_limit:fixed_window:{user_id}:{current_window_start}"

    def allow_request(self, user_id: str) -> bool:
        """
        Checks if a request from the given user_id should be allowed.

        Args:
            user_id: A unique identifier for the user or client making the request.

        Returns:
            True if the request is allowed, False otherwise.
        """
        if not isinstance(user_id, str) or not user_id:
            raise ValueError("user_id must be a non-empty string")

        key = self._get_window_key(user_id)

        # Use Redis pipeline for atomicity and efficiency
        with self.redis.pipeline() as pipe:
            pipe.incr(key)  # Increment the counter for the current window
            pipe.expire(key, self.window_seconds * 2) # Set expiration, slightly longer than window
                                                     # to prevent race conditions at window boundary
                                                     # where key might expire before counter is checked.
                                                     # Redis automatically updates TTL on INCR if it exists.
                                                     # We ensure it exists and has a sufficient TTL.
            count, _ = pipe.execute()

        return count <= self.limit

    def get_remaining_requests(self, user_id: str) -> int:
        """
        Gets the number of remaining requests for the current window for a given user.

        Args:
            user_id: A unique identifier for the user or client.

        Returns:
            The number of remaining requests. Returns 0 if the limit is exceeded
            or if the key doesn't exist (e.g., new window).
        """
        if not isinstance(user_id, str) or not user_id:
            raise ValueError("user_id must be a non-empty string")

        key = self._get_window_key(user_id)
        current_count_str = self.redis.get(key)
        current_count = int(current_count_str) if current_count_str else 0
        return max(0, self.limit - current_count)

    def get_reset_time(self, user_id: str) -> int:
        """
        Calculates the approximate time until the current window resets.

        Args:
            user_id: A unique identifier for the user or client.

        Returns:
            The time in seconds until the current window resets.
        """
        current_time = int(time.time())
        current_window_start = (current_time // self.window_seconds) * self.window_seconds
        next_window_start = current_window_start + self.window_seconds
        return max(0, next_window_start - current_time)

# --- Example Usage for Fixed Window Counter ---
if __name__ == "__main__":
    # Ensure a Redis server is running, e.g., on localhost:6379
    try:
        r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
        r.ping()
        print("Successfully connected to Redis.")
    except redis.exceptions.ConnectionError as e:
        print(f"Could not connect to Redis: {e}")
        print("Please ensure Redis server is running and accessible.")
        exit(1)

    # Clean up any previous test keys to ensure a fresh start
    print("Cleaning up previous test keys...")
    for key in r.keys("rate_limit:fixed_window:test_user:*"):
        r.delete(key)
    print("Cleanup complete.")

    # Rate limit: 5 requests per 10 seconds for 'test_user'
    fixed_limiter = FixedWindowRateLimiter(r, limit=5, window_seconds=10)
    user_id = "test_user_fixed"

    print(f"\n--- Testing Fixed Window Rate Limiter for user '{user_id}' (5 req/10s) ---")

    # Simulate requests within the window
    for i in range(1, 8):
        allowed = fixed_limiter.allow_request(user_id)
        remaining = fixed_limiter.get_remaining_requests(user_id)
        reset_time = fixed_limiter.get_reset_time(user_id)
        status = "ALLOWED" if allowed else "DENIED"
        print(f"Request {i}: {status} | Remaining: {remaining} | Reset in: {reset_time}s")
        time.sleep(0.5) # Simulate some delay between requests

    print("\n--- Waiting for window to reset (approx 10 seconds) ---")
    time.sleep(fixed_limiter.window_seconds + 1) # Wait slightly more than the window duration

    # Test after window reset
    print("\n--- After window reset ---")
    for i in range(1, 3):
        allowed = fixed_limiter.allow_request(user_id)
        remaining = fixed_limiter.get_remaining_requests(user_id)
        reset_time = fixed_limiter.get_reset_time(user_id)
        status = "ALLOWED" if allowed else "DENIED"
        print(f"Request {i}: {status} | Remaining: {remaining} | Reset in: {reset_time}s")
        time.sleep(0.5)

    # Demonstrate the "burst" problem (if window ends exactly when new requests come)
    print("\n--- Demonstrating potential burst problem (Fixed Window) ---")
    fixed_limiter_burst = FixedWindowRateLimiter(r, limit=2, window_seconds=5)
    user_id_burst = "test_user_burst"

    # Fill up the first window
    print(f"Filling up first window for user '{user_id_burst}' (2 req/5s)")
    fixed_limiter_burst.allow_request(user_id_burst)
    fixed_limiter_burst.allow_request(user_id_burst)
    print(f"Requests made: 2. Remaining: {fixed_limiter_burst.get_remaining_requests(user_id_burst)}")

    # Wait almost until the end of the window
    time.sleep(fixed_limiter_burst.window_seconds - 1)
    print(f"Waiting {fixed_limiter_burst.window_seconds - 1}s...")

    # Make requests at the very end of the current window and beginning of the next
    print("Making requests at window boundary...")
    allowed1 = fixed_limiter_burst.allow_request(user_id_burst) # Should be denied
    allowed2 = fixed_limiter_burst.allow_request(user_id_burst) # Should be allowed (new window)
    allowed3 = fixed_limiter_burst.allow_request(user_id_burst) # Should be allowed (new window)
    print(f"Request at boundary (1): {'ALLOWED' if allowed1 else 'DENIED'}")
    print(f"Request at boundary (2): {'ALLOWED' if allowed2 else 'DENIED'}")
    print(f"Request at boundary (3): {'ALLOWED' if allowed3 else 'DENIED'}")
    # Note: If the sleep is precise, request 1 might be denied, and 2,3 allowed very quickly,
    # showcasing 3 requests in a short span across two windows.
    # The exact behavior depends on the timing of `time.time()` and Redis operations.

4.2 Sliding Window Log Rate Limiter

This algorithm stores timestamps of each request and removes old ones, providing a more accurate rate limit.


import redis
import time
from typing import Optional, Tuple

class SlidingWindowLogRateLimiter:
    """
    Implements a Sliding Window Log rate limiting algorithm using Redis Sorted Sets.

    This algorithm stores a timestamp for each request made by a user.
    When a new request arrives, it first removes all timestamps that are
    older than the current time minus the window duration. Then, it checks
    if the number of remaining timestamps (requests) exceeds the allowed limit.
    If not, it adds the current request's timestamp to the log.

    Pros: Highly accurate, handles bursty traffic well, offers a smoother rate limit
          compared to Fixed Window.
    Cons: Higher memory usage as it stores individual timestamps for each request.
          More complex to implement efficiently.
    """

    def __init__(self, redis_client: redis.Redis, limit: int, window_seconds: int):
        """
        Initializes the SlidingWindowLogRateLimiter.

        Args:
            redis_client: An initialized Redis
gemini Output

This document provides a comprehensive overview and detailed recommendations for implementing and managing an API Rate Limiter. This is a critical component for ensuring the stability, security, and fair usage of your API infrastructure.


API Rate Limiter: Comprehensive Review and Documentation

Executive Summary

An API Rate Limiter is a fundamental mechanism for controlling the rate at which clients can make requests to an API. Its primary purpose is to protect the API from various forms of abuse, ensure fair resource allocation among users, prevent denial-of-service (DoS) attacks, and maintain the stability and performance of the underlying services. This document outlines the core concepts, common algorithms, implementation strategies, and best practices for effectively designing and deploying an API Rate Limiter, culminating in actionable recommendations for your organization.

1. Introduction to API Rate Limiting

1.1 What is API Rate Limiting?

API Rate Limiting is a technique used to restrict the number of API requests a user or client can make within a given timeframe. When a client exceeds the predefined limit, subsequent requests are typically blocked or throttled for a specified duration, often returning an HTTP 429 Too Many Requests status code.

1.2 Why is API Rate Limiting Crucial?

Implementing a robust API Rate Limiter offers several significant benefits:

  • Protection Against Abuse: Safeguards your API from malicious activities such as brute-force attacks, credential stuffing, and scraping.
  • System Stability: Prevents a single user or a small group of users from overwhelming your servers, ensuring consistent performance and availability for all legitimate users.
  • Fair Resource Allocation: Distributes API resources equitably among all consumers, preventing "noisy neighbors" from monopolizing capacity.
  • Cost Control: Reduces operational costs associated with excessive resource consumption (e.g., CPU, memory, network bandwidth, database queries) triggered by uncontrolled API calls.
  • Monetization and Tiering: Enables the creation of different service tiers (e.g., free, premium, enterprise) with varying rate limits, supporting business models.
  • Improved User Experience: By maintaining stability, it ensures a more reliable and predictable experience for compliant users.

2. Core Concepts and Metrics

Understanding the following terms is essential for designing effective rate limiting policies:

  • Rate Limit: The maximum number of requests permitted within a specific time window (e.g., 100 requests per minute).
  • Burst Limit: An allowance for a client to exceed the average rate limit for a short period, often to handle sudden spikes in traffic without immediately hitting the limit.
  • Quota: A total number of requests allowed over a longer period (e.g., 10,000 requests per day/month), often used in conjunction with shorter-term rate limits.
  • Time Window/Interval: The duration over which requests are counted (e.g., 60 seconds for "requests per minute").
  • Throttling: The act of slowing down a client's request rate rather than outright blocking them, often by introducing delays.
  • API Rate Limit Headers: Standard HTTP headers communicated back to the client to inform them about their current rate limit status. These typically include:

* X-RateLimit-Limit: The total number of requests allowed in the current window.

* X-RateLimit-Remaining: The number of requests remaining in the current window.

* X-RateLimit-Reset: The time (usually in UTC epoch seconds) when the current rate limit window resets.

* Retry-After: For blocked requests (HTTP 429), this header indicates how long the client should wait before making another request.

3. Common Rate Limiting Algorithms

Different algorithms offer varying trade-offs in terms of accuracy, fairness, and implementation complexity.

3.1 Fixed Window Counter

  • How it works: Divides time into fixed-size windows (e.g., 1 minute). Each request increments a counter for the current window. If the counter exceeds the limit, requests are blocked until the next window.
  • Pros: Simple to implement, low overhead.
  • Cons: Can lead to "burstiness" at the window edges (e.g., a client making maximum requests at the end of one window and the beginning of the next, effectively doubling the rate in a short period).

3.2 Sliding Window Log

  • How it works: Stores a timestamp for each request made by a client. To check if a request is allowed, it counts all timestamps within the last N seconds/minutes. If the count exceeds the limit, the request is blocked.
  • Pros: Extremely accurate, no "burstiness" at window edges.
  • Cons: High memory consumption, especially for high request volumes, as it needs to store all timestamps. Computationally more intensive.

3.3 Sliding Window Counter

  • How it works: A hybrid approach. It uses two fixed windows: the current window and the previous window. It calculates the allowed requests in the current window based on a weighted average of the previous window's count and the current window's count, proportional to how much of the current window has passed.
  • Pros: Addresses the "burstiness" issue of Fixed Window Counter, more memory efficient than Sliding Window Log.
  • Cons: More complex to implement than Fixed Window, less precise than Sliding Window Log.

3.4 Token Bucket

  • How it works: Clients are given a "bucket" of tokens. Tokens are added to the bucket at a fixed rate. Each request consumes one token. If the bucket is empty, requests are blocked until a new token is available. The bucket has a maximum capacity, allowing for bursts up to that capacity.
  • Pros: Allows for bursts of traffic, very smooth average rate, simple to understand.
  • Cons: Requires careful tuning of bucket size and refill rate.

3.5 Leaky Bucket

  • How it works: Similar to a bucket with a hole in the bottom. Requests are added to the bucket. If the bucket is not full, requests are processed at a constant rate ("leaking" out of the bucket). If the bucket is full, new requests are dropped.
  • Pros: Smooths out traffic spikes, ensures a constant output rate.
  • Cons: Can drop requests during sustained high traffic, doesn't easily allow for bursts beyond the bucket capacity.

4. Implementation Strategies and Considerations

4.1 Where to Implement

  • API Gateway/Edge Proxy (Recommended for most cases): Implementing rate limiting at the edge (e.g., using Nginx, Envoy, AWS API Gateway, Azure API Management, Kong) is highly efficient. It protects your backend services from receiving excessive traffic and can scale independently.
  • Application Layer: Rate limiting can be implemented within your application code. This offers fine-grained control (e.g., per-endpoint, per-user logic) but adds overhead to your application servers and requires careful distribution if your application is horizontally scaled.
  • Load Balancer: Some load balancers offer basic rate limiting capabilities.

4.2 Granularity of Rate Limiting

Rate limits should be applied at an appropriate granularity based on your use case:

  • Per IP Address: Simple but can be problematic for users behind NATs or proxies, or for mobile carriers.
  • Per User/API Key/Client ID: Most common and effective for authenticated users, allowing different tiers. Requires authentication to occur before rate limiting.
  • Per Endpoint: Allows for different limits on different API resources (e.g., heavier limits on data-modifying operations vs. read-only lookups).
  • Per Geographic Region: Useful for localized services or to mitigate attacks from specific regions.

4.3 Distributed vs. Single Node

  • Single Node: Suitable for small applications running on a single server. Rate limit state is held in memory.
  • Distributed (Recommended for scalable applications): For microservices and scaled applications, rate limit state must be shared across all instances. This typically involves using a fast, distributed data store like Redis. Redis's atomic operations and speed make it ideal for managing counters and timestamps.

4.4 Data Storage for Counters

  • In-Memory: Fastest, but data is lost on restart and not shared across instances. Only for single-node deployments.
  • Redis: Highly recommended for distributed systems. Offers excellent performance, persistence options, and atomic operations (e.g., INCR, EXPIRE) critical for rate limiting.
  • Database: Generally too slow for high-volume rate limiting due to I/O latency. Could be used for very long-term quotas (e.g., monthly).

4.5 Error Handling and Client Communication

  • HTTP 429 Too Many Requests: This is the standard HTTP status code for rate-limited responses.
  • Informative Headers: Always include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset in your responses (even for successful requests) to enable clients to self-regulate.
  • Retry-After Header: For 429 responses, provide the Retry-After header to guide clients on when they can safely retry.
  • Clear Documentation: Provide comprehensive client-side documentation explaining your rate limiting policies and how clients should handle 429 responses.

5. Best Practices and Design Principles

  • Transparency: Clearly document your rate limiting policies for API consumers.
  • Configurability: Make rate limits easily configurable without code changes. This allows for quick adjustments based on traffic patterns, incidents, or business needs.
  • Monitoring and Alerting: Implement robust monitoring to track rate limit hits, identify potential abuse, and alert operations teams to unusual patterns.
  • Graceful Degradation: Instead of hard blocking, consider throttling or returning cached data for lower-priority requests when nearing limits.
  • Tiered Rate Limiting: Implement different rate limits for different types of users (e.g., free tier, paid tier, internal applications).
  • Dynamic Adjustments: Consider implementing logic that can dynamically adjust limits based on overall system load.
  • Exemptions: Allow for specific IPs, internal services, or critical partners to bypass or have higher limits, but manage these exemptions carefully.
  • Testing: Thoroughly test your rate limiter under various load conditions to ensure it performs as expected and doesn't introduce bottlenecks.
  • Consistency: Ensure rate limits are consistently applied across all relevant API endpoints and services.

6. Actionable Recommendations for Implementation

To effectively implement or enhance your API Rate Limiter, consider the following steps:

  1. Define Rate Limiting Policies:

* Identify Critical Endpoints: Determine which API endpoints are most vulnerable to abuse or resource-intensive.

* Establish Baseline Limits: Define initial rate limits (e.g., requests per minute, requests per hour) based on expected usage patterns and system capacity.

* Consider Granularity: Decide whether to apply limits per IP, per user/API key, or per endpoint. Start with per-user/API key for authenticated APIs.

* Define Burst Tolerance: Determine if a burst limit is necessary and its appropriate size.

  1. Choose Appropriate Algorithm(s):

* For general-purpose rate limiting, a Token Bucket (for burst tolerance and smooth average rate) or Sliding Window Counter (for better accuracy than fixed window with reasonable memory) are often good starting points.

* If extreme accuracy and no edge-case bursts are critical, and memory is not a constraint, Sliding Window Log could be considered.

  1. Select Implementation Layer:

* Recommendation: Prioritize implementation at the API Gateway or Edge Proxy layer (e.g., Nginx with Lua, Kong, Envoy, AWS API Gateway) using a distributed caching system like Redis for state management. This offloads the work from your application servers and provides a centralized control point.

* For very specific, fine-grained, or complex business logic-driven rate limits, consider a secondary, complementary rate limiter within the application layer.

  1. Implement and Test:

* Proof of Concept: Develop a small proof-of-concept for your chosen algorithm and implementation layer.

* Integration: Integrate the rate limiter into your existing infrastructure.

* Thorough Testing: Conduct load testing and abuse testing to validate that the rate limiter performs as expected under stress and correctly blocks/throttles requests. Verify HTTP 429 responses and header accuracy.

  1. Monitor and Iterate:

* Set up Monitoring: Implement dashboards and alerts for key rate limiting metrics (see Section 7).

* Analyze Data: Regularly review rate limiting logs and metrics to identify usage patterns, potential abuse, and areas for policy refinement.

* Adjust Policies: Be prepared to iterate on your rate limiting policies based on real-world usage and performance data.

7. Monitoring and Alerting

Effective monitoring is crucial for a successful rate limiting strategy.

7.1 Key Metrics to Track

  • Total API Requests: Overall traffic volume.
  • Rate Limit Hits (429 Responses): The absolute number and percentage of requests that are blocked due to rate limits.
  • Rate Limit Remaining: For individual clients, the number of requests they have left in their window.
  • Top Rate-Limited Clients: Identify which clients or IP addresses are most frequently hitting limits.
  • System Load: Monitor CPU, memory, and network usage of your API gateway and backend services to correlate with rate limit activity.
  • Latency: Track the latency of both rate-limited and successful requests.

7.2 Alerting Scenarios

  • Spike in 429 Responses: Alert if the percentage or absolute number of 429 responses suddenly spikes, indicating potential abuse or a misconfigured limit.
  • High Usage for Specific Clients: Alert if a particular client consistently operates near their rate limit, which might indicate a need for a higher tier or a review of their usage pattern.
  • Rate Limiter Service Health: Ensure the rate limiting service itself (e.g., Redis instance, API Gateway component) is healthy and responsive.

8. Conclusion and Next Steps

Implementing a well-designed API Rate Limiter is not just a defensive measure; it's a strategic component that safeguards your infrastructure, optimizes resource utilization, and enables a reliable experience for your API consumers. By following the recommendations outlined in this document, your organization can establish a robust and scalable rate limiting solution.

Next Steps:

  1. Internal Workshop: Convene a meeting with relevant stakeholders (development, operations, product management) to discuss and finalize initial rate limiting policies.
  2. Technology Selection: Confirm the specific technologies for implementation (e.g., API Gateway solution, Redis deployment strategy).
  3. Design & Architecture Review: Develop a detailed design document based on the chosen algorithm and implementation layer, followed by an architectural review.
  4. Phased Rollout: Plan for a phased rollout of
api_rate_limiter.txt
Download source file
Copy all content
Full output as text
Download ZIP
IDE-ready project ZIP
Copy share link
Permanent URL for this run
Get Embed Code
Embed this result on any website
Print / Save PDF
Use browser print dialog
\n\n\n"); var hasSrcMain=Object.keys(extracted).some(function(k){return k.indexOf("src/main")>=0;}); if(!hasSrcMain) zip.file(folder+"src/main."+ext,"import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport App from './App'\nimport './index.css'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n \n \n \n)\n"); var hasSrcApp=Object.keys(extracted).some(function(k){return k==="src/App."+ext||k==="App."+ext;}); if(!hasSrcApp) zip.file(folder+"src/App."+ext,"import React from 'react'\nimport './App.css'\n\nfunction App(){\n return(\n
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n
\n )\n}\nexport default App\n"); zip.file(folder+"src/index.css","*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#f0f2f5;color:#1a1a2e}\n.app{min-height:100vh;display:flex;flex-direction:column}\n.app-header{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:40px}\nh1{font-size:2.5rem;font-weight:700}\n"); zip.file(folder+"src/App.css",""); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/pages/.gitkeep",""); zip.file(folder+"src/hooks/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\n## Open in IDE\nOpen the project folder in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Vue (Vite + Composition API + TypeScript) --- */ function buildVue(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "type": "module",\n "scripts": {\n "dev": "vite",\n "build": "vue-tsc -b && vite build",\n "preview": "vite preview"\n },\n "dependencies": {\n "vue": "^3.5.13",\n "vue-router": "^4.4.5",\n "pinia": "^2.3.0",\n "axios": "^1.7.9"\n },\n "devDependencies": {\n "@vitejs/plugin-vue": "^5.2.1",\n "typescript": "~5.7.3",\n "vite": "^6.0.5",\n "vue-tsc": "^2.2.0"\n }\n}\n'); zip.file(folder+"vite.config.ts","import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport { resolve } from 'path'\n\nexport default defineConfig({\n plugins: [vue()],\n resolve: { alias: { '@': resolve(__dirname,'src') } }\n})\n"); zip.file(folder+"tsconfig.json",'{"files":[],"references":[{"path":"./tsconfig.app.json"},{"path":"./tsconfig.node.json"}]}\n'); zip.file(folder+"tsconfig.app.json",'{\n "compilerOptions":{\n "target":"ES2020","useDefineForClassFields":true,"module":"ESNext","lib":["ES2020","DOM","DOM.Iterable"],\n "skipLibCheck":true,"moduleResolution":"bundler","allowImportingTsExtensions":true,\n "isolatedModules":true,"moduleDetection":"force","noEmit":true,"jsxImportSource":"vue",\n "strict":true,"paths":{"@/*":["./src/*"]}\n },\n "include":["src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue"]\n}\n'); zip.file(folder+"env.d.ts","/// \n"); zip.file(folder+"index.html","\n\n\n \n \n "+slugTitle(pn)+"\n\n\n
\n \n\n\n"); var hasMain=Object.keys(extracted).some(function(k){return k==="src/main.ts"||k==="main.ts";}); if(!hasMain) zip.file(folder+"src/main.ts","import { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from './App.vue'\nimport './assets/main.css'\n\nconst app = createApp(App)\napp.use(createPinia())\napp.mount('#app')\n"); var hasApp=Object.keys(extracted).some(function(k){return k.indexOf("App.vue")>=0;}); if(!hasApp) zip.file(folder+"src/App.vue","\n\n\n\n\n"); zip.file(folder+"src/assets/main.css","*{margin:0;padding:0;box-sizing:border-box}body{font-family:system-ui,sans-serif;background:#fff;color:#213547}\n"); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/views/.gitkeep",""); zip.file(folder+"src/stores/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\nOpen in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Angular (v19 standalone) --- */ function buildAngular(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var sel=pn.replace(/_/g,"-"); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "scripts": {\n "ng": "ng",\n "start": "ng serve",\n "build": "ng build",\n "test": "ng test"\n },\n "dependencies": {\n "@angular/animations": "^19.0.0",\n "@angular/common": "^19.0.0",\n "@angular/compiler": "^19.0.0",\n "@angular/core": "^19.0.0",\n "@angular/forms": "^19.0.0",\n "@angular/platform-browser": "^19.0.0",\n "@angular/platform-browser-dynamic": "^19.0.0",\n "@angular/router": "^19.0.0",\n "rxjs": "~7.8.0",\n "tslib": "^2.3.0",\n "zone.js": "~0.15.0"\n },\n "devDependencies": {\n "@angular-devkit/build-angular": "^19.0.0",\n "@angular/cli": "^19.0.0",\n "@angular/compiler-cli": "^19.0.0",\n "typescript": "~5.6.0"\n }\n}\n'); zip.file(folder+"angular.json",'{\n "$schema": "./node_modules/@angular/cli/lib/config/schema.json",\n "version": 1,\n "newProjectRoot": "projects",\n "projects": {\n "'+pn+'": {\n "projectType": "application",\n "root": "",\n "sourceRoot": "src",\n "prefix": "app",\n "architect": {\n "build": {\n "builder": "@angular-devkit/build-angular:application",\n "options": {\n "outputPath": "dist/'+pn+'",\n "index": "src/index.html",\n "browser": "src/main.ts",\n "tsConfig": "tsconfig.app.json",\n "styles": ["src/styles.css"],\n "scripts": []\n }\n },\n "serve": {"builder":"@angular-devkit/build-angular:dev-server","configurations":{"production":{"buildTarget":"'+pn+':build:production"},"development":{"buildTarget":"'+pn+':build:development"}},"defaultConfiguration":"development"}\n }\n }\n }\n}\n'); zip.file(folder+"tsconfig.json",'{\n "compileOnSave": false,\n "compilerOptions": {"baseUrl":"./","outDir":"./dist/out-tsc","forceConsistentCasingInFileNames":true,"strict":true,"noImplicitOverride":true,"noPropertyAccessFromIndexSignature":true,"noImplicitReturns":true,"noFallthroughCasesInSwitch":true,"paths":{"@/*":["src/*"]},"skipLibCheck":true,"esModuleInterop":true,"sourceMap":true,"declaration":false,"experimentalDecorators":true,"moduleResolution":"bundler","importHelpers":true,"target":"ES2022","module":"ES2022","useDefineForClassFields":false,"lib":["ES2022","dom"]},\n "references":[{"path":"./tsconfig.app.json"}]\n}\n'); zip.file(folder+"tsconfig.app.json",'{\n "extends":"./tsconfig.json",\n "compilerOptions":{"outDir":"./dist/out-tsc","types":[]},\n "files":["src/main.ts"],\n "include":["src/**/*.d.ts"]\n}\n'); zip.file(folder+"src/index.html","\n\n\n \n "+slugTitle(pn)+"\n \n \n \n\n\n \n\n\n"); zip.file(folder+"src/main.ts","import { bootstrapApplication } from '@angular/platform-browser';\nimport { appConfig } from './app/app.config';\nimport { AppComponent } from './app/app.component';\n\nbootstrapApplication(AppComponent, appConfig)\n .catch(err => console.error(err));\n"); zip.file(folder+"src/styles.css","* { margin: 0; padding: 0; box-sizing: border-box; }\nbody { font-family: system-ui, -apple-system, sans-serif; background: #f9fafb; color: #111827; }\n"); var hasComp=Object.keys(extracted).some(function(k){return k.indexOf("app.component")>=0;}); if(!hasComp){ zip.file(folder+"src/app/app.component.ts","import { Component } from '@angular/core';\nimport { RouterOutlet } from '@angular/router';\n\n@Component({\n selector: 'app-root',\n standalone: true,\n imports: [RouterOutlet],\n templateUrl: './app.component.html',\n styleUrl: './app.component.css'\n})\nexport class AppComponent {\n title = '"+pn+"';\n}\n"); zip.file(folder+"src/app/app.component.html","
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n \n
\n"); zip.file(folder+"src/app/app.component.css",".app-header{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:60vh;gap:16px}h1{font-size:2.5rem;font-weight:700;color:#6366f1}\n"); } zip.file(folder+"src/app/app.config.ts","import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';\nimport { provideRouter } from '@angular/router';\nimport { routes } from './app.routes';\n\nexport const appConfig: ApplicationConfig = {\n providers: [\n provideZoneChangeDetection({ eventCoalescing: true }),\n provideRouter(routes)\n ]\n};\n"); zip.file(folder+"src/app/app.routes.ts","import { Routes } from '@angular/router';\n\nexport const routes: Routes = [];\n"); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nng serve\n# or: npm start\n\`\`\`\n\n## Build\n\`\`\`bash\nng build\n\`\`\`\n\nOpen in VS Code with Angular Language Service extension.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n.angular/\n"); } /* --- Python --- */ function buildPython(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var reqMap={"numpy":"numpy","pandas":"pandas","sklearn":"scikit-learn","tensorflow":"tensorflow","torch":"torch","flask":"flask","fastapi":"fastapi","uvicorn":"uvicorn","requests":"requests","sqlalchemy":"sqlalchemy","pydantic":"pydantic","dotenv":"python-dotenv","PIL":"Pillow","cv2":"opencv-python","matplotlib":"matplotlib","seaborn":"seaborn","scipy":"scipy"}; var reqs=[]; Object.keys(reqMap).forEach(function(k){if(src.indexOf("import "+k)>=0||src.indexOf("from "+k)>=0)reqs.push(reqMap[k]);}); var reqsTxt=reqs.length?reqs.join("\n"):"# add dependencies here\n"; zip.file(folder+"main.py",src||"# "+title+"\n# Generated by PantheraHive BOS\n\nprint(title+\" loaded\")\n"); zip.file(folder+"requirements.txt",reqsTxt); zip.file(folder+".env.example","# Environment variables\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt\n\`\`\`\n\n## Run\n\`\`\`bash\npython main.py\n\`\`\`\n"); zip.file(folder+".gitignore",".venv/\n__pycache__/\n*.pyc\n.env\n.DS_Store\n"); } /* --- Node.js --- */ function buildNode(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var depMap={"mongoose":"^8.0.0","dotenv":"^16.4.5","axios":"^1.7.9","cors":"^2.8.5","bcryptjs":"^2.4.3","jsonwebtoken":"^9.0.2","socket.io":"^4.7.4","uuid":"^9.0.1","zod":"^3.22.4","express":"^4.18.2"}; var deps={}; Object.keys(depMap).forEach(function(k){if(src.indexOf(k)>=0)deps[k]=depMap[k];}); if(!deps["express"])deps["express"]="^4.18.2"; var pkgJson=JSON.stringify({"name":pn,"version":"1.0.0","main":"src/index.js","scripts":{"start":"node src/index.js","dev":"nodemon src/index.js"},"dependencies":deps,"devDependencies":{"nodemon":"^3.0.3"}},null,2)+"\n"; zip.file(folder+"package.json",pkgJson); var fallback="const express=require(\"express\");\nconst app=express();\napp.use(express.json());\n\napp.get(\"/\",(req,res)=>{\n res.json({message:\""+title+" API\"});\n});\n\nconst PORT=process.env.PORT||3000;\napp.listen(PORT,()=>console.log(\"Server on port \"+PORT));\n"; zip.file(folder+"src/index.js",src||fallback); zip.file(folder+".env.example","PORT=3000\n"); zip.file(folder+".gitignore","node_modules/\n.env\n.DS_Store\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\n\`\`\`\n\n## Run\n\`\`\`bash\nnpm run dev\n\`\`\`\n"); } /* --- Vanilla HTML --- */ function buildVanillaHtml(zip,folder,app,code){ var title=slugTitle(app); var isFullDoc=code.trim().toLowerCase().indexOf("=0||code.trim().toLowerCase().indexOf("=0; var indexHtml=isFullDoc?code:"\n\n\n\n\n"+title+"\n\n\n\n"+code+"\n\n\n\n"; zip.file(folder+"index.html",indexHtml); zip.file(folder+"style.css","/* "+title+" — styles */\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#fff;color:#1a1a2e}\n"); zip.file(folder+"script.js","/* "+title+" — scripts */\n"); zip.file(folder+"assets/.gitkeep",""); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Open\nDouble-click \`index.html\` in your browser.\n\nOr serve locally:\n\`\`\`bash\nnpx serve .\n# or\npython3 -m http.server 3000\n\`\`\`\n"); zip.file(folder+".gitignore",".DS_Store\nnode_modules/\n.env\n"); } /* ===== MAIN ===== */ var sc=document.createElement("script"); sc.src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"; sc.onerror=function(){ if(lbl)lbl.textContent="Download ZIP"; alert("JSZip load failed — check connection."); }; sc.onload=function(){ var zip=new JSZip(); var base=(_phFname||"output").replace(/\.[^.]+$/,""); var app=base.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"my_app"; var folder=app+"/"; var vc=document.getElementById("panel-content"); var panelTxt=vc?(vc.innerText||vc.textContent||""):""; var lang=detectLang(_phCode,panelTxt); if(_phIsHtml){ buildVanillaHtml(zip,folder,app,_phCode); } else if(lang==="flutter"){ buildFlutter(zip,folder,app,_phCode,panelTxt); } else if(lang==="react-native"){ buildReactNative(zip,folder,app,_phCode,panelTxt); } else if(lang==="swift"){ buildSwift(zip,folder,app,_phCode,panelTxt); } else if(lang==="kotlin"){ buildKotlin(zip,folder,app,_phCode,panelTxt); } else if(lang==="react"){ buildReact(zip,folder,app,_phCode,panelTxt); } else if(lang==="vue"){ buildVue(zip,folder,app,_phCode,panelTxt); } else if(lang==="angular"){ buildAngular(zip,folder,app,_phCode,panelTxt); } else if(lang==="python"){ buildPython(zip,folder,app,_phCode); } else if(lang==="node"){ buildNode(zip,folder,app,_phCode); } else { /* Document/content workflow */ var title=app.replace(/_/g," "); var md=_phAll||_phCode||panelTxt||"No content"; zip.file(folder+app+".md",md); var h=""+title+""; h+="

"+title+"

"; var hc=md.replace(/&/g,"&").replace(//g,">"); hc=hc.replace(/^### (.+)$/gm,"

$1

"); hc=hc.replace(/^## (.+)$/gm,"

$1

"); hc=hc.replace(/^# (.+)$/gm,"

$1

"); hc=hc.replace(/\*\*(.+?)\*\*/g,"$1"); hc=hc.replace(/\n{2,}/g,"

"); h+="

"+hc+"

Generated by PantheraHive BOS
"; zip.file(folder+app+".html",h); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\nFiles:\n- "+app+".md (Markdown)\n- "+app+".html (styled HTML)\n"); } zip.generateAsync({type:"blob"}).then(function(blob){ var a=document.createElement("a"); a.href=URL.createObjectURL(blob); a.download=app+".zip"; a.click(); URL.revokeObjectURL(a.href); if(lbl)lbl.textContent="Download ZIP"; }); }; document.head.appendChild(sc); } function phShare(){navigator.clipboard.writeText(window.location.href).then(function(){var el=document.getElementById("ph-share-lbl");if(el){el.textContent="Link copied!";setTimeout(function(){el.textContent="Copy share link";},2500);}});}function phEmbed(){var runId=window.location.pathname.split("/").pop().replace(".html","");var embedUrl="https://pantherahive.com/embed/"+runId;var code='';navigator.clipboard.writeText(code).then(function(){var el=document.getElementById("ph-embed-lbl");if(el){el.textContent="Embed code copied!";setTimeout(function(){el.textContent="Get Embed Code";},2500);}});}