A minimal reverse proxy service that routes requests to the OpenLiga API with validation, rate limiting, and exponential backoff retry logic.
cp example.env .env
./run_docker.shServer runs at http://localhost:8000
cp example.env .env
pip install -r requirements.txt
uvicorn main:app --reloadcurl -X POST http://localhost:8000/proxy/execute \
-H "Content-Type: application/json" \
-d '{"operationType": "ListLeagues", "payload": {}}'curl -X POST http://localhost:8000/proxy/execute \
-H "Content-Type: application/json" \
-d '{"operationType": "GetLeagueMatches", "payload": {"leagueId": 8, "season": 2023}}'curl -X POST http://localhost:8000/proxy/execute \
-H "Content-Type: application/json" \
-d '{"operationType": "GetTeam", "payload": {"teamId": 1}}'curl -X POST http://localhost:8000/proxy/execute \
-H "Content-Type: application/json" \
-d '{"operationType": "GetMatch", "payload": {"teamId1": 1, "teamId2": 2}}'No payload required:
{"operationType": "ListLeagues", "payload": {}}{
"leagueId": 8,
"season": 2023
}{
"teamId": 1
}{
"teamId1": 1,
"teamId2": 2
}The DecisionMapper routes operationType to the correct adapter method:
- Receives
operationTypeandpayloadfrom the request - Validates payload against the operation's Pydantic schema
- Calls the corresponding method on
OpenLigaDBAdapter - Returns a normalized response
Supported operations: ListLeagues, GetLeagueMatches, GetTeam, GetMatch
SportsProvider is an abstract base class that all adapters must implement:
class SportsProvider(ABC):
async def list_leagues() -> AdapterResponse: ...
async def get_league_matches(league_id: int, season: Optional[int]) -> AdapterResponse: ...
async def get_team(team_id: int) -> AdapterResponse: ...
async def get_matches_between_teams(team_id1: int, team_id2: int) -> AdapterResponse: ...This keeps the proxy code independent from any specific provider.
OpenLigaDBAdapter implements SportsProvider for the OpenLiga API:
- Uses
httpx.AsyncClientwith configurable timeout - Enforces rate limiting before each request
- Retries transient errors (429, 5xx, timeouts) with exponential backoff + jitter
- Logs provider calls, status codes, and latencies
API endpoints used:
GET /api/getavailableleaguesGET /api/getmatchdata/{leagueId}/{season}GET /api/getteam/{teamId}GET /api/getmatchdata/{teamId1}/{teamId2}
Configure via .env file (copy from example.env):
DEBUG=True
HOST=0.0.0.0
PORT=8000
OPENLIGADB_BASE_URL=https://www.openligadb.de
OPENLIGADB_TIMEOUT=10
RATE_LIMIT__openliga=1000 # requests per window
RATE_WINDOW__openliga=3600 # seconds
BACKOFF_BASE_DELAY=1 # seconds
BACKOFF_MAX_DELAY=32 # seconds
BACKOFF_MAX_RETRIES=3
BACKOFF_JITTER=True
LOG_LEVEL=INFO
LOG_FORMAT=json
LOG_BODY_LIMIT=1000 # chars truncated in logsPer-provider config uses __ delimiter: RATE_LIMIT__openliga, RATE_LIMIT__otherprovider, etc.