ratelimitcli 0.2.2
A CLI to manage rate limits.
License: Apache-2.0
Requires Python: >=3.9,<4.0

# Adding rate limits to your API

This is a python CLI that communicates with a cloud based HTTP **Ratelimit Service** to configure rate limits based on the token bucket algorithm.

Redis is used as the backing storage for the rate limit configurations, and using Lua scripts, the **Ratelimit Service** can make a determination to allow or deny a request within milliseconds.

This python package also comes with:

1. An HTTP client that can be used to manually test your limits.
2. A python decorator for your HTTP endpoint functions.

### Where to start

The quickstart below will guide you through configuring the CLI on your system, authenticating to the **Ratelimit Service**, creating a rate limit, and testing it.

For more detailed documentation check out [https://docs.ratelimit.xyz](https://docs.ratelimit.xyz).

## Configure the CLI

1. Install the RateLimit CLI.

```bash
$ pip install ratelimitcli
```

2. Configure the RateLimit CLI and follow the interactive prompts. You won't have an API key yet so you can just press `[ENTER]` when asked for a value.

```bash
$ ratelimitcli configure
Client ID  [None]:
API key    [None]:
First name [None]: Jasmin
Last name  [None]: Smith
Email name [None]: jasmin.smith@gmail.com
Updated config file (~/.ratelimit/config).
```

3. Request an API key and a Client ID. You'll be asked to enter credit card information.

```
$ ratelimitcli billing configure
Enter a credit card number: 1234567890123456
Enter the expiration date for that credit card: 10/12
Enter the CCV for that credit card: 333
New values have been written to the config file.
```

> **Disclaimer**
>
> I have not yet implemented payments and as such, you won't be charged for anything. That said, this service is currently in a closed alpha. There is only a single credit card that will allow you to get an API key and you'd have to get that from me.
>
> If this service or the concept is interesting enough for you and you don't mind sending an email, feel free to contact me and we can discuss your usecase.
>
> Oh and the fake card I show above won't work - just FYI.

4. Check to see that the config file has been written to `$HOME/.ratelimit/config`

```bash
$ cat ~/.ratelimit/config
```

The CLI is now configured and ready to use.

---

## Creating your first limit

1. Create your first limit by supplying integers for the **burst limit** and the **rate limit**.

```bash
$ ratelimitcli limits upsert \
  --throttling-burst-limit 2 \
  --throttling-rate-limit 0
```

> **Burst Limit**
>
> The maximum available instantaneous capacity.\
> https://en.wikipedia.org/wiki/Token_bucket

> **Rate Limit**
>
> The rate at which used capacity is restored.\
> https://en.wikipedia.org/wiki/Token_bucket

You'll know everything is set up when you get an API response with a limit ID that identifies the rate limit you just created.

Returns

```
Ok: API response ({"limit_id": "a9f9f31b-2c0b-321b-b398-f9d36kd30820"}).
```

2. We can store that limit ID into an environment variable for ease of access.

```bash
export LIMIT_ID=a9f9f31b-2c0b-321b-b398-f9d36kd30820
```

3. Test your first rate limit.

```bash
# ok - capacity 2, using the env var
$ ratelimitcli limits record $LIMIT_ID

# ok - capacity 1, using ID directly
$ ratelimitcli limits record \
  a9f9f31b-2c0b-321b-b398-f9d36kd30820

# ERROR - capacity 0, we've exhausted the capacity
$ ratelimitcli limits record $LIMIT_ID
```

4. Test rate limits in a python interpreter shell. Note that in production, you most likely will not be using the `RatelimitClient` directly, nor will you be using synchronous calls to the **Ratelimit Service**.

```python
$ python

# Get the limit ID from the env var
>>> import os
>>> limit_id = os.environ['LIMIT_ID']
>>> limit_id
'a9f9f31b-2c0b-321b-b398-f9d36kd30820'

# Import the client
>>> from ratelimitcli.client.client import RatelimitClient

# All args are optional.
# Defaults are pulled from env and config file.
>>> client = RatelimitClient()

# Synchronous calls to the service.
>>> client.sync_record_request(limit_id)
>>> client.sync_record_request(
... "a9f9f31b-2c0b-321b-b398-f9d36kd30820"
... )
```

8. Use your rate limit in your code.

```python
from fastapi import FastAPI

from ratelimitcli.client.exceptions import APIRateLimitException
from ratelimitcli.client.decorator import RatelimitDecorator

app = FastAPI()

def on_error_callback(
  msg: str,
  exception: APIRateLimitException
):
    """Callback function.

    Args:
        1. All the `*args` and `**kwargs` that were passed
           to the original function. In this case `msg: str`.

        2. The exception that was raised by RatelimitDecorator

    Returns:
        Some value that takes the place of the wrapped function's
        return value.
    """
    return "Goodbye, World!" + msg


@app.get("/")
@RatelimitDecorator(
    # The limit ID to check capacity against.
    id="a9f9f31b-2c0b-321b-b398-f9d36kd30820",
    # The function to call if capacity has been exhausted.
    callback=on_error_callback,
)
async def hello(msg: str):
    return "Hello, World!" + msg
```