-
-
Notifications
You must be signed in to change notification settings - Fork 0
API Patterns
Last Updated: 2025-10-27 Status: ✅ Active - Both patterns implemented with platform-agnostic sync validation Enforcement: Pre-commit hooks (PRIMARY) + Makefile + Optional CI/CD
- Overview
- Two Implementation Patterns
- Architecture
- Synchronization Strategy
- Developer Workflows
- Technical Details
- Testing Strategy
- CI/CD Integration
- Troubleshooting
- Examples
This reference implementation demonstrates two real-world API development patterns:
- Code-First Pattern - Implementation drives documentation
- API-First Pattern - Contract drives implementation
Both patterns are kept in perfect synchronization through automated validation, ensuring developers can learn from accurate, working examples of both approaches.
Different projects need different approaches:
| Pattern | Best For | Common In |
|---|---|---|
| Code-First | Rapid prototyping, startups, internal tools | FastAPI projects, Django REST, small teams |
| API-First | Microservices, public APIs, large teams | Enterprise, banks, public APIs, SaaS platforms |
This project shows both so you can choose the right pattern for your needs.
Philosophy: Write code, documentation follows automatically.
Code → Auto-Generated Docs → Runtime OpenAPI
Directory Structure:
reference-apps/fastapi/
├── app/
│ ├── main.py # FastAPI app definition
│ ├── routers/ # Endpoint implementations
│ │ ├── health.py
│ │ ├── vault_demo.py
│ │ └── redis_cluster.py
│ └── models/ # Pydantic models
├── Dockerfile
└── README.md
Access Points:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
- OpenAPI JSON: http://localhost:8000/openapi.json
When to Use:
- ✅ Rapid prototyping
- ✅ Small teams (1-3 developers)
- ✅ Internal tools
- ✅ Learning FastAPI
- ✅ Iterating on design quickly
Pros:
- Fast initial development
- Less tooling required
- Direct code changes
- Hot reload in development
Cons:
- Documentation can drift
- Harder to coordinate multiple teams
- No client SDK generation before implementation
- Contract changes are implicit
Philosophy: Design contract first, generate code from specification.
OpenAPI Spec → Code Generation → Implementation → Validation
Directory Structure:
reference-apps/
├── shared/
│ ├── openapi.yaml # ⭐ SINGLE SOURCE OF TRUTH
│ └── test-suite/ # Shared tests for both
│ ├── test_health.py
│ ├── test_vault.py
│ └── test_redis.py
├── fastapi-api-first/
│ ├── generated/ # Auto-generated (don't edit)
│ │ ├── main.py
│ │ ├── models/
│ │ └── apis/
│ ├── custom/ # ✏️ Your business logic
│ │ ├── implementations.py
│ │ └── services/
│ ├── codegen.sh # Regenerate from spec
│ └── README.md
└── scripts/
├── validate-sync.sh # Core sync validation
├── sync-report.sh # Detailed diff reports
├── regenerate-api-first.sh # Regenerate API-first
├── install-hooks.sh # Install pre-commit hooks
└── hooks/
└── pre-commit # PRIMARY enforcement layer
Access Points:
- Swagger UI: http://localhost:8001/docs
- ReDoc: http://localhost:8001/redoc
- OpenAPI JSON: http://localhost:8001/openapi.json
When to Use:
- ✅ Microservices architecture
- ✅ Multiple teams (frontend/backend)
- ✅ Public/external APIs
- ✅ Client SDK generation needed
- ✅ Contract testing required
- ✅ Enterprise environments
Pros:
- Contract is guaranteed
- Generate clients in any language
- Teams can work in parallel
- Design review before implementation
- Breaking changes are explicit
Cons:
- More initial setup
- Requires code generation tooling
- Slower iteration (spec → generate → implement)
- Learning curve for tooling
┌─────────────────────────────────────────────────────────────┐
│ Shared OpenAPI Spec │
│ (shared/openapi.yaml) │
│ SINGLE SOURCE OF TRUTH │
└──────────────────────┬──────────────────────────────────────┘
│
┌─────────────┴─────────────┐
↓ ↓
┌────────────────────┐ ┌────────────────────┐
│ Code-First │ │ API-First │
│ Implementation │ │ Implementation │
│ │ │ │
│ Manual Code │ │ Generated Code │
│ (fastapi/) │ │ (fastapi-api- │
│ │ │ first/generated/)│
│ Port: 8000 │ │ Port: 8001 │
└────────┬───────────┘ └──────┬─────────────┘
│ │
└──────────┬──────────────┘
↓
┌────────────────────┐
│ Shared Test Suite │
│ (Both must pass) │
└────────────────────┘
↓
┌────────────────────┐
│ CI/CD Validation │
│ (Enforces sync) │
└────────────────────┘
Both implementations are validated against the shared OpenAPI specification:
1. Extract OpenAPI from code-first (runtime)
2. Compare with shared/openapi.yaml
3. Run shared tests against both implementations
4. If any differences → CI FAILS ❌
5. If all match → CI PASSES ✅
This ensures both implementations are always identical in behavior.
Multiple implementations will drift apart without enforcement:
Week 1: Both match ✅
Week 2: Added endpoint to code-first only ❌
Week 3: Changed response in API-first only ❌
Week 4: Completely different APIs ❌❌❌
Four-Layer Protection Strategy:
# scripts/hooks/pre-commit
# Runs BEFORE every commit - catches issues locally BEFORE push
# This is the PRIMARY enforcement mechanism (CI/CD is just a safety net)
if git diff --cached --name-only | grep -q "openapi.yaml"; then
# Validate OpenAPI YAML syntax
yq eval . "$SHARED_SPEC" >/dev/null 2>&1
fi
if git diff --cached --name-only | grep -qE "fastapi.*\.py"; then
# Check API synchronization if both implementations running
if both_apis_running; then
make sync-check || exit 1
fi
fi
# Install with: make install-hooks
# Bypass with: git commit --no-verify (discouraged)# Makefile - Platform-agnostic standard interface
# Works with ANY CI/CD system (GitHub Actions, GitLab CI, Jenkins, etc.)
validate: ## Run all validation checks (use in CI/CD)
@$(MAKE) validate-spec
@$(MAKE) sync-check
@$(MAKE) test
sync-check: ## Check if both implementations match OpenAPI spec
@make sync-check
sync-report: ## Generate detailed synchronization report
@./scripts/sync-report.sh
regenerate: ## Regenerate API-first from shared spec
@make regenerate
install-hooks: ## Install pre-commit hooks
@./scripts/install-hooks.sh
# Any CI/CD system invokes: make validate# scripts/validate-sync.sh - Core validation logic
# Platform-agnostic (works on any Unix-like system)
# Google-style documented functions
# 1. Extract specs from running APIs
extract_spec "$CODE_FIRST_URL" "code-first.yaml"
extract_spec "$API_FIRST_URL" "api-first.yaml"
# 2. Normalize specs (remove implementation-specific fields)
normalize_spec "code-first.yaml" "code-first-normalized.yaml"
# 3. Compare normalized specs
compare_specs "code-first-normalized.yaml" "shared/openapi.yaml"
# Exit codes: 0=synced, 1=out of sync, 2=APIs unreachable# Works with ANY CI/CD system - just invoke Makefile targets
# GitHub Actions:
- run: make validate
# GitLab CI:
script:
- make validate
# Jenkins:
sh 'make validate'
# Platform-agnostic design - NO GitHub dependencyUse when: Prototyping, exploring design
# Step 1: Add endpoint to code-first implementation
vim reference-apps/fastapi/app/routers/my_feature.py# my_feature.py
@router.get("/my-feature/data")
async def get_my_feature_data():
return {"data": "example"}# Step 2: Test it works
curl http://localhost:8000/my-feature/data
# Step 3: Extract new OpenAPI spec
make extract-openapi
# Step 4: Update shared spec
# Manually review and update reference-apps/shared/openapi.yaml
# Step 5: Regenerate API-first implementation
make regenerate
# Step 6: Implement business logic in API-first
vim reference-apps/fastapi-api-first/custom/implementations.py
# Step 7: Add shared tests
vim reference-apps/shared/test-suite/test_my_feature.py
# Step 8: Validate synchronization
make sync-check
# Step 9: Run all tests
make test
# Step 10: Commit changes
git add .
git commit -m "Add new feature endpoint"
# Pre-commit hook validates sync automatically ✓Expected Time: 15-30 minutes
Use when: Coordinating with other teams, public API
# Step 1: Design API in shared spec
vim reference-apps/shared/openapi.yaml# Add to openapi.yaml
paths:
/my-feature/data:
get:
summary: Get my feature data
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
properties:
data:
type: string# Step 2: Validate spec syntax
spectral lint reference-apps/shared/openapi.yaml
# Step 3: Regenerate API-first implementation
make regenerate
# Step 4: Implement business logic
vim reference-apps/fastapi-api-first/custom/implementations.py
# Step 5: Test API-first works
curl http://localhost:8001/my-feature/data
# Step 6: Update code-first to match
vim reference-apps/fastapi/app/routers/my_feature.py
# Implement identical endpoint
# Step 7: Add shared tests
vim reference-apps/shared/test-suite/test_my_feature.py
# Step 8: Validate synchronization
make sync-check
# Step 9: Run all tests
cd reference-apps/shared/test-suite
pytest -v
# Step 10: Commit changes
git add .
git commit -m "Add new feature endpoint"Expected Time: 20-40 minutes
Always start with shared spec:
# Step 1: Update shared/openapi.yaml
vim reference-apps/shared/openapi.yaml
# Change response schema, add field, etc.
# Step 2: Regenerate API-first
make regenerate
# Step 3: Update business logic in API-first
vim reference-apps/fastapi-api-first/custom/implementations.py
# Step 4: Update code-first manually
vim reference-apps/fastapi/app/routers/existing_feature.py
# Step 5: Update shared tests
vim reference-apps/shared/test-suite/test_existing_feature.py
# Step 6: Validate sync
make sync-check
# Step 7: Commit
git add .
git commit -m "Update existing endpoint response"Quick check:
make sync-checkOutput:
✓ Starting code-first API...
✓ Extracting OpenAPI spec...
✓ Comparing specifications...
✓ Running shared tests (code-first)...
✓ Running shared tests (API-first)...
✓ Contract testing (code-first)...
✓ Contract testing (API-first)...
✅ APIs are synchronized - all validations passed
Detailed report:
make sync-reportOutput:
# API Synchronization Report
## Status: ❌ OUT OF SYNC
## Differences Found:
### Missing in Code-First:
- POST /my-feature/data (defined in shared spec)
### Extra in Code-First:
- GET /debug/internal (not in shared spec)
### Schema Differences:
- /health/all response: code-first has extra field "uptime"
## Action Required:
1. Add POST /my-feature/data to code-first, OR
2. Remove from shared spec
3. Remove GET /debug/internal from code-first, OR
4. Add to shared spec
5. Align /health/all response schemas
Follow the action steps above to fix synchronization.
When CI/pre-commit hooks are failing due to sync issues:
# Step 1: Check current status
make status
# Step 2: Generate detailed report
make sync-report > /tmp/sync-diff.txt
cat /tmp/sync-diff.txt
# Step 3: Option A - Make code-first authoritative
# Extract spec from code-first
make extract-openapi
# Manually update shared/openapi.yaml based on extracted spec
# Regenerate API-first from updated shared spec
make regenerate
# Step 4: Option B - Make shared spec authoritative
# Regenerate API-first from shared spec
make regenerate
# Manually update code-first routers to match shared spec
# Step 5: Validate synchronization
make sync-check
# Step 6: Run all tests
make test
# Step 7: Commit (pre-commit hook will validate automatically)
git add .
git commit -m "Fix API synchronization"# shared/openapi.yaml
openapi: 3.0.0
info:
title: DevStack Core Reference API
version: 1.0.0
description: |
Reference implementation demonstrating infrastructure integration patterns.
Features:
- Health monitoring for all services
- Vault secret management examples
- Database connectivity (PostgreSQL, MySQL, MongoDB)
- Redis cluster operations
- RabbitMQ messaging patterns
⚠️ This is a REFERENCE implementation for learning, not production code.
servers:
- url: http://localhost:8000
description: Code-First Implementation
- url: http://localhost:8001
description: API-First Implementation
security:
- ApiKeyAuth: []
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
schemas:
HealthStatus:
type: object
required:
- status
- services
properties:
status:
type: string
enum: [healthy, degraded, unhealthy]
services:
type: object
additionalProperties:
$ref: '#/components/schemas/ServiceHealth'
ServiceHealth:
type: object
required:
- status
properties:
status:
type: string
enum: [healthy, unhealthy]
details:
type: object
ErrorResponse:
type: object
required:
- error
- message
properties:
error:
type: string
message:
type: string
details:
type: object
status_code:
type: integer
paths:
/health/all:
get:
summary: Check health of all infrastructure services
description: |
Returns aggregate health status for:
- Vault
- PostgreSQL
- MySQL
- MongoDB
- Redis (cluster)
- RabbitMQ
tags:
- Health
responses:
'200':
description: Health status retrieved
content:
application/json:
schema:
$ref: '#/components/schemas/HealthStatus'
examples:
all_healthy:
summary: All services healthy
value:
status: healthy
services:
vault:
status: healthy
details:
initialized: true
sealed: false
postgres:
status: healthy
redis:
status: healthy
details:
cluster_state: ok
nodes: 3
'503':
description: One or more services unhealthy
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'# reference-apps/fastapi-api-first/openapi-generator-config.yaml
generatorName: python-fastapi
inputSpec: ../shared/openapi.yaml
outputDir: ./generated
additionalProperties:
packageName: app
projectName: devstack-core-api-first
packageVersion: 1.0.0
globalProperties:
models: true
apis: true
supportingFiles: true
templateDir: ./templates # Custom templates if needed#!/bin/bash
# scripts/validate-sync.sh
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Colors
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
info() { echo -e "${GREEN}✓${NC} $1"; }
error() { echo -e "${RED}✗${NC} $1"; }
warn() { echo -e "${YELLOW}⚠${NC} $1"; }
# Step 1: Start services
info "Starting services..."
cd "$PROJECT_ROOT"
./devstack.sh start
sleep 30
# Step 2: Wait for both APIs
info "Waiting for APIs to be ready..."
for i in {1..30}; do
if curl -sf http://localhost:8000/health >/dev/null && \
curl -sf http://localhost:8001/health >/dev/null; then
break
fi
sleep 2
done
# Step 3: Extract OpenAPI from code-first
info "Extracting OpenAPI from code-first..."
curl -sf http://localhost:8000/openapi.json > /tmp/code-first-openapi.json
# Step 4: Compare with shared spec
info "Comparing specifications..."
docker run --rm \
-v "$PROJECT_ROOT:/work" \
-v "/tmp:/tmp" \
openapitools/openapi-diff:latest \
/work/reference-apps/shared/openapi.yaml \
/tmp/code-first-openapi.json \
--fail-on-incompatible
if [ $? -ne 0 ]; then
error "SPEC MISMATCH: Code-first doesn't match shared spec"
echo "Run 'make sync-report' for details"
exit 1
fi
# Step 5: Run shared test suite (code-first)
info "Running shared tests (code-first)..."
cd "$PROJECT_ROOT/reference-apps/shared/test-suite"
pytest --api-url=http://localhost:8000 -v --tb=short
# Step 6: Run shared test suite (API-first)
info "Running shared tests (API-first)..."
pytest --api-url=http://localhost:8001 -v --tb=short
# Step 7: Contract testing (code-first)
info "Contract testing (code-first)..."
dredd "$PROJECT_ROOT/reference-apps/shared/openapi.yaml" \
http://localhost:8000
# Step 8: Contract testing (API-first)
info "Contract testing (API-first)..."
dredd "$PROJECT_ROOT/reference-apps/shared/openapi.yaml" \
http://localhost:8001
info "APIs are synchronized - all validations passed"Code-First:
cd reference-apps/fastapi
pytest tests/ -vAPI-First:
cd reference-apps/fastapi-api-first
pytest tests/ -vPurpose: Test implementation-specific logic.
cd reference-apps/shared/test-suite
pytest -vPurpose: Ensure both implementations behave identically.
Example:
# shared/test-suite/test_redis_cluster.py
@pytest.mark.parametrize("api_url", [
"http://localhost:8000", # Code-first
"http://localhost:8001", # API-first
])
class TestRedisCluster:
async def test_cluster_nodes(self, api_url):
async with httpx.AsyncClient() as client:
response = await client.get(f"{api_url}/redis/cluster/nodes")
assert response.status_code == 200
data = response.json()
assert "nodes" in data
assert data["total_nodes"] == 3# Validates API matches OpenAPI spec exactly
dredd shared/openapi.yaml http://localhost:8000
dredd shared/openapi.yaml http://localhost:8001Purpose: Ensure implementation matches contract.
Both implementations must maintain:
- ✅ 80%+ code coverage (unit tests)
- ✅ 100% endpoint coverage (shared tests)
- ✅ 100% schema validation (contract tests)
# .github/workflows/api-sync-validation.yml
name: API Synchronization Validation
on:
pull_request:
paths:
- 'reference-apps/**'
- 'scripts/**'
push:
branches: [main, develop]
jobs:
validate-api-sync:
name: Validate Both API Implementations Match
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Start infrastructure
run: |
./devstack.sh start
sleep 30
- name: Wait for services
run: |
timeout 300 bash -c 'until curl -sf http://localhost:8000/health && curl -sf http://localhost:8001/health; do sleep 5; done'
- name: Extract OpenAPI from code-first
run: |
curl http://localhost:8000/openapi.json > /tmp/code-first.json
- name: Install validation tools
run: |
npm install -g dredd @stoplight/spectral-cli
pip install pytest pytest-asyncio httpx
- name: Validate shared spec syntax
run: |
spectral lint reference-apps/shared/openapi.yaml --fail-severity=warn
- name: Compare specifications
run: |
docker run --rm \
-v "${PWD}:/work" \
-v "/tmp:/tmp" \
openapitools/openapi-diff:latest \
/work/reference-apps/shared/openapi.yaml \
/tmp/code-first.json \
--fail-on-incompatible
- name: Run shared test suite (both implementations)
run: |
cd reference-apps/shared/test-suite
pytest -v --tb=short
- name: Contract test code-first
run: |
dredd reference-apps/shared/openapi.yaml http://localhost:8000
- name: Contract test API-first
run: |
dredd reference-apps/shared/openapi.yaml http://localhost:8001
- name: Generate sync report on failure
if: failure()
run: |
./scripts/sync-report.sh > sync-report.md
cat sync-report.md
- name: Upload sync report
if: failure()
uses: actions/upload-artifact@v3
with:
name: sync-report
path: sync-report.md
- name: Comment on PR
if: failure() && github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('sync-report.md', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## ❌ API Synchronization Failed\n\n${report}`
});# .git/hooks/pre-commit
#!/bin/bash
# Only validate if API files changed
if git diff --cached --name-only | grep -q "reference-apps/"; then
echo "🔍 Validating API synchronization..."
make sync-check
if [ $? -ne 0 ]; then
echo ""
echo "❌ Pre-commit validation failed"
echo "Fix synchronization issues before committing"
echo "Run 'make sync-report' for details"
exit 1
fi
echo "✅ API synchronization validated"
fiInstallation:
# Install pre-commit hooks (creates symlink for easy updates)
make install-hooks
# Hooks will now run automatically on every commit
# Bypass with: git commit --no-verify (discouraged)
# Test hook manually
.git/hooks/pre-commitSymptom:
✗ SPEC MISMATCH: Code-first doesn't match shared spec
Run 'make sync-report' for details
Solution:
# Step 1: See what's different
make sync-report
# Step 2: Decide which is authoritative
# Option A: Code-first is correct
make extract-openapi
# Manually update shared/openapi.yaml
make regenerate
# Option B: Shared spec is correct
make regenerate
# Manually update code-first routers to match
# Step 3: Validate
make sync-checkSymptom:
test_health_all[http://localhost:8000] PASSED
test_health_all[http://localhost:8001] FAILED
Solution:
# Step 1: Check logs for that implementation
docker logs dev-reference-api-first
# Step 2: Test manually
curl http://localhost:8001/health/all
# Step 3: Compare responses
diff <(curl -s http://localhost:8000/health/all | jq -S .) \
<(curl -s http://localhost:8001/health/all | jq -S .)
# Step 4: Fix the failing implementation
vim reference-apps/fastapi-api-first/custom/implementations.py
# Step 5: Re-test
pytest shared/test-suite/test_health.py -vSymptom:
dredd shared/openapi.yaml http://localhost:8000
fail: GET /health/all returns extra field "uptime"
Solution:
# Step 1: Determine if field should exist
# If YES: Update shared spec
vim reference-apps/shared/openapi.yaml
# Add "uptime" field to schema
# If NO: Remove from implementation
vim reference-apps/fastapi/app/routers/health.py
# Remove "uptime" from response
# Step 2: Re-validate
make sync-checkSymptom:
Error: Unable to generate code from specification
Solution:
# Step 1: Validate spec syntax
spectral lint reference-apps/shared/openapi.yaml
# Step 2: Fix any errors in spec
vim reference-apps/shared/openapi.yaml
# Step 3: Try generation again
make regenerate
# Step 4: Check generator logs
cat reference-apps/fastapi-api-first/generated/.openapi-generator/FILESScenario: Add /health/redis-cluster endpoint to check Redis cluster specifically.
Using API-First:
# 1. Update shared spec
vim reference-apps/shared/openapi.yaml# Add to paths:
/health/redis-cluster:
get:
summary: Check Redis cluster health specifically
tags:
- Health
responses:
'200':
description: Redis cluster health
content:
application/json:
schema:
type: object
properties:
cluster_state:
type: string
enum: [ok, fail]
nodes:
type: integer
slots_covered:
type: integer# 2. Regenerate API-first
make regenerate
# 3. Implement in API-first
vim reference-apps/fastapi-api-first/custom/implementations.pyasync def get_redis_cluster_health():
# Implementation
creds = await vault_client.get_secret("redis-1")
client = redis.Redis(host=settings.REDIS_HOST, password=creds["password"])
info = await client.execute_command("CLUSTER", "INFO")
await client.close()
return {
"cluster_state": "ok" if "cluster_state:ok" in info else "fail",
"nodes": 3,
"slots_covered": 16384
}# 4. Update code-first to match
vim reference-apps/fastapi/app/routers/health.py@router.get("/redis-cluster")
async def redis_cluster_health():
"""Check Redis cluster health"""
# Same implementation as API-first
...# 5. Add shared test
vim reference-apps/shared/test-suite/test_health.py@pytest.mark.parametrize("api_url", ["http://localhost:8000", "http://localhost:8001"])
async def test_redis_cluster_health(api_url):
async with httpx.AsyncClient() as client:
response = await client.get(f"{api_url}/health/redis-cluster")
assert response.status_code == 200
data = response.json()
assert "cluster_state" in data
assert data["cluster_state"] in ["ok", "fail"]
assert "nodes" in data# 6. Validate and test
make sync-check
cd reference-apps/shared/test-suite
pytest test_health.py::test_redis_cluster_health -vScenario: Add timestamp field to all health check responses.
# 1. Update shared spec
vim reference-apps/shared/openapi.yaml# Update HealthStatus schema
components:
schemas:
HealthStatus:
type: object
required:
- status
- services
- timestamp # NEW
properties:
status:
type: string
enum: [healthy, degraded, unhealthy]
services:
type: object
timestamp: # NEW
type: string
format: date-time
example: "2025-10-27T12:34:56Z"# 2. Regenerate API-first
make regenerate
# 3. Update implementations
# API-first:
vim reference-apps/fastapi-api-first/custom/implementations.py
# Code-first:
vim reference-apps/fastapi/app/routers/health.py# Both implementations:
from datetime import datetime, timezone
@router.get("/all")
async def health_all():
# ... existing logic ...
return {
"status": status,
"services": services,
"timestamp": datetime.now(timezone.utc).isoformat() # NEW
}# 4. Update shared tests
vim reference-apps/shared/test-suite/test_health.pyasync def test_health_all(api_url):
response = await client.get(f"{api_url}/health/all")
data = response.json()
assert "timestamp" in data
# Validate ISO format
datetime.fromisoformat(data["timestamp"].replace("Z", "+00:00"))# 5. Validate
make sync-checkThis documentation provides:
- ✅ Complete understanding of both patterns
- ✅ Step-by-step workflows for all common tasks
- ✅ Technical details for implementation
- ✅ Testing strategy to ensure quality
- ✅ CI/CD integration for automation
- ✅ Troubleshooting guide for common issues
- ✅ Real examples you can follow
- Shared OpenAPI spec is the single source of truth
- Both implementations must behave identically
- Automated validation prevents drift
- CI/CD enforces synchronization
- Shared tests guarantee compatibility
- Read through workflows relevant to your use case
- Try adding a simple endpoint using both patterns
- Run validation scripts to see them in action
- Review CI/CD workflow to understand automation
Questions? See TROUBLESHOOTING or check the individual README files in each implementation directory.
Last Updated: 2025-10-27 | Maintained By: Development Team