Search code examples
pythonfastapirate-limitingrate

Python FastAPI error when I test my endpoint on POSTMAN


I am trying to create a rate limiting for my API, I am using this approach. But when I run the endpoint on POSTMAN, it does not work. It gives me Internal Server Error

TypeError: RateLimiterMiddleware.__call__() got an unexpected keyword argument 'app' INFO: 127.0.0.1:53544 - "POST /battery HTTP/1.1" 500 Internal Server Error

The error above is what I am getting and I only get this error when I test the endpoint on POSTMAN

I have tried everything I can but it is not working. I have a middleware.py, another file for eeping request counts and my main.py where I have all my endpoints.

This is my middleware.py where the function I am having the issues is

from fastapi import FastAPI, Request, HTTPException
from typing import Callable, Any
from request_counter import RequestCounter
from starlette.requests import Request
import time


class RateLimiterMiddleware:
    def __init__(self, app: FastAPI, max_requests: int, window_seconds: int, request_counter: RequestCounter):
        self.app = app
        self.request_counter = request_counter
        self.max_requests = max_requests
        self.window_seconds = window_seconds

    async def __call__(self, request: Request, call_next: Callable[[Request], Any]):
        client_ip = request.client.host
        current_time = time.time()

        request_count = self.request_counter.check_rate_limit(
            client_ip, current_time, self.window_seconds, self.max_requests)
        if request_count >= self.max_requests:
            raise HTTPException(status_code=429, detail="Too Many Requests")

        self.request_counter.increase_request_count(
            client_ip, current_time, self.window_seconds)
        return await call_next(request)

This below is where I am keeping request count

from fastapi import HTTPException


class RequestCounter:
    def __init__(self):
        self.request_counts = {}

    def increase_request_count(self, client_ip, current_time, window_seconds):
        timestamp = int(current_time - window_seconds)

        if client_ip not in self.request_counts:
            self.request_counts[client_ip] = {}

        if timestamp not in self.request_counts:
            self.request_counts[timestamp] = 0

        self.request_counts[client_ip][timestamp] += 1

    def check_rate_limit(self, client_ip, current_time, window_seconds, max_requests):
        timestamp = int(current_time - window_seconds)

        if client_ip not in self.request_counts or timestamp not in self.request_counts[client_ip]:
            return 0

        request_count = self.request_counts[client_ip][timestamp]
        if request_count >= max_requests:
            raise HTTPException(status_code=429, detail="Too Many Requests")

        return request_count


This is my main.py


from config import init_firebase
from firebase_admin import firestore
from fastapi import FastAPI, HTTPException
from models import Battery
from typing import List
from middleware import RateLimiterMiddleware
from request_counter import RequestCounter


init_firebase()

app = FastAPI()

db = firestore.client()


request_counter = RequestCounter()
rate_limiter_middleware = RateLimiterMiddleware(
    app, max_requests=10, window_seconds=60, request_counter=request_counter)

app.add_middleware(rate_limiter_middleware)


@app.post("/battery/", response_model=Battery)
async def create_battery(battery: Battery):
    try:
        doc_ref = db.collection("batteries").document()
        doc_ref.set(battery.model_dump())
        return {**battery.model_dump(), "id": doc_ref.id}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@app.get("/batteries/", response_model=List[Battery])
async def get_batteries():
    try:
        ref = db.collection("batteries")
        batteries = ref.stream()

        battery_list = []

        for battery in batteries:
            data = battery.to_dict()
            battery_list.append(data)
        return battery_list
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


Solution

  • FastAPI.add_middleware() is intherited from Starlette which accepts it's own middleware classes. You need to use app.middleware("http")(rate_limiter_middleware) instead (FastAPI's middleware decorator) to make it work