A rate limiter for Starlette and FastAPI

Overview

SlowApi

A rate limiting library for Starlette and FastAPI adapted from flask-limiter.

Note: this is alpha quality code still, the API may change, and things may fall apart while you try it.

The documentation is on read the docs.

Quick start

Installation

slowapi is available from pypi so you can install it as usual:

$ pip install slowapi

Features

Most feature are coming from (will come from) FlaskLimiter and the underlying limits.

Supported now:

  • Single and multiple limit decorator on endpoint functions to apply limits
  • redis, memcached and memory backends to track your limits (memory as a fallback)
  • support for sync and async HTTP endpoints
  • Support for shared limits across a set of routes

Limitations and known issues

  • The request argument must be explicitly passed to your endpoint, or slowapi won't be able to hook into it. In other words, write:
    @limiter.limit("5/minute")
    async def myendpoint(request: Request)
        pass

and not:

    @limiter.limit("5/minute")
    async def myendpoint()
        pass
  • websocket endpoints are not supported yet.

Developing and contributing

PRs are more than welcome! Please include tests for your changes :)

The package uses poetry to manage dependencies. To setup your dev env:

$ poetry install

To run the tests:

$ pytest

Credits

Credits go to flask-limiter of which SlowApi is a (still partial) adaptation to Starlette and FastAPI. It's also important to mention that the actual rate limiting work is done by limits, slowapi is just a wrapper around it.

