- 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
316 lines
8.0 KiB
ReStructuredText
316 lines
8.0 KiB
ReStructuredText
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"}
|