pythonfastapimiddlewarerate-limitingslowapi

How to apply a global rate limit for all routes using SlowAPI and FastAPI?


I want to configure rate limiting with SlowAPI (in-memory, without Redis Cache etc.), but I don't want to add the @limiter.limit() decorator seperately for every endpoint.

So, something I don't want to do manually would be:

@limiter.limit("5/minute")
async def myendpoint(request: Request)
    pass

So, essentially, I want to include it in a middleware:

from slowapi import Limiter

limiter = Limiter(key_func=get_remote_address)

@app.middleware("http")
async def check_request(request: Request, call_next):
    client_ip = request.client.host
    prefix = "request_rate_limiter." + client_ip

    #... (logic from slowapi to get allowed flag)
    if allowed:
        response = await call_next(request)
    return response

But I didn't find a solution on how to receive a boolean from the limiter.

How would I proceed from this and would this work?

It would be great if I could configure it for different routes and also for example depending on a user subscription (e.g. free/premium).

Thanks!


Solution

  • To apply a global (default) limit to all routes, you could use the SlowAPIMiddleware, as shown in the example below. The relevant documentation could be found here.

    Related answers could also be found here and here.

    Example

    from fastapi import FastAPI
    from slowapi import Limiter, _rate_limit_exceeded_handler
    from slowapi.util import get_remote_address
    from slowapi.middleware import SlowAPIMiddleware
    from slowapi.errors import RateLimitExceeded
    
    limiter = Limiter(key_func=get_remote_address, default_limits=["1/minute"])
    app = FastAPI()
    app.state.limiter = limiter
    app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
    app.add_middleware(SlowAPIMiddleware)
    
    
    @app.get("/")
    async def main():
        return "Only once per minute"
    

    To exempt a route from the global limit, you could use the @limiter.exempt decorator on a given route, as shown in the following example. However, it seems that, currently, the decorator would only work for endpoints defined with normal def instead of async def (it could be a bug in the relevant implementation of SlowAPI—UPDATE: The issue has been fixed, so please make sure to upgrade to the latest version of SlowAPI).

    @app.get("/someroute")
    @limiter.exempt
    async def someroute():
        return "I'm unlimited"