15. API Documentation

Chapter 15 of 18 · 20 min

API documentation describes endpoints, request formats, response formats, and error codes. OpenAPI (Swagger) provides machine-readable documentation that can be auto-generated from code.

The FastAPI application generates OpenAPI spec automatically:

# backend/app/main.py
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
from .api import router

app = FastAPI(
    title="AI Document Q&A API",
    description="API for querying documents using local LLM inference",
    version="1.0.0",
    docs_url="/docs",
    redoc_url="/redoc",
)

app.include_router(router, prefix="/api/v1")

# Custom OpenAPI schema with authentication details
def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    
    openapi_schema = get_openapi(
        title="AI Document Q&A API",
        version="1.0.0",
        description="API for querying documents using local LLM inference",
        routes=app.routes,
    )
    
    openapi_schema["components"]["securitySchemes"] = {
        "BearerAuth": {
            "type": "http",
            "scheme": "bearer",
            "bearerFormat": "JWT"
        }
    }
    
    # Add security requirement to all endpoints
    for path in openapi_schema["paths"].values():
        for operation in path.values():
            if isinstance(operation, dict):
                operation["security"] = [{"BearerAuth": []}]
    
    app.openapi_schema = openapi_schema
    return app.openapi_schema

app.openapi = custom_openapi

Endpoint documentation with examples:

# backend/app/api/documents.py
from fastapi import APIRouter, UploadFile, File, HTTPException, Depends
from pydantic import BaseModel
from typing import List

router = APIRouter(tags=["Documents"])

class DocumentResponse(BaseModel):
    document_id: str
    filename: str
    status: str
    uploaded_at: str

@router.post("/upload", response_model=DocumentResponse)
async def upload_document(
    file: UploadFile = File(...),
    user=Depends(get_current_user)
):
    """
    Upload a PDF document for Q&A processing.
    
    The document is queued for processing, which includes:
    - Text extraction
    - Embedding generation
    - Indexing for retrieval
    
    Processing typically takes 30-60 seconds. Poll the document
    status endpoint to check if processing is complete.
    
    **Request:**
    - Content-Type: multipart/form-data
    - Body: file (PDF, max 50MB)
    
    **Response:**
    - 200: Document uploaded and queued
    - 400: Invalid file type or corrupted PDF
    - 413: File too large
    - 401: Unauthorized
    """
    if file.content_type != "application/pdf":
        raise HTTPException(400, "Only PDF files are supported")
    
    # ... upload logic ...
    
    return DocumentResponse(
        document_id=document_id,
        filename=file.filename,
        status="processing",
        uploaded_at=datetime.utcnow().isoformat()
    )

@router.get("/{document_id}", response_model=DocumentResponse)
async def get_document(
    document_id: str,
    user=Depends(get_current_user)
):
    """Get document metadata and processing status."""
    # ... retrieval logic ...

class QuestionRequest(BaseModel):
    question: str
    document_id: str

class QuestionResponse(BaseModel):
    answer: str
    sources: List[str]
    latency_ms: int

@router.post("/ask")
async def ask_question(
    request: QuestionRequest,
    user=Depends(get_current_user)
):
    """
    Ask a question about a document.
    
    The response is streamed using Server-Sent Events. The client
    should listen for events with the `data:` prefix containing
    chunks of the response.
    
    **Request:**
    ```json
    {
        "question": "What is the main topic?",
        "document_id": "550e8400-e29b-41d4-a716-446655440000"
    }
    ```
    
    **Response (SSE stream):**
    ```
    data: The
    data: main
    data: topic
    data: of
    data: this
    data: document
    data: is
    data: ...
    ```
    
    **Error codes:**
    - 400: Invalid question format
    - 404: Document not found
    - 422: Document still processing
    - 429: Rate limit exceeded
    - 503: Model server unavailable
    """

Access the interactive documentation at /docs (Swagger UI) or /redoc (ReDoc).

EXERCISE

Generate OpenAPI spec and create a Postman collection that covers all endpoints with example requests.