Adaptive API Quick Start

This guide walks through deploying a complete Library API using the Adaptive API Gateway.

Overview

We’ll build a Library API that manages books and borrowing operations through six steps:

  1. Create OpenAPI specification

  2. Deploy spec to Kubernetes

  3. Test auto-generated endpoints

  4. Create custom Python handler

  5. Deploy handler to Kubernetes

  6. Test custom endpoints with real business logic

Step 1: Create OpenAPI Spec

Create an OpenAPI 3.0 specification defining your API:

cat > /tmp/library-api.openapi.yaml <<'EOF'
openapi: 3.0.3
info:
  title: Library API
  description: API for managing library books and borrowing operations
  version: 1.0.0

servers:
  - url: /api/adaptive/library-api
    description: SDL environment

tags:
  - name: Books
    description: Book catalog management

paths:
  /books:
    get:
      tags:
        - Books
      summary: List all books
      operationId: listBooks
      parameters:
        - name: available
          in: query
          required: false
          schema:
            type: boolean
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              example:
                books:
                  - id: 1
                    title: "The Python Handbook"
                    author: "Jane Developer"
                    isbn: "978-1234567890"
                    available: true
                count: 1

    post:
      tags:
        - Books
      summary: Add a new book
      operationId: createBook
      requestBody:
        required: true
        content:
          application/json:
            example:
              title: "New Book"
              author: "Author Name"
              isbn: "978-1234567890"
      responses:
        '201':
          description: Book created successfully
          content:
            application/json:
              example:
                id: 2
                title: "New Book"
                author: "Author Name"
                isbn: "978-1234567890"
                available: true

  /books/{bookId}:
    get:
      tags:
        - Books
      summary: Get book by ID
      operationId: getBook
      parameters:
        - name: bookId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              example:
                id: 1
                title: "The Python Handbook"
                author: "Jane Developer"
                isbn: "978-1234567890"
                available: true
        '404':
          description: Book not found
EOF

Step 2: Deploy OpenAPI Spec

Add your spec to the ConfigMap and deploy:

# Get existing specs to preserve them
kubectl get configmap df-adaptive-api-sample-specs -n svraft \
  -o jsonpath='{.data.sample-crud\.openapi\.yaml}' > /tmp/sample-crud.openapi.yaml

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

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

# Wait for pod to be ready
kubectl wait --for=condition=ready pod -l app=df-adaptive-api -n svraft --timeout=60s

Step 3: Test Auto-Generated Endpoints

Test the endpoints that auto-generate responses from the OpenAPI spec:

# List all books (returns example data from spec)
curl -u username:password https://localhost/api/adaptive/library-api/books

# Get a specific book
curl -u username:password https://localhost/api/adaptive/library-api/books/1

# Create a book (echoes request with example data)
curl -u username:password -X POST https://localhost/api/adaptive/library-api/books \
  -H "Content-Type: application/json" \
  -d '{"title":"New Book","author":"Author Name","isbn":"978-1234567890"}'
{
  "books": [
    {
      "id": 1,
      "title": "The Python Handbook",
      "author": "Jane Developer",
      "isbn": "978-1234567890",
      "available": true,
      "publishedYear": 2024,
      "addedAt": "2024-01-15T10:00:00Z"
    },
    {
      "id": 2,
      "title": "FastAPI Patterns",
      "author": "John Coder",
      "isbn": "978-0987654321",
      "available": true,
      "publishedYear": 2023,
      "addedAt": "2023-06-20T14:30:00Z"
    }
  ],
  "count": 2,
  "timestamp": "2025-11-26T14:00:00Z"
}

At this stage, all endpoints return the example data defined in your OpenAPI spec. No Python code required yet.

Step 4: Create Custom Handler

Write a Python FastAPI handler with real business logic:

cat > /tmp/library-api_handler.py <<'EOF'
"""
Library API Custom Handler - Real business logic implementation
"""
from fastapi import APIRouter, HTTPException, Query, Request
from datetime import datetime
from typing import Optional, Dict

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

