release: bump version to 0.3.0

- Refactor Redis backend connection handling and pool management
- Update algorithm implementations with improved type annotations
- Enhance config loader validation with stricter Pydantic schemas
- Improve decorator and middleware error handling
- Expand example scripts with better docstrings and usage patterns
- Add new 00_basic_usage.py example for quick start
- Reorganize examples directory structure
- Fix type annotation inconsistencies across core modules
- Update dependencies in pyproject.toml
This commit is contained in:
2026-03-17 20:55:38 +00:00
parent 492410614f
commit f3453cb0fc
51 changed files with 6507 additions and 166 deletions

View File

@@ -0,0 +1,315 @@
Configuration
=============
FastAPI Traffic supports loading configuration from environment variables and files.
This makes it easy to manage settings across different environments without changing code.
Configuration Loader
--------------------
The ``ConfigLoader`` class handles loading configuration from various sources:
.. code-block:: python
from fastapi_traffic import ConfigLoader, RateLimitConfig
loader = ConfigLoader()
# Load from environment variables
config = loader.load_rate_limit_config_from_env()
# Load from a JSON file
config = loader.load_rate_limit_config_from_json("config/rate_limits.json")
# Load from a .env file
config = loader.load_rate_limit_config_from_env_file(".env")
Environment Variables
---------------------
Set rate limit configuration using environment variables with the ``FASTAPI_TRAFFIC_``
prefix:
.. code-block:: bash
# Basic settings
export FASTAPI_TRAFFIC_RATE_LIMIT_LIMIT=100
export FASTAPI_TRAFFIC_RATE_LIMIT_WINDOW_SIZE=60
export FASTAPI_TRAFFIC_RATE_LIMIT_ALGORITHM=sliding_window_counter
# Optional settings
export FASTAPI_TRAFFIC_RATE_LIMIT_KEY_PREFIX=myapp
export FASTAPI_TRAFFIC_RATE_LIMIT_BURST_SIZE=20
export FASTAPI_TRAFFIC_RATE_LIMIT_INCLUDE_HEADERS=true
export FASTAPI_TRAFFIC_RATE_LIMIT_ERROR_MESSAGE="Too many requests"
export FASTAPI_TRAFFIC_RATE_LIMIT_STATUS_CODE=429
export FASTAPI_TRAFFIC_RATE_LIMIT_SKIP_ON_ERROR=false
export FASTAPI_TRAFFIC_RATE_LIMIT_COST=1
Then load them in your app:
.. code-block:: python
from fastapi_traffic import load_rate_limit_config_from_env, rate_limit
# Load config from environment
config = load_rate_limit_config_from_env()
# Use it with the decorator
@app.get("/api/data")
@rate_limit(config.limit, config.window_size, algorithm=config.algorithm)
async def get_data(request: Request):
return {"data": "here"}
Custom Prefix
-------------
If ``FASTAPI_TRAFFIC_`` conflicts with something else, use a custom prefix:
.. code-block:: python
loader = ConfigLoader(prefix="MYAPP_RATELIMIT")
config = loader.load_rate_limit_config_from_env()
# Now reads from:
# MYAPP_RATELIMIT_RATE_LIMIT_LIMIT=100
# MYAPP_RATELIMIT_RATE_LIMIT_WINDOW_SIZE=60
# etc.
JSON Configuration
------------------
For more complex setups, use a JSON file:
.. code-block:: json
{
"limit": 100,
"window_size": 60,
"algorithm": "token_bucket",
"burst_size": 25,
"key_prefix": "api",
"include_headers": true,
"error_message": "Rate limit exceeded. Please slow down.",
"status_code": 429,
"skip_on_error": false,
"cost": 1
}
Load it:
.. code-block:: python
from fastapi_traffic import ConfigLoader
loader = ConfigLoader()
config = loader.load_rate_limit_config_from_json("config/rate_limits.json")
.env Files
----------
You can also use ``.env`` files, which is handy for local development:
.. code-block:: bash
# .env
FASTAPI_TRAFFIC_RATE_LIMIT_LIMIT=100
FASTAPI_TRAFFIC_RATE_LIMIT_WINDOW_SIZE=60
FASTAPI_TRAFFIC_RATE_LIMIT_ALGORITHM=sliding_window
Load it:
.. code-block:: python
loader = ConfigLoader()
config = loader.load_rate_limit_config_from_env_file(".env")
Global Configuration
--------------------
Besides per-endpoint configuration, you can set global defaults:
.. code-block:: bash
# Global settings
export FASTAPI_TRAFFIC_GLOBAL_ENABLED=true
export FASTAPI_TRAFFIC_GLOBAL_DEFAULT_LIMIT=100
export FASTAPI_TRAFFIC_GLOBAL_DEFAULT_WINDOW_SIZE=60
export FASTAPI_TRAFFIC_GLOBAL_DEFAULT_ALGORITHM=sliding_window_counter
export FASTAPI_TRAFFIC_GLOBAL_KEY_PREFIX=fastapi_traffic
export FASTAPI_TRAFFIC_GLOBAL_INCLUDE_HEADERS=true
export FASTAPI_TRAFFIC_GLOBAL_ERROR_MESSAGE="Rate limit exceeded"
export FASTAPI_TRAFFIC_GLOBAL_STATUS_CODE=429
export FASTAPI_TRAFFIC_GLOBAL_SKIP_ON_ERROR=false
export FASTAPI_TRAFFIC_GLOBAL_HEADERS_PREFIX=X-RateLimit
Load global config:
.. code-block:: python
from fastapi_traffic import load_global_config_from_env, RateLimiter
from fastapi_traffic.core.limiter import set_limiter
global_config = load_global_config_from_env()
limiter = RateLimiter(config=global_config)
set_limiter(limiter)
Auto-Detection
--------------
The convenience functions automatically detect file format:
.. code-block:: python
from fastapi_traffic import load_rate_limit_config, load_global_config
# Detects JSON by extension
config = load_rate_limit_config("config/limits.json")
# Detects .env file
config = load_rate_limit_config("config/.env")
# Works for global config too
global_config = load_global_config("config/global.json")
Overriding Values
-----------------
You can override loaded values programmatically:
.. code-block:: python
loader = ConfigLoader()
# Load base config from file
config = loader.load_rate_limit_config_from_json(
"config/base.json",
limit=200, # Override the limit
key_prefix="custom", # Override the prefix
)
This is useful for environment-specific overrides:
.. code-block:: python
import os
base_config = loader.load_rate_limit_config_from_json("config/base.json")
# Apply environment-specific overrides
if os.getenv("ENVIRONMENT") == "production":
config = loader.load_rate_limit_config_from_json(
"config/base.json",
limit=base_config.limit * 2, # Double the limit in production
)
Validation
----------
Configuration is validated when loaded. Invalid values raise ``ConfigurationError``:
.. code-block:: python
from fastapi_traffic import ConfigLoader, ConfigurationError
loader = ConfigLoader()
try:
config = loader.load_rate_limit_config_from_env()
except ConfigurationError as e:
print(f"Invalid configuration: {e}")
# Handle the error appropriately
Common validation errors:
- ``limit`` must be a positive integer
- ``window_size`` must be a positive number
- ``algorithm`` must be one of the valid algorithm names
- ``status_code`` must be a valid HTTP status code
Algorithm Names
---------------
When specifying algorithms in configuration, use these names:
.. list-table::
:header-rows: 1
* - Config Value
- Algorithm
* - ``token_bucket``
- Token Bucket
* - ``sliding_window``
- Sliding Window
* - ``fixed_window``
- Fixed Window
* - ``leaky_bucket``
- Leaky Bucket
* - ``sliding_window_counter``
- Sliding Window Counter (default)
Boolean Values
--------------
Boolean settings accept various formats:
- **True:** ``true``, ``1``, ``yes``, ``on``
- **False:** ``false``, ``0``, ``no``, ``off``
Case doesn't matter.
Complete Example
----------------
Here's a full example showing configuration loading in a real app:
.. code-block:: python
import os
from fastapi import FastAPI, Request
from fastapi_traffic import (
ConfigLoader,
ConfigurationError,
RateLimiter,
rate_limit,
)
from fastapi_traffic.core.limiter import set_limiter
app = FastAPI()
@app.on_event("startup")
async def startup():
loader = ConfigLoader()
try:
# Try to load from environment first
global_config = loader.load_global_config_from_env()
except ConfigurationError:
# Fall back to defaults
global_config = None
limiter = RateLimiter(config=global_config)
set_limiter(limiter)
await limiter.initialize()
@app.get("/api/data")
@rate_limit(100, 60)
async def get_data(request: Request):
return {"data": "here"}
# Or load endpoint-specific config
loader = ConfigLoader()
try:
api_config = loader.load_rate_limit_config_from_json("config/api_limits.json")
except (FileNotFoundError, ConfigurationError):
api_config = None
if api_config:
@app.get("/api/special")
@rate_limit(
api_config.limit,
api_config.window_size,
algorithm=api_config.algorithm,
)
async def special_endpoint(request: Request):
return {"special": "data"}