RateLimiter Class
Sliding window rate limiter for API protection and abuse prevention. Prevent brute force attacks and ensure fair resource usage.
RateLimiter(key, limit, window_seconds)
Creates a new rate limiter instance for the given key.
key(String) - Rate limit key (e.g., "ip:192.168.1.1" or "user:123")limit(Int) - Maximum requests allowed in the windowwindow_seconds(Int) - Time window in seconds
# Create a rate limiter for API access
let limiter = RateLimiter("api:user123", 100, 60)
limiter.allowed()
Checks if a request is allowed under the rate limit using sliding window algorithm.
# Limit API calls to 100 per minute per IP
let limiter = RateLimiter("ip:" + req["headers"]["X-Forwarded-For"], 100, 60)
if !limiter.allowed()
return { "status": 429, "body": "Too Many Requests" }
end
limiter.throttle()
Returns the number of seconds until the next request is allowed.
limiter.status()
Gets detailed rate limit status with remaining requests and reset time.
allowed, remaining, reset_in, limit, window
let limiter = RateLimiter("api:" + api_key, 1000, 3600)
let status = limiter.status
println("Remaining: " + str(status["remaining"]) + "/" + str(status["limit"]))
limiter.headers()
Generates rate limit headers for HTTP responses.
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
limiter.reset()
Resets the rate limit for this instance's key.
Static Methods
RateLimiter.reset_all()
Resets all rate limit counters.
RateLimiter.cleanup()
Cleans up expired rate limit entries.
Helper Functions
rate_limiter_from_ip(req, limit, window_seconds?)
Creates a RateLimiter instance keyed on the client IP address. window_seconds is optional and defaults to 60.
The address comes from the TCP peer (remote_addr). X-Forwarded-For is consulted only when enable_trust_proxy() / SOLI_TRUST_PROXY is on — on a directly-exposed app the client controls that header, so trusting it would let a rotating value defeat the limiter entirely.
The bucket key is the address alone (ip:<addr>), with no per-route component, so two call sites with the same limit share one budget. The scaffolded auth controllers rely on this: AUTH_ATTEMPTS_PER_IP is the total across sign-in, sign-up and reset rather than that many each. For a separate budget per action, build the key yourself with RateLimiter("login:" + key, limit, window).
Complete Example
# API middleware with rate limiting
def rate_limit_middleware
let ip = req["headers"]["X-Forwarded-For"] || req["headers"]["Remote-Addr"]
let limiter = RateLimiter("api:" + ip, 100, 60)
# Check rate limit (100 requests per minute)
if !limiter.allowed()
let status = limiter.status
let headers = limiter.headers
return {
"continue": false,
"response": {
"status": 429,
"headers": headers,
"body": json_stringify({
"error": "Rate limit exceeded",
"retry_after": status["reset_in"]
})
}
}
end
{ "continue": true, "request": req }
end