Adaptive API

The Adaptive API Gateway enables on-the-fly API extensibility in airgapped environments. Deploy new APIs via ConfigMaps without rebuilding Docker images.

Overview

The /api/adaptive endpoint provides a dynamic API gateway that supports:

  • Auto-generation of working endpoints from OpenAPI 3.0 specifications

  • Hot-reload of new APIs via ConfigMaps (no pod restart required)

  • Custom Python handler injection for business logic

  • Integration with SDL Swagger UI

  • Full CRUD support (GET, POST, PUT, DELETE, PATCH)

How It Works

The Adaptive API Gateway uses a two-phase approach:

Phase 1: OpenAPI Spec (Auto-Generated Endpoints)

Create an OpenAPI 3.0 specification and deploy it as a ConfigMap. The gateway automatically generates working endpoints that return example data from the spec.

Use cases:

  • API design and prototyping

  • Frontend development (mock backend)

  • Contract-first development

  • API documentation

Phase 2: Custom Python Handlers (Real Business Logic)

Write Python FastAPI handlers and deploy them as ConfigMaps. Custom handlers override auto-generated routes with real business logic.

Use cases:

  • Database integration

  • External API calls

  • Business logic validation

  • Production implementations

Authentication

Authentication is handled by the SDL API Gateway, not by the Adaptive API itself. The Adaptive API requires sdl-api to be deployed as an upstream dependency.

