laurentS / slowapi

A rate limiter for Starlette and FastAPI
https://pypi.org/project/slowapi/
MIT License
1.21k stars 76 forks source link

Endpoint with limiter does not show 429 as a possible answer on the documentation page #74

Open AntonGsv opened 2 years ago

AntonGsv commented 2 years ago

Endpoint with limiter does not show 429 as a possible answer on the documentation page

twcurrie commented 2 years ago

To elaborate, the generated OpenAPI docs available for the service at /docs (or /redocs?) do not show that 429 is a possible response for an endpoint with the rate limiter configured?

AntonGsv commented 2 years ago

@twcurrie Yes, right now the decorator has no effect on OpenAPI generation.

from fastapi import FastAPI,  Request, Response
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

limiter = Limiter(key_func=get_remote_address)
app = FastAPI()
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

@app.get("/mars")
@limiter.limit("5/minute")
async def homepage(request: Request, response: Response):
    return {"key": "value"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", host="127.0.0.1", reload=False, port=int("8000"))

I have to specify the error manually to get the desired result:

from fastapi import FastAPI, Request, Response
from pydantic import BaseModel
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

limiter = Limiter(key_func=get_remote_address)
app = FastAPI()
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

class HTTPException429(BaseModel):
    error: str = "Rate limit exceeded: X per Y minute"

@app.get("/mars", responses={429: {"model": HTTPException429}})
@limiter.limit("5/minute")
async def homepage(request: Request, response: Response):
    return {"key": "value"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", host="127.0.0.1", reload=False, port=int("8000"))

Since the decorator before app.get does not work is there an alternative way to automatically generate 429 response code for OpenAPI?

twcurrie commented 2 years ago

I'd need to review the internals of FastAPI's docs generation to identify a way to hook into that process, but I agree, that should be surfaced within the OpenAPI documentation.