# In-memory storage (replace with database in production)
books_db: Dict[int, dict] = {
    1: {
        "id": 1,
        "title": "The Python Handbook",
        "author": "Jane Developer",
        "isbn": "978-1234567890",
        "available": True,
        "publishedYear": 2024
    }
}
next_book_id = 2

@router.get("/books")
async def list_books(
    available: Optional[bool] = Query(None)
):
    """List all books with optional filtering"""
    books = list(books_db.values())

    if available is not None:
        books = [b for b in books if b["available"] == available]

    return {"books": books, "count": len(books)}

@router.post("/books")
async def create_book(request: Request):
    """Add a new book to the catalog"""
    global next_book_id
    body = await request.json()

    if "title" not in body or "author" not in body:
        raise HTTPException(status_code=400, detail="Title and author required")

    new_book = {
        "id": next_book_id,
        "title": body["title"],
        "author": body["author"],
        "isbn": body.get("isbn", f"AUTO-{next_book_id}"),
        "available": True,
        "publishedYear": body.get("publishedYear", datetime.utcnow().year)
    }

    books_db[next_book_id] = new_book
    next_book_id += 1

    return new_book

@router.get("/books/{bookId}")
async def get_book(bookId: int):
    """Get a specific book by ID"""
    if bookId not in books_db:
        raise HTTPException(status_code=404, detail=f"Book {bookId} not found")

    return books_db[bookId]
EOF

Step 5: Deploy Custom Handler

Before deploying the handler, remove the OpenAPI spec to avoid route conflicts:

# Remove library-api spec (keep sample-crud)
kubectl get configmap df-adaptive-api-sample-specs -n svraft \
  -o jsonpath='{.data.sample-crud\.openapi\.yaml}' > /tmp/sample-crud.openapi.yaml

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

# Deploy custom handler
kubectl create configmap df-adaptive-api-handlers \
  --from-file=library-api_handler.py=/tmp/library-api_handler.py \
  -n svraft --dry-run=client -o yaml | kubectl apply -f -

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

# Wait for pod to be ready
kubectl wait --for=condition=ready pod -l app=df-adaptive-api -n svraft --timeout=60s

Step 6: Test Custom Endpoints

Test the real business logic:

# List books (returns real data from in-memory storage)
curl -u username:password https://localhost/api/adaptive/library-api/books

# Add a new book (actually creates it)
curl -u username:password -X POST https://localhost/api/adaptive/library-api/books \
  -H "Content-Type: application/json" \
  -d '{"title":"Docker Deep Dive","author":"Mike Container","isbn":"978-5566778899"}'

# Verify it was added
curl -u username:password https://localhost/api/adaptive/library-api/books

# Get the new book by ID
curl -u username:password https://localhost/api/adaptive/library-api/books/2

The handler now maintains real state with business logic validation.

Advanced Topics

Multiple Handlers

You can deploy multiple handlers in the same ConfigMap:

kubectl create configmap df-adaptive-api-handlers \
  --from-file=library-api_handler.py=/tmp/library-api_handler.py \
  --from-file=weather_handler.py=/tmp/weather_handler.py \
  --from-file=products_handler.py=/tmp/products_handler.py \
  -n svraft --dry-run=client -o yaml | kubectl apply -f -

Each handler will be mounted at its own path.

Custom Endpoints Not in Spec

Handlers can add endpoints not defined in the OpenAPI spec:

@router.get("/stats")
async def get_stats():
    """Custom endpoint - not in OpenAPI spec"""
    return {"total_books": len(books_db)}

These will automatically appear in the Swagger UI.

Database Integration

For production use, replace in-memory storage with database access:

import psycopg2
from os import getenv

DB_HOST = getenv("DB_HOST", "df-backend-postgresql")
DB_NAME = getenv("DB_NAME", "datafabric")

@router.get("/books")
async def list_books():
    conn = psycopg2.connect(host=DB_HOST, database=DB_NAME, ...)
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM books")
    # ... process results

Additional Resources