Comments
  • new: :sparkles: add ASGI middleware alternative

    new: :sparkles: add ASGI middleware alternative

    fix https://github.com/laurentS/slowapi/issues/98

    Might be easier for people reviewing to explain the different commits:

    • first commit adds the new ASGI middleware
    • second one update tests so that it test both the HTTP and ASGI middlewares (using pytest parametrize and fixtures)
    • third one is a refactoring commit, to avoid duplicating too much code between the HTTP and the ASGI middleware

    Might be easier to first review the first two commits, then the refactoring one (to avoid seeing too many changes at once)

    Let me know what do you think about this :smile:

    Note: maybe I could do a bit of refactoring between _inject_headers and _inject_asgi_headers as well ?

    opened by thentgesMindee 13
  • Limit rate issue

    Limit rate issue

    I've tested limit rate locally and it works fine.

    After I deployed application on AWS, rates did't work at all until I set redis as storage.

    But even with redis, rate limit seems to be broken. Limit is exceeded after ~10th attempt, and I've set limit to 5.

    I've checked redis value for the key inserted by limiter, and I think it did not count every attempt.

    I'm using FastAPI 0.45.0 and slowapi 0.1.1

    opened by HereComesTheRainAgain 12
  • Move to ASGI middleware implementation?

    Move to ASGI middleware implementation?

    There's been an uptick in issues (1, 2, 3,etc.) around Starlette's BaseHTTPMiddleware class, so much so that some are proposing to deprecate it.

    Should we shift the implementation to be a completely ASGI-based implementation?

    opened by twcurrie 8
  • Documentation?

    Documentation?

    Hi, I was looking for a way to rate limit my production FastAPI backend and I stumbled upon this repo, which seems to meet my needs, but lacks documentation. I was able to grasp some stuff from the examples and by reading the source code, but is there any chance to find proper documentation for this project?

    Best Regards, and thanks for making this!

    opened by nocturn9x 8
  • Dynamic rate limit based on user type

    Dynamic rate limit based on user type

    I need a way to dynamically set the rate limit based on the user type.

    For example, I want to limit users without access token & have unlimited access to users with the access token.

    What I am currently using:

    
    limiter = Limiter(key_func=identify_my_user_func)
    app.state.limiter = limiter
    app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
    
    def identify_my_user_func(request: Request):
        if 'access_token' not in request.query_params:
            return request.client.host
        return "REGISTERED_USER"
    
    @limiter.limit("2/minute")
    def some_request(request: Request)):
         return data
    
    

    I am trying to find a way to conditionally limit 2/minute. Basically I want to increase the limit based on the user type.

    opened by sdklab007 7
  • Using slowapi in bigger application

    Using slowapi in bigger application

    In test.py from app is run:

    import uvicorn
    
    from app.main import app
    
    if __name__ == "__main__":
        uvicorn.run("test:app", host="0.0.0.0", port=8000, reload=True)
    
    

    In main.py

    from app.api.api_v1.api import router as api_router
    from fastapi import FastAPI
    from slowapi import Limiter, _rate_limit_exceeded_handler
    from slowapi.util import get_remote_address
    from slowapi.errors import RateLimitExceeded
    def get_application() -> FastAPI:
         application = FastAPI(title=PROJECT_NAME, debug=DEBUG, version=VERSION)
         application.add_event_handler(
            "startup", create_start_app_handler(application))
         application.add_event_handler(
            "shutdown", create_stop_app_handler(application))
        application.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
        application.include_router(api_router, prefix=API_PREFIX)
    
         return application
    app = get_application()
    

    In endpoint user.py, how to use @limiter.limit("5/minute") ?

    from starlette.requests import Request
    router = APIRouter()
    @router.post('/show',
                 tags=["show"],
                 name="show:show")
    async def homepage(request: Request):
            return PlainTextResponse("test")
    ***code****
    

    In this use case how to use slowapi in endpoint. I need to limit to 5 requests per minute per user only and block that user for a minute Sleep and retry after a minute

    In api.py

    from app.api.api_v1.endpoints.show import router as ShowRouter
    
    router = APIRouter()
    
    
    
    router.include_router(ShowRouter, tags=["show"], prefix="/show")
    opened by himalacharya 6
  • add redis as extra package

    add redis as extra package

    So Redis is not directly use in slowapi but is used by the dependency of it, limiter and is downgrading redis package ( this project has older version of it) which is already used in our project.

    So we can add redis as a extra package.

    This PR is for that @laurentS

    opened by ME-ON1 5
  • Migrate to async

    Migrate to async

    Relates to #55

    Changes

    • All methods are now async/await coroutines.
    • Synchronous routes are run in a threadpool from an async wrapper, like how Starlette does it.
    • Blocking calls to limits storage backends or limiters is also executed in a threadpool.

    NOTE: Decorated functions now always become async coroutines, which should not cause any issues unless developers are invoking their routes directly.

    opened by thearchitector 5
  • Limit Http Headers

    Limit Http Headers

    I'm setting a rate limit for a route as below

    from slowapi import Limiter, _rate_limit_exceeded_handler from slowapi.util import get_remote_address limiter = Limiter(key_func=get_remote_address, headers_enabled=True)

    @router.post("/send-verification-otp", response_model=schemas.Msg) @limiter.limit("1/1 minute") def send_verification_otp(

    Check the below error:

    AttributeError: 'dict' object has no attribute 'headers'

    opened by moayyadfaris 5
  • Return a custom JSON-like object when rate limit is hit

    Return a custom JSON-like object when rate limit is hit

    I know it is possible to specify the error message in the limit decorator, but I'm building a very complex API and all of my requests need to be consistent and follow a specific schema that I represented using a pydantic.BaseModel subclass (basically a dataclass on steroids). Is it possible to return such a custom object instead of the default starlette.responses.JSONResponse?

    My response looks like this (once converted to a dictionary)

    {"request_id" : "foobarbaz", "status_code": 416, "response_data": None, "error_message" : "Unsupported media type"}

    opened by nocturn9x 5
  • Encountered an error reporting: AttributeError: 'State' object has no attribute 'view_rate_limit'

    Encountered an error reporting: AttributeError: 'State' object has no attribute 'view_rate_limit'

    Code reproduction:

    slowapi code configuration

    from slowapi.errors import RateLimitExceeded
    from slowapi.middleware import SlowAPIMiddleware
    
    
    def limiter_key_func(request: Request) -> str:
        ip = request.headers.get("CF-Connecting-IP") or get_remote_address(request)
        return ip
        
    limiter = Limiter(key_func=limiter_key_func, default_limits=["1/second"])
    
    def rate_limit_exceeded_handler(request: Request, exc: RateLimitExceeded) -> Response:
        .
        .
        .
        response = JSONResponse(content={"code": 429, "data": data, "msg": msg}, status_code=429)
        response = request.app.state.limiter._inject_headers(response, request.state.view_rate_limit)
        return response
        
        
    
    
    app.state.limiter = limiter
    app.add_exception_handler(RateLimitExceeded, rate_limit_exceeded_handler)
    app.add_middleware(SlowAPIMiddleware)
    

    This is my interface code

    @app.get('/demo1')
    @limiter.limit("100/day", override_defaults=False)
    async def channel(request: Request):
        return Response("A")
      
    @app.get('/demo2')
    @limiter.exempt
    async def channel(request: Request):
        return Response("B")
    

    When I request the interface demo1 I get the error AttributeError: 'State' object has no attribute 'view_rate_limit', When I comment out the @limiter.exempt and then request the interface demo1, it works fine.

    The following is the complete log of errors reported.

    INFO:     127.0.0.1:12134 - "GET /demo1 HTTP/1.1" 500 Internal Server Error
    ERROR     2022-10-11 13:22:55.444 - uvicorn.protocols.http.httptools_impl:run_asgi - Exception in ASGI application
    Traceback (most recent call last):
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\anyio\streams\memory.py", line 81, in receive
        return self.receive_nowait()
               │    └ <function MemoryObjectReceiveStream.receive_nowait at 0x000001BAD1BD8280>
               └ MemoryObjectReceiveStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=0, open_rece...
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\anyio\streams\memory.py", line 76, in receive_nowait
        raise WouldBlock
              └ <class 'anyio.WouldBlock'>
    
    anyio.WouldBlock
    
    
    During handling of the above exception, another exception occurred:
    
    
    Traceback (most recent call last):
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\base.py", line 43, in call_next
        message = await recv_stream.receive()
                        │           └ <function MemoryObjectReceiveStream.receive at 0x000001BAD1BD8310>
                        └ MemoryObjectReceiveStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=0, open_rece...
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\anyio\streams\memory.py", line 101, in receive
        raise EndOfStream
              └ <class 'anyio.EndOfStream'>
    
    anyio.EndOfStream
    
    
    During handling of the above exception, another exception occurred:
    
    
    Traceback (most recent call last):
    
      File "xxx\PycharmProjects\public_service2\app\main.py", line 10, in <module>
        uvicorn.run(app=app, host="0.0.0.0", port=PORT, log_level="info", forwarded_allow_ips="*", access_log=True,
        │       │       │                         └ 8080
        │       │       └ <fastapi.applications.FastAPI object at 0x000001BAD1ADC8B0>
        │       └ <function run at 0x000001BACF787A30>
        └ <module 'uvicorn' from 'xxx\\AppData\\Local\\Programs\\Python\\Python310\\lib\\site-packages\\uvicorn\\__init__....
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\uvicorn\main.py", line 447, in run
        server.run()
        │      └ <function Server.run at 0x000001BACF787D00>
        └ <uvicorn.server.Server object at 0x000001BAD275D960>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\uvicorn\server.py", line 68, in run
        return asyncio.run(self.serve(sockets=sockets))
               │       │   │    │             └ None
               │       │   │    └ <function Server.serve at 0x000001BACF787D90>
               │       │   └ <uvicorn.server.Server object at 0x000001BAD275D960>
               │       └ <function run at 0x000001BACF4FB250>
               └ <module 'asyncio' from 'xxx\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\__init__.py'>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\asyncio\runners.py", line 44, in run
        return loop.run_until_complete(main)
               │    │                  └ <coroutine object Server.serve at 0x000001BAD273E030>
               │    └ <function BaseEventLoop.run_until_complete at 0x000001BACF504CA0>
               └ <_WindowsSelectorEventLoop running=True closed=False debug=False>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 628, in run_until_complete
        self.run_forever()
        │    └ <function BaseEventLoop.run_forever at 0x000001BACF504C10>
        └ <_WindowsSelectorEventLoop running=True closed=False debug=False>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 595, in run_forever
        self._run_once()
        │    └ <function BaseEventLoop._run_once at 0x000001BACF506710>
        └ <_WindowsSelectorEventLoop running=True closed=False debug=False>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 1881, in _run_once
        handle._run()
        │      └ <function Handle._run at 0x000001BACF4A5E10>
        └ <Handle <TaskStepMethWrapper object at 0x000001BAD29004C0>()>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\asyncio\events.py", line 80, in _run
        self._context.run(self._callback, *self._args)
        │    │            │    │           │    └ <member '_args' of 'Handle' objects>
        │    │            │    │           └ <Handle <TaskStepMethWrapper object at 0x000001BAD29004C0>()>
        │    │            │    └ <member '_callback' of 'Handle' objects>
        │    │            └ <Handle <TaskStepMethWrapper object at 0x000001BAD29004C0>()>
        │    └ <member '_context' of 'Handle' objects>
        └ <Handle <TaskStepMethWrapper object at 0x000001BAD29004C0>()>
    
    > File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 375, in run_asgi
        result = await app(self.scope, self.receive, self.send)
                       │   │    │      │    │        │    └ <function RequestResponseCycle.send at 0x000001BAD27CC8B0>
                       │   │    │      │    │        └ <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001BAD2886920>
                       │   │    │      │    └ <function RequestResponseCycle.receive at 0x000001BAD27CC940>
                       │   │    │      └ <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001BAD2886920>
                       │   │    └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
                       │   └ <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001BAD2886920>
                       └ <uvicorn.middleware.proxy_headers.ProxyHeadersMiddleware object at 0x000001BAD278ECE0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 75, in __call__
        return await self.app(scope, receive, send)
                     │    │   │      │        └ <bound method RequestResponseCycle.send of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001BAD2...
                     │    │   │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001B...
                     │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
                     │    └ <fastapi.applications.FastAPI object at 0x000001BAD1ADC8B0>
                     └ <uvicorn.middleware.proxy_headers.ProxyHeadersMiddleware object at 0x000001BAD278ECE0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\applications.py", line 269, in __call__
        await super().__call__(scope, receive, send)
                               │      │        └ <bound method RequestResponseCycle.send of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001BAD2...
                               │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001B...
                               └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\applications.py", line 124, in __call__
        await self.middleware_stack(scope, receive, send)
              │    │                │      │        └ <bound method RequestResponseCycle.send of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001BAD2...
              │    │                │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001B...
              │    │                └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
              │    └ <starlette.middleware.errors.ServerErrorMiddleware object at 0x000001BAD278E830>
              └ <fastapi.applications.FastAPI object at 0x000001BAD1ADC8B0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\errors.py", line 184, in __call__
        raise exc
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\errors.py", line 162, in __call__
        await self.app(scope, receive, _send)
              │    │   │      │        └ <function ServerErrorMiddleware.__call__.<locals>._send at 0x000001BAD2848790>
              │    │   │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001B...
              │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
              │    └ <starlette.middleware.cors.CORSMiddleware object at 0x000001BAD278E800>
              └ <starlette.middleware.errors.ServerErrorMiddleware object at 0x000001BAD278E830>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\cors.py", line 84, in __call__
        await self.app(scope, receive, send)
              │    │   │      │        └ <function ServerErrorMiddleware.__call__.<locals>._send at 0x000001BAD2848790>
              │    │   │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001B...
              │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
              │    └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x000001BAD278E7A0>
              └ <starlette.middleware.cors.CORSMiddleware object at 0x000001BAD278E800>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\base.py", line 68, in __call__
        response = await self.dispatch_func(request, call_next)
                         │    │             │        └ <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x000001BAD2848820>
                         │    │             └ <starlette.requests.Request object at 0x000001BAD2835CC0>
                         │    └ <function mid at 0x000001BAD277E4D0>
                         └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x000001BAD278E7A0>
    
      File "xxx\PycharmProjects\public_service2\app\api\__init__.py", line 92, in mid
        response = await call_next(request)
                         │         └ <starlette.requests.Request object at 0x000001BAD2835CC0>
                         └ <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x000001BAD2848820>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\base.py", line 46, in call_next
        raise app_exc
              └ AttributeError("'State' object has no attribute 'view_rate_limit'")
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\base.py", line 36, in coro
        await self.app(scope, request.receive, send_stream.send)
              │    │   │      │       │        │           └ <function MemoryObjectSendStream.send at 0x000001BAD1BD8A60>
              │    │   │      │       │        └ MemoryObjectSendStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=0, open_receive...
              │    │   │      │       └ <property object at 0x000001BAD265D170>
              │    │   │      └ <starlette.requests.Request object at 0x000001BAD2835CC0>
              │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
              │    └ <slowapi.middleware.SlowAPIMiddleware object at 0x000001BAD278E3B0>
              └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x000001BAD278E7A0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\base.py", line 68, in __call__
        response = await self.dispatch_func(request, call_next)
                         │    │             │        └ <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x000001BAD2849630>
                         │    │             └ <starlette.requests.Request object at 0x000001BAD2900280>
                         │    └ <bound method SlowAPIMiddleware.dispatch of <slowapi.middleware.SlowAPIMiddleware object at 0x000001BAD278E3B0>>
                         └ <slowapi.middleware.SlowAPIMiddleware object at 0x000001BAD278E3B0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\slowapi\middleware.py", line 34, in dispatch
        return await call_next(request)
                     │         └ <starlette.requests.Request object at 0x000001BAD2900280>
                     └ <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x000001BAD2849630>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\base.py", line 46, in call_next
        raise app_exc
              └ AttributeError("'State' object has no attribute 'view_rate_limit'")
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\base.py", line 36, in coro
        await self.app(scope, request.receive, send_stream.send)
              │    │   │      │       │        │           └ <function MemoryObjectSendStream.send at 0x000001BAD1BD8A60>
              │    │   │      │       │        └ MemoryObjectSendStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=0, open_receive...
              │    │   │      │       └ <property object at 0x000001BAD265D170>
              │    │   │      └ <starlette.requests.Request object at 0x000001BAD2900280>
              │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
              │    └ <starlette.exceptions.ExceptionMiddleware object at 0x000001BAD278E380>
              └ <slowapi.middleware.SlowAPIMiddleware object at 0x000001BAD278E3B0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\exceptions.py", line 93, in __call__
        raise exc
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\exceptions.py", line 82, in __call__
        await self.app(scope, receive, sender)
              │    │   │      │        └ <function ExceptionMiddleware.__call__.<locals>.sender at 0x000001BAD28FB880>
              │    │   │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001B...
              │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
              │    └ <fastapi.middleware.asyncexitstack.AsyncExitStackMiddleware object at 0x000001BAD278E140>
              └ <starlette.exceptions.ExceptionMiddleware object at 0x000001BAD278E380>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 21, in __call__
        raise e
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__
        await self.app(scope, receive, send)
              │    │   │      │        └ <function ExceptionMiddleware.__call__.<locals>.sender at 0x000001BAD28FB880>
              │    │   │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001B...
              │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
              │    └ <fastapi.routing.APIRouter object at 0x000001BAD1ADC9A0>
              └ <fastapi.middleware.asyncexitstack.AsyncExitStackMiddleware object at 0x000001BAD278E140>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\routing.py", line 670, in __call__
        await route.handle(scope, receive, send)
              │     │      │      │        └ <function ExceptionMiddleware.__call__.<locals>.sender at 0x000001BAD28FB880>
              │     │      │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001B...
              │     │      └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
              │     └ <function Route.handle at 0x000001BAD2685750>
              └ <fastapi.routing.APIRoute object at 0x000001BAD275DDE0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\routing.py", line 266, in handle
        await self.app(scope, receive, send)
              │    │   │      │        └ <function ExceptionMiddleware.__call__.<locals>.sender at 0x000001BAD28FB880>
              │    │   │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0x000001B...
              │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.1', 'server': ('127.0.0.1', 8080), 'cl...
              │    └ <function request_response.<locals>.app at 0x000001BAD277CB80>
              └ <fastapi.routing.APIRoute object at 0x000001BAD275DDE0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\routing.py", line 65, in app
        response = await func(request)
                         │    └ <starlette.requests.Request object at 0x000001BAD2900670>
                         └ <function get_request_handler.<locals>.app at 0x000001BAD277CAF0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\routing.py", line 227, in app
        raw_response = await run_endpoint_function(
                             └ <function run_endpoint_function at 0x000001BAD26863B0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\routing.py", line 160, in run_endpoint_function
        return await dependant.call(**values)
                     │         │      └ {'x': '', 'x': 'xxx', 'x': 'xxx', 'request': <starlette.requests.Request object at 0x000001BAD2900670>}
                     │         └ <function channel at 0x000001BAD2705B40>
                     └ <fastapi.dependencies.models.Dependant object at 0x000001BAD275DED0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\slowapi\extension.py", line 646, in async_wrapper
        response, request.state.view_rate_limit
        │         │       └ <property object at 0x000001BAD265D080>
        │         └ <starlette.requests.Request object at 0x000001BAD2900670>
        └ <starlette.responses.Response object at 0x000001BAD29008E0>
    
      File "xxx\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\datastructures.py", line 693, in __getattr__
        raise AttributeError(message.format(self.__class__.__name__, key))
                             │       │      │    │         │         └ 'view_rate_limit'
                             │       │      │    │         └ <member '__name__' of 'getset_descriptor' objects>
                             │       │      │    └ <attribute '__class__' of 'object' objects>
                             │       │      └ <starlette.datastructures.State object at 0x000001BAD29008B0>
                             │       └ <method 'format' of 'str' objects>
                             └ "'{}' object has no attribute '{}'"
    
    AttributeError: 'State' object has no attribute 'view_rate_limit'
    
    SUCCESS   2022-10-11 13:22:56.883 - app.utile:init - init final
    
    
    

    I don't know how to fix this error and I need help.

    opened by 239144498 4
  • Error in production

    Error in production

    Describe the bug Use Gunicorn in production

    To Reproduce Use gunicorn for start and call a endpoint with limit

    Expected behavior do nothing

    Screenshots If applicable, add screenshots to help explain your problem. Oct 25 20:16:46 gunicorn[12540]: return await dependant.call(**values) Oct 25 20:16:46 gunicorn[12540]: File "/home/ubuntu/proyect/api/env/lib/python3.10/site-packages/slowapi/extension.py", line 635, in async_wrapper Oct 25 20:16:46 gunicorn[12540]: self._check_request_limit(request, func, False) Oct 25 20:16:46 gunicorn[12540]: File "/home/ubuntu/proyect/api/env/lib/python3.10/site-packages/slowapi/extension.py", line 535, in _check_request_limit Oct 25 20:16:46 gunicorn[12540]: self.__evaluate_limits(request, endpoint, all_limits) Oct 25 20:16:46 gunicorn[12540]: File "/home/ubuntu/proyect/api/env/lib/python3.10/site-packages/slowapi/extension.py", line 413, in __evaluate_limits Oct 25 20:16:46 gunicorn[12540]: limit_key = lim.key_func(request) Oct 25 20:16:46 gunicorn[12540]: File "/home/ubuntu/proyect/api/env/lib/python3.10/site-packages/slowapi/util.py", line 21, in get_remote_address Oct 25 20:16:46 gunicorn[12540]: return request.client.host or "127.0.0.1" Oct 25 20:16:46 gunicorn[12540]: AttributeError: 'NoneType' object has no attribute 'host'

    Your app (please complete the following information):

    • fastapi or starlette? use fastapi
    • Version? - 0.79.0 and 0.85.1
    • slowapi version (have you tried with the latest version)? yes, is 0.85.1

    Additional context y set the api in production, and get this error

    opened by juanretamales 5
  • Information on ratelimit-string

    Information on ratelimit-string

    Throughout the code and docs, it says to refer to ratelimit-string for more information on what are valid inputs. However, I am unable to find information on this and have just guessed so far for my usage. I would appreciate information on all the options

    opened by mallorbc 3
  • Publish release to testpypi from github actions

    Publish release to testpypi from github actions

    This is a draft of what an automatic package publishing workflow could look like.

    TODO:

    • [ ] Add automatic changelogs
    • [ ] Allow publishing only from master branch
    • [ ] Restrict publishing to allowed contributors
    • [ ] switch from testpypi to production pypi once everything else is done
    opened by laurentS 2
  • Can slowapi be used in production?

    Can slowapi be used in production?

    The documention says: Note: this is alpha quality code still, the API may change, and things may fall apart while you try it.

    Because of that I am unsure if it can be used in production.

    opened by krissik 1
Owner
Laurent Savaete
Core dev mapswipe/mapswipe Core team madada.fr author of slowapi
Laurent Savaete
A request rate limiter for fastapi

fastapi-limiter Introduction FastAPI-Limiter is a rate limiting tool for fastapi routes. Requirements redis Install Just install from pypi > pip insta

long2ice 200 Jan 8, 2023
A request rate limiter for fastapi

fastapi-limiter Introduction FastAPI-Limiter is a rate limiting tool for fastapi routes. Requirements redis Install Just install from pypi > pip insta

long2ice 16 Feb 8, 2021
Prometheus exporter for Starlette and FastAPI

starlette_exporter Prometheus exporter for Starlette and FastAPI. The middleware collects basic metrics: Counter: starlette_requests_total Histogram:

Steve Hillier 225 Jan 5, 2023
Opentracing support for Starlette and FastApi

Starlette-OpenTracing OpenTracing support for Starlette and FastApi. Inspired by: Flask-OpenTracing OpenTracing implementations exist for major distri

Rene Dohmen 63 Dec 30, 2022
Prometheus exporter for Starlette and FastAPI

starlette_exporter Prometheus exporter for Starlette and FastAPI. The middleware collects basic metrics: Counter: starlette_requests_total Histogram:

Steve Hillier 82 Feb 13, 2021
Opentracing support for Starlette and FastApi

Starlette-OpenTracing OpenTracing support for Starlette and FastApi. Inspired by: Flask-OpenTracing OpenTracing implementations exist for major distri

Rene Dohmen 26 Feb 11, 2021
SQLAlchemy Admin for Starlette/FastAPI

SQLAlchemy Admin for Starlette/FastAPI SQLAdmin is a flexible Admin interface for SQLAlchemy models. Main features include: SQLAlchemy sync/async engi

Amin Alaee 683 Jan 3, 2023
A FastAPI Framework for things like Database, Redis, Logging, JWT Authentication and Rate Limits

A FastAPI Framework for things like Database, Redis, Logging, JWT Authentication and Rate Limits Install You can install this Library with: pip instal

Tert0 33 Nov 28, 2022
Farlimit - FastAPI rate limit with python

FastAPIRateLimit Contributing is F&E (free&easy) Y Usage pip install farlimit N

omid 27 Oct 6, 2022
Redis-based rate-limiting for FastAPI

Redis-based rate-limiting for FastAPI

Glib 6 Nov 14, 2022
Middleware for Starlette that allows you to store and access the context data of a request. Can be used with logging so logs automatically use request headers such as x-request-id or x-correlation-id.

starlette context Middleware for Starlette that allows you to store and access the context data of a request. Can be used with logging so logs automat

Tomasz Wójcik 300 Dec 26, 2022
Middleware for Starlette that allows you to store and access the context data of a request. Can be used with logging so logs automatically use request headers such as x-request-id or x-correlation-id.

starlette context Middleware for Starlette that allows you to store and access the context data of a request. Can be used with logging so logs automat

Tomasz Wójcik 110 Feb 16, 2021
Dead simple CSRF security middleware for Starlette ⭐ and Fast API ⚡

csrf-starlette-fastapi Dead simple CSRF security middleware for Starlette ⭐ and Fast API ⚡ Will work with either a <input type="hidden"> field or ajax

Nathaniel Sabanski 9 Nov 20, 2022
Starlette middleware for Prerender

Prerender Python Starlette Starlette middleware for Prerender Documentation: https://BeeMyDesk.github.io/prerender-python-starlette/ Source Code: http

BeeMyDesk 14 May 2, 2021
Prometheus integration for Starlette.

Starlette Prometheus Introduction Prometheus integration for Starlette. Requirements Python 3.6+ Starlette 0.9+ Installation $ pip install starlette-p

José Antonio Perdiguero 229 Dec 21, 2022
Starlette middleware for Prerender

Prerender Python Starlette Starlette middleware for Prerender Documentation: https://BeeMyDesk.github.io/prerender-python-starlette/ Source Code: http

BeeMyDesk 13 Jul 27, 2020
Prometheus integration for Starlette.

Starlette Prometheus Introduction Prometheus integration for Starlette. Requirements Python 3.6+ Starlette 0.9+ Installation $ pip install starlette-p

José Antonio Perdiguero 125 Feb 13, 2021
FastAPI-Amis-Admin is a high-performance, efficient and easily extensible FastAPI admin framework. Inspired by django-admin, and has as many powerful functions as django-admin.

简体中文 | English 项目介绍 FastAPI-Amis-Admin fastapi-amis-admin是一个拥有高性能,高效率,易拓展的fastapi管理后台框架. 启发自Django-Admin,并且拥有不逊色于Django-Admin的强大功能. 源码 · 在线演示 · 文档 · 文

AmisAdmin 318 Dec 31, 2022
fastapi-admin2 is an upgraded fastapi-admin, that supports ORM dialects, true Dependency Injection and extendability

FastAPI2 Admin Introduction fastapi-admin2 is an upgraded fastapi-admin, that supports ORM dialects, true Dependency Injection and extendability. Now

Glib 14 Dec 5, 2022