All requests to /api/adaptive/* are routed through the SDL API Gateway, which enforces authentication using:

  • Basic Auth (df_basic): Username/password authentication

  • Bearer JWT (df_bearer_jwt): JWT tokens from Keycloak

# Using Basic Auth
curl -u username:password https://localhost/api/adaptive/my-api/endpoint

# Using Bearer Token
curl -H "Authorization: Bearer $TOKEN" https://localhost/api/adaptive/my-api/endpoint

Swagger UI Authentication

  1. Open https://localhost/api/adaptive/swagger-ui/

  2. Click the Authorize button (lock icon)

  3. Choose authentication method:

    • df_basic: Enter username and password

    • df_bearer_jwt: Enter JWT token from Keycloak

  4. Click Authorize

  5. All "Try it out" requests will include authentication

The security schemes in Swagger UI are for documentation purposes. Actual authentication enforcement happens at the SDL API Gateway layer.

Direct Access

When accessing the Adaptive API service directly (bypassing the SDL API Gateway), no authentication is enforced. This is intended for:

  • Kubernetes health probes

  • Internal service-to-service communication

  • Development and testing environments

For production deployments, always access through the SDL API Gateway.

Quick Start

See Adaptive API Quick Start for a complete example of deploying a Library API with both auto-generated and custom endpoints.

Gateway Endpoints

Documentation

  • GET /api/adaptive/swagger-ui/ - Interactive Swagger UI documentation

  • GET /api/adaptive/openapi.json - OpenAPI 3.0 schema

Management

  • GET /api/adaptive/ - Gateway information and endpoint listing

  • GET /api/adaptive/actuator/health/liveness - Kubernetes liveness probe

  • GET /api/adaptive/actuator/health/readiness - Kubernetes readiness probe

  • GET /api/adaptive/actuator/info - Application information (requires auth)

  • POST /api/adaptive/actuator/refresh - Hot-reload routes and handlers (requires auth)

Custom APIs

All APIs you deploy will be mounted under:

  • /api/adaptive/<api-name>/* - Your custom API endpoints

Example: If you deploy a library-api, endpoints will be at /api/adaptive/library-api/books, etc.

Adding New APIs

Option 1: OpenAPI Spec Only (Auto-Generated)

Perfect for API design, prototyping, and frontend development.

  1. Create your OpenAPI 3.0 specification

  2. Deploy as ConfigMap

  3. Gateway auto-generates working endpoints

  4. Returns example data from spec

# Create ConfigMap with your spec
kubectl get configmap df-adaptive-api-sample-specs -n svraft \
  -o jsonpath='{.data.sample-crud\.openapi\.yaml}' > /tmp/existing-spec.yaml

kubectl create configmap df-adaptive-api-sample-specs \
  --from-file=sample-crud.openapi.yaml=/tmp/existing-spec.yaml \
  --from-file=my-api.openapi.yaml=/tmp/my-api.openapi.yaml \
  -n svraft --dry-run=client -o yaml | kubectl apply -f -

# Restart deployment
kubectl rollout restart deployment df-adaptive-api -n svraft

Option 2: Custom Python Handler (Real Logic)

For production implementations with real business logic.

  1. Write Python FastAPI handler

  2. Deploy as ConfigMap

  3. Handlers override auto-generated routes

  4. Full access to FastAPI features

# my_handler.py
from fastapi import APIRouter, HTTPException

router = APIRouter(tags=["My API"])

@router.get("/items")
async def list_items():
    return {"items": [{"id": 1, "name": "Item 1"}]}

@router.get("/items/{item_id}")
async def get_item(item_id: int):
    if item_id == 1:
        return {"id": 1, "name": "Item 1"}
    raise HTTPException(status_code=404, detail="Not found")
# Deploy handler
kubectl create configmap df-adaptive-api-handlers \
  --from-file=my_handler.py=/tmp/my_handler.py \
  -n svraft --dry-run=client -o yaml | kubectl apply -f -

# Restart deployment
kubectl rollout restart deployment df-adaptive-api -n svraft

Your API will be available at /api/adaptive/my (filename my_handler.py → mount path /my).

Path Mapping Convention

Handler filenames determine the API mount path:

Handler Filename Mount Path Example Endpoint

library-api_handler.py

/library-api

/api/adaptive/library-api/books

weather_handler.py

/weather

/api/adaptive/weather/seattle

products_handler.py

/products

/api/adaptive/products/123

Rules:

  • _handler suffix is removed

  • Underscores (_) are converted to hyphens (-)

  • Routes in handler are relative to mount path

Hot Reload

Reload routes without pod restart:

curl -u username:password -X POST https://localhost/api/adaptive/actuator/refresh

Returns:

{
  "status": "success",
  "message": "Routes reloaded successfully",
  "handlers": ["library-api_handler.py"],
  "specs": ["sample-crud.openapi.yaml"]
}
ConfigMap changes require kubectl rollout restart to remount updated volumes. Hot-reload only works for changes to already-mounted files.

Configuration

Environment variables (set in Helm values):

Variable Description Default

AUTH_ENABLED

Enable authentication enforcement

true

KEYCLOAK_URL

Keycloak server URL

http://sdl.local/auth

KEYCLOAK_REALM

Keycloak realm name

data-fabric

SPECS_DIR

Directory containing OpenAPI specs

/app/specs

HANDLERS_DIR

Directory containing custom handlers

/app/handlers

RELOAD_ON_CHANGE

Enable hot-reload

true

Examples

See Adaptive API Quick Start for a complete walkthrough including:

  • Creating OpenAPI specifications

  • Deploying to Kubernetes

  • Testing auto-generated endpoints

  • Adding custom Python handlers

  • Authentication

Troubleshooting

Specs Not Loading

Check ConfigMaps and logs:

# View specs ConfigMap
kubectl get configmap df-adaptive-api-sample-specs -n svraft -o yaml

# Check logs for loading errors
kubectl logs -l app.kubernetes.io/name=df-adaptive-api -n svraft -c api --tail=100

Handler Errors

Check Python syntax and imports:

# View handler logs
kubectl logs -l app.kubernetes.io/name=df-adaptive-api -n svraft -c api -f

# Test handler syntax locally
python -m py_compile my_handler.py

Authentication Failures

Verify credentials and check if auth is enabled:

# Check if auth is enabled
kubectl get deployment df-adaptive-api -n svraft \
  -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="AUTH_ENABLED")].value}'

# Disable auth for testing
kubectl set env deployment/df-adaptive-api -n svraft AUTH_ENABLED=false
kubectl rollout restart deployment df-adaptive-api -n svraft

Route Conflicts (Spec vs Handler)

If both an OpenAPI spec and custom handler define the same routes, the spec-generated route may take precedence.

Solution: Remove the OpenAPI spec from the ConfigMap when deploying a custom handler:

# Keep only other specs, remove conflicting one
kubectl get configmap df-adaptive-api-sample-specs -n svraft \
  -o jsonpath='{.data.sample-crud\.openapi\.yaml}' > /tmp/sample-crud.yaml

kubectl create configmap df-adaptive-api-sample-specs \
  --from-file=sample-crud.openapi.yaml=/tmp/sample-crud.yaml \
  -n svraft --dry-run=client -o yaml | kubectl apply -f -

kubectl rollout restart deployment df-adaptive-api -n svraft