fastapi-limiter


Namefastapi-limiter JSON
Version 0.1.6 PyPI version JSON
download
home_pagehttps://github.com/long2ice/fastapi-limiter
SummaryA request rate limiter for fastapi
upload_time2024-01-05 09:14:48
maintainer
docs_urlNone
authorlong2ice
requires_python>=3.9,<4.0
licenseApache2.0
keywords fastapi limiter
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # fastapi-limiter

[![pypi](https://img.shields.io/pypi/v/fastapi-limiter.svg?style=flat)](https://pypi.python.org/pypi/fastapi-limiter)
[![license](https://img.shields.io/github/license/long2ice/fastapi-limiter)](https://github.com/long2ice/fastapi-limiter/blob/master/LICENCE)
[![workflows](https://github.com/long2ice/fastapi-limiter/workflows/pypi/badge.svg)](https://github.com/long2ice/fastapi-limiter/actions?query=workflow:pypi)
[![workflows](https://github.com/long2ice/fastapi-limiter/workflows/ci/badge.svg)](https://github.com/long2ice/fastapi-limiter/actions?query=workflow:ci)

## Introduction

FastAPI-Limiter is a rate limiting tool for [fastapi](https://github.com/tiangolo/fastapi) routes with lua script.

## Requirements

- [redis](https://redis.io/)

## Install

Just install from pypi

```shell script
> pip install fastapi-limiter
```

## Quick Start

FastAPI-Limiter is simple to use, which just provide a dependency `RateLimiter`, the following example allow `2` times
request per `5` seconds in route `/`.

```py
import redis.asyncio as redis
import uvicorn
from fastapi import Depends, FastAPI

from fastapi_limiter import FastAPILimiter
from fastapi_limiter.depends import RateLimiter

app = FastAPI()


@app.on_event("startup")
async def startup():
    redis_connection = redis.from_url("redis://localhost", encoding="utf-8", decode_responses=True)
    await FastAPILimiter.init(redis_connection)


@app.get("/", dependencies=[Depends(RateLimiter(times=2, seconds=5))])
async def index():
    return {"msg": "Hello World"}


if __name__ == "__main__":
    uvicorn.run("main:app", debug=True, reload=True)
```

## Usage

There are some config in `FastAPILimiter.init`.

### redis

The `redis` instance of `aioredis`.

### prefix

Prefix of redis key.

### identifier

Identifier of route limit, default is `ip`, you can override it such as `userid` and so on.

```py
async def default_identifier(request: Request):
    forwarded = request.headers.get("X-Forwarded-For")
    if forwarded:
        return forwarded.split(",")[0]
    return request.client.host + ":" + request.scope["path"]
```

### callback

Callback when access is forbidden, default is raise `HTTPException` with `429` status code.

```py
async def default_callback(request: Request, response: Response, pexpire: int):
    """
    default callback when too many requests
    :param request:
    :param pexpire: The remaining milliseconds
    :param response:
    :return:
    """
    expire = ceil(pexpire / 1000)

    raise HTTPException(
        HTTP_429_TOO_MANY_REQUESTS, "Too Many Requests", headers={"Retry-After": str(expire)}
    )
```

## Multiple limiters

You can use multiple limiters in one route.

```py
@app.get(
    "/multiple",
    dependencies=[
        Depends(RateLimiter(times=1, seconds=5)),
        Depends(RateLimiter(times=2, seconds=15)),
    ],
)
async def multiple():
    return {"msg": "Hello World"}
```

Not that you should note the dependencies orders, keep lower of result of `seconds/times` at the first.

## Rate limiting within a websocket.

While the above examples work with rest requests, FastAPI also allows easy usage
of websockets, which require a slightly different approach.

Because websockets are likely to be long lived, you may want to rate limit in
response to data sent over the socket.

You can do this by rate limiting within the body of the websocket handler:

```py
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    ratelimit = WebSocketRateLimiter(times=1, seconds=5)
    while True:
        try:
            data = await websocket.receive_text()
            await ratelimit(websocket, context_key=data)  # NB: context_key is optional
            await websocket.send_text(f"Hello, world")
        except WebSocketRateLimitException:  # Thrown when rate limit exceeded.
            await websocket.send_text(f"Hello again")
```

## Lua script

The lua script used.

```lua
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local expire_time = ARGV[2]

local current = tonumber(redis.call('get', key) or "0")
if current > 0 then
    if current + 1 > limit then
        return redis.call("PTTL", key)
    else
        redis.call("INCR", key)
        return 0
    end
else
    redis.call("SET", key, 1, "px", expire_time)
    return 0
end
```

## License

This project is licensed under the
[Apache-2.0](https://github.com/long2ice/fastapi-limiter/blob/master/LICENCE) License.


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/long2ice/fastapi-limiter",
    "name": "fastapi-limiter",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.9,<4.0",
    "maintainer_email": "",
    "keywords": "fastapi,limiter",
    "author": "long2ice",
    "author_email": "long2ice@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/7f/99/c7903234488d4dca5f9bccb4f88c2f582a234f0dca33348781c9cf8a48c6/fastapi_limiter-0.1.6.tar.gz",
    "platform": null,
    "description": "# fastapi-limiter\n\n[![pypi](https://img.shields.io/pypi/v/fastapi-limiter.svg?style=flat)](https://pypi.python.org/pypi/fastapi-limiter)\n[![license](https://img.shields.io/github/license/long2ice/fastapi-limiter)](https://github.com/long2ice/fastapi-limiter/blob/master/LICENCE)\n[![workflows](https://github.com/long2ice/fastapi-limiter/workflows/pypi/badge.svg)](https://github.com/long2ice/fastapi-limiter/actions?query=workflow:pypi)\n[![workflows](https://github.com/long2ice/fastapi-limiter/workflows/ci/badge.svg)](https://github.com/long2ice/fastapi-limiter/actions?query=workflow:ci)\n\n## Introduction\n\nFastAPI-Limiter is a rate limiting tool for [fastapi](https://github.com/tiangolo/fastapi) routes with lua script.\n\n## Requirements\n\n- [redis](https://redis.io/)\n\n## Install\n\nJust install from pypi\n\n```shell script\n> pip install fastapi-limiter\n```\n\n## Quick Start\n\nFastAPI-Limiter is simple to use, which just provide a dependency `RateLimiter`, the following example allow `2` times\nrequest per `5` seconds in route `/`.\n\n```py\nimport redis.asyncio as redis\nimport uvicorn\nfrom fastapi import Depends, FastAPI\n\nfrom fastapi_limiter import FastAPILimiter\nfrom fastapi_limiter.depends import RateLimiter\n\napp = FastAPI()\n\n\n@app.on_event(\"startup\")\nasync def startup():\n    redis_connection = redis.from_url(\"redis://localhost\", encoding=\"utf-8\", decode_responses=True)\n    await FastAPILimiter.init(redis_connection)\n\n\n@app.get(\"/\", dependencies=[Depends(RateLimiter(times=2, seconds=5))])\nasync def index():\n    return {\"msg\": \"Hello World\"}\n\n\nif __name__ == \"__main__\":\n    uvicorn.run(\"main:app\", debug=True, reload=True)\n```\n\n## Usage\n\nThere are some config in `FastAPILimiter.init`.\n\n### redis\n\nThe `redis` instance of `aioredis`.\n\n### prefix\n\nPrefix of redis key.\n\n### identifier\n\nIdentifier of route limit, default is `ip`, you can override it such as `userid` and so on.\n\n```py\nasync def default_identifier(request: Request):\n    forwarded = request.headers.get(\"X-Forwarded-For\")\n    if forwarded:\n        return forwarded.split(\",\")[0]\n    return request.client.host + \":\" + request.scope[\"path\"]\n```\n\n### callback\n\nCallback when access is forbidden, default is raise `HTTPException` with `429` status code.\n\n```py\nasync def default_callback(request: Request, response: Response, pexpire: int):\n    \"\"\"\n    default callback when too many requests\n    :param request:\n    :param pexpire: The remaining milliseconds\n    :param response:\n    :return:\n    \"\"\"\n    expire = ceil(pexpire / 1000)\n\n    raise HTTPException(\n        HTTP_429_TOO_MANY_REQUESTS, \"Too Many Requests\", headers={\"Retry-After\": str(expire)}\n    )\n```\n\n## Multiple limiters\n\nYou can use multiple limiters in one route.\n\n```py\n@app.get(\n    \"/multiple\",\n    dependencies=[\n        Depends(RateLimiter(times=1, seconds=5)),\n        Depends(RateLimiter(times=2, seconds=15)),\n    ],\n)\nasync def multiple():\n    return {\"msg\": \"Hello World\"}\n```\n\nNot that you should note the dependencies orders, keep lower of result of `seconds/times` at the first.\n\n## Rate limiting within a websocket.\n\nWhile the above examples work with rest requests, FastAPI also allows easy usage\nof websockets, which require a slightly different approach.\n\nBecause websockets are likely to be long lived, you may want to rate limit in\nresponse to data sent over the socket.\n\nYou can do this by rate limiting within the body of the websocket handler:\n\n```py\n@app.websocket(\"/ws\")\nasync def websocket_endpoint(websocket: WebSocket):\n    await websocket.accept()\n    ratelimit = WebSocketRateLimiter(times=1, seconds=5)\n    while True:\n        try:\n            data = await websocket.receive_text()\n            await ratelimit(websocket, context_key=data)  # NB: context_key is optional\n            await websocket.send_text(f\"Hello, world\")\n        except WebSocketRateLimitException:  # Thrown when rate limit exceeded.\n            await websocket.send_text(f\"Hello again\")\n```\n\n## Lua script\n\nThe lua script used.\n\n```lua\nlocal key = KEYS[1]\nlocal limit = tonumber(ARGV[1])\nlocal expire_time = ARGV[2]\n\nlocal current = tonumber(redis.call('get', key) or \"0\")\nif current > 0 then\n    if current + 1 > limit then\n        return redis.call(\"PTTL\", key)\n    else\n        redis.call(\"INCR\", key)\n        return 0\n    end\nelse\n    redis.call(\"SET\", key, 1, \"px\", expire_time)\n    return 0\nend\n```\n\n## License\n\nThis project is licensed under the\n[Apache-2.0](https://github.com/long2ice/fastapi-limiter/blob/master/LICENCE) License.\n\n",
    "bugtrack_url": null,
    "license": "Apache2.0",
    "summary": "A request rate limiter for fastapi",
    "version": "0.1.6",
    "project_urls": {
        "Documentation": "https://github.com/long2ice/fastapi-limiter",
        "Homepage": "https://github.com/long2ice/fastapi-limiter",
        "Repository": "https://github.com/long2ice/fastapi-limiter.git"
    },
    "split_keywords": [
        "fastapi",
        "limiter"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "cdb56f6b4d18bee1cafc857eae12738b3a03b7d1102b833668be868938c57b9d",
                "md5": "b8de0eacbf4b70d76d1e6efbde70a706",
                "sha256": "2e53179a4208b8f2c8795e38bb001324d3dc37d2800ff49fd28ec5caabf7a240"
            },
            "downloads": -1,
            "filename": "fastapi_limiter-0.1.6-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "b8de0eacbf4b70d76d1e6efbde70a706",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9,<4.0",
            "size": 15829,
            "upload_time": "2024-01-05T09:14:47",
            "upload_time_iso_8601": "2024-01-05T09:14:47.613434Z",
            "url": "https://files.pythonhosted.org/packages/cd/b5/6f6b4d18bee1cafc857eae12738b3a03b7d1102b833668be868938c57b9d/fastapi_limiter-0.1.6-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7f99c7903234488d4dca5f9bccb4f88c2f582a234f0dca33348781c9cf8a48c6",
                "md5": "6265ed8c20ab3a76e2277ffe2ff5e5f3",
                "sha256": "6f5fde8efebe12eb33861bdffb91009f699369a3c2862cdc7c1d9acf912ff443"
            },
            "downloads": -1,
            "filename": "fastapi_limiter-0.1.6.tar.gz",
            "has_sig": false,
            "md5_digest": "6265ed8c20ab3a76e2277ffe2ff5e5f3",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9,<4.0",
            "size": 8307,
            "upload_time": "2024-01-05T09:14:48",
            "upload_time_iso_8601": "2024-01-05T09:14:48.628923Z",
            "url": "https://files.pythonhosted.org/packages/7f/99/c7903234488d4dca5f9bccb4f88c2f582a234f0dca33348781c9cf8a48c6/fastapi_limiter-0.1.6.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-01-05 09:14:48",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "long2ice",
    "github_project": "fastapi-limiter",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "fastapi-limiter"
}
        
Elapsed time: 0.15674s