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.