This repository now includes a lightweight, configurable API gateway framework built with FastAPI.
- Dynamic gateway routes loaded from YAML (
config/gateway.yaml) - Configurable upstream targets and per-upstream timeout settings
- OpenAPI docs for gateway endpoints via FastAPI (
/docs) - Optional serving of an external OpenAPI contract (
/openapi/external.json) - Administrative portal at
/admin/portal - Runtime configuration editor via admin endpoints
- API key creation/list/revocation with optional route protection
- Built-in management endpoints:
GET /healthzGET /readyzGET /admin/routes
src/gateway_framework/app.py: app factory and dynamic route registrationsrc/gateway_framework/proxy.py: request forwarding/proxy logicsrc/gateway_framework/config.py: config schema and loadersrc/gateway_framework/main.py: ASGI entrypointsrc/gateway_framework/static/admin/: scalable portal frontend assets (index.html,styles.css,app.js)config/gateway.yaml: route and upstream configurationopenapi_json/lta_datamall_openapi_v0-1-1.json: external OpenAPI contract
- Install
uvif not already installed:
curl -LsSf https://astral.sh/uv/install.sh | sh- Sync dependencies and create the project virtual environment:
uv sync- Choose a gateway profile:
config/gateway.yamltargets a locally running companion backend on127.0.0.1:8068.config/gateway.container.yamlusesGATEWAY_UPSTREAM_HOSTfor host-container networking and defaults tohost.docker.internal(setGATEWAY_UPSTREAM_HOST=host.containers.internalfor Podman), with defaultgateway_port: 8067.
- Run the gateway with the managed environment:
make run-local CONFIG_PROFILE=local- Open the docs:
http://127.0.0.1:8000/docs
To run on a different gateway port, pass GATEWAY_PORT:
make run-local CONFIG_PROFILE=local GATEWAY_PORT=8088This gateway is a good front door for the companion backend at furyhawk/lta_datamall_api.
The gateway now ships with two explicit upstream profiles:
config/gateway.yamlfor a local backend onhttp://127.0.0.1:8068config/gateway.container.yamlfor the compose servicehttp://lta-datamall-api:8000
Both profiles are aligned to the companion backend's published route surface:
- public route prefix:
/api/v1 - backend health endpoints remain available at
/healthzand/readyz
One-command profile selection:
- Local host pairing:
make run-local CONFIG_PROFILE=local - Containerized gateway:
make run CONFIG_PROFILE=container - Route parity check against the running companion backend OpenAPI:
make check-companion-parity CONFIG_PROFILE=local
Suggested local pairing workflow:
- Run
furyhawk/lta_datamall_apion port8068. - Start this gateway with
make run-local CONFIG_PROFILE=local. - Use this gateway for edge concerns such as admin config editing, gateway API keys, and response caching while the companion backend owns the LTA DataMall domain logic.
Use the bundled compose file when you want this repo to start both the gateway and the companion backend together.
- Create
.envfrom.env.exampleand setDATAMALL_API_KEY. - Start the stack:
make compose-up- Open the gateway at
http://127.0.0.1:8000.
If you use the repository defaults from .env.example, the gateway is exposed on port 8067.
Notes:
make compose-upstarts the companion backend first, checks its live/openapi.json, then starts the gateway.- The compose workflow uses Docker Compose because the companion backend is built directly from its Git repository.
- Tail logs with
make compose-logsand stop the stack withmake compose-down.
config/gateway.yaml uses this model:
settings:
title: string
version: string
description: string
gateway_port: 8067
external_openapi_file: path/to/openapi.json
cache_enabled: false
cache_ttl_seconds: 30
cache_max_entries: 500
require_api_key: false
api_keys_file: config/api_keys.json
admin_api_key_env: ADMIN_API_KEY
upstreams:
service_name:
base_url: https://service.example.com/
port: 8443
timeout_seconds: 15
routes:
- path: /api/v1/resource
methods: [GET, POST]
upstream: service_name
upstream_path: /v2/resource
strip_prefix: /api
summary: Optional operation summary
tags: [Gateway]
operation_id: gateway_resource_getNotes:
pathis the public gateway path.upstream_pathoverrides forwarded path.strip_prefixremoves a leading path segment before forwarding.portoverrides the upstream URL port without changing the host or scheme.settings.gateway_portcontrols the gateway listen port unlessGATEWAY_PORT(orPORT) is set.
- Admin UI:
GET /admin/portal - Admin static assets:
GET /admin/portal/assets/* - Admin summary:
GET /admin/dashboard - Get config YAML:
GET /admin/config - Save config YAML:
PUT /admin/config - List API keys:
GET /admin/api-keys - Create API key:
POST /admin/api-keyswith body{"name":"client-name"} - Revoke API key:
DELETE /admin/api-keys/{key_id} - Cache status:
GET /admin/cache - Invalidate cache by route/query fragment:
POST /admin/cache/invalidate - Clear all cache entries:
DELETE /admin/cache
Optional admin auth:
- Set environment variable
ADMIN_API_KEYbefore startup. - Send header
x-admin-key: <your-admin-key>to admin endpoints.
- Enable gateway key checks by setting
settings.require_api_key: true. - Clients then must send
x-api-key: <key>on proxied route requests. - Create keys from the admin portal or
POST /admin/api-keys.
- Enable cache with
settings.cache_enabled: true. - Configure TTL with
settings.cache_ttl_seconds. - Configure memory bound with
settings.cache_max_entries. - Cache currently applies to proxied
GET/HEADresponses with2xxstatus. - Gateway adds
x-gateway-cache: MISSfor first fetch andx-gateway-cache: HITfor cached responses. - Admin invalidation payload example:
{"path": "/api/v1/bus-arrival", "method": "GET", "query_contains": "BusStopCode=12345"}
- Add routes through config, not code, for repeatable deployments.
- Split large configurations into environment-specific files and set
GATEWAY_CONFIG_PATH. - Keep route changes backward compatible (
/api/v1stability) for client safety. - Define explicit timeout values per upstream to isolate slow dependencies.
- Add auth/rate limiting middleware for production.
- Add OpenAPI linting in CI (for example, Redocly CLI).
- Add contract tests for every configured route.
- Run tests:
uv run pytest - Run lint:
uv run ruff check . - Add dependency:
uv add <package> - Add dev dependency:
uv add --dev <package>
The repository includes a containerized workflow via Dockerfile and Makefile.
- Install either Docker or Podman.
- By default, Makefile uses Podman when available; otherwise Docker.
make buildmake run CONFIG_PROFILE=localGateway will be available at http://127.0.0.1:8000.
Use CONFIG_PROFILE=container when the upstream backend is running inside the compose network.
make logs
make stopCONTAINER_ENGINE=docker(orpodman)PORT=8067GATEWAY_PORT=8067GATEWAY_UPSTREAM_HOST=host.docker.internal(set tohost.containers.internalfor Podman host networking)IMAGE_NAME=openapi-api-gatewayIMAGE_TAG=latestENV_FILE=.env
Example:
make build CONTAINER_ENGINE=docker IMAGE_TAG=dev
make run CONTAINER_ENGINE=docker PORT=8080 GATEWAY_PORT=8080 ENV_FILE=.env CONFIG_PROFILE=local- Automated image publishing is configured in
.github/workflows/release-image.yml. - On push of a release tag (for example
v0.2.1), CI builds and pushes:ghcr.io/furyhawk/api_gateway:v0.2.1ghcr.io/furyhawk/api_gateway:latest
Pull example:
docker pull ghcr.io/furyhawk/api_gateway:v0.2.1