Appearance
API Endpoint Generator — Med Tracker
Generate a complete CRUD API endpoint following the established Med Tracker patterns.
Usage
/api-endpoint <resource_name>Example: /api-endpoint allergies
What Gets Created
For a resource named {name}:
| File | Purpose |
|---|---|
backend/app/schemas/{name}.py | Pydantic v2 Create/Update/Response schemas |
backend/app/api/v1/{name}.py | FastAPI router with CRUD endpoints |
backend/tests/test_{name}_endpoints.py | pytest-asyncio test suite |
Update backend/app/main.py | Register new router |
Schema Pattern (app/schemas/{name}.py)
python
from pydantic import BaseModel, Field
from datetime import datetime
class {Name}Create(BaseModel):
# Required fields with validation
field: str = Field(..., min_length=1, max_length=200)
class {Name}Update(BaseModel):
# All fields optional for partial update
field: str | None = None
class {Name}Response(BaseModel):
id: str
user_id: str
created_at: datetime
updated_at: datetime
# ... resource fieldsRoute Pattern (app/api/v1/{name}.py)
python
from fastapi import APIRouter, Depends, HTTPException
from app.api.deps import get_current_user, get_supabase_client
from app.schemas.{name} import {Name}Create, {Name}Update, {Name}Response
router = APIRouter(prefix="/api/v1/{name}s", tags=["{name}s"])
@router.get("/")
async def list_{name}s(user=Depends(get_current_user), db=Depends(get_supabase_client)):
result = db.table("{name}s").select("*").eq("user_id", user.id).execute()
return {"{name}s": result.data}
@router.post("/", status_code=201)
async def create_{name}(payload: {Name}Create, user=Depends(get_current_user), db=Depends(get_supabase_client)):
data = {**payload.model_dump(), "user_id": user.id}
result = db.table("{name}s").insert(data).execute()
return result.data[0]
@router.patch("/{id}")
async def update_{name}(id: str, payload: {Name}Update, user=Depends(get_current_user), db=Depends(get_supabase_client)):
updates = {k: v for k, v in payload.model_dump(exclude_unset=True).items()}
result = db.table("{name}s").update(updates).eq("id", id).eq("user_id", user.id).execute()
if not result.data:
raise HTTPException(status_code=404, detail="{Name} not found")
return result.data[0]
@router.delete("/{id}", status_code=204)
async def delete_{name}(id: str, user=Depends(get_current_user), db=Depends(get_supabase_client)):
result = db.table("{name}s").delete().eq("id", id).eq("user_id", user.id).execute()
if not result.data:
raise HTTPException(status_code=404, detail="{Name} not found")Test Pattern (tests/test_{name}_endpoints.py)
Follow TDD — write tests FIRST, then implement:
python
import pytest
from unittest.mock import MagicMock
@pytest.fixture
def mock_user():
user = MagicMock()
user.id = "test-user-id"
return user
class TestList{Name}s:
async def test_returns_user_scoped_data(self, client, mock_supabase, mock_user):
mock_supabase.table().select().eq().execute.return_value.data = []
response = await client.get("/api/v1/{name}s")
assert response.status_code == 200
class TestCreate{Name}:
async def test_creates_with_valid_data(self, client, mock_supabase, mock_user):
mock_supabase.table().insert().execute.return_value.data = [{"id": "new-id"}]
response = await client.post("/api/v1/{name}s", json={...})
assert response.status_code == 201Registration (app/main.py)
Add to the router imports and registrations:
python
from app.api.v1.{name} import router as {name}_router
app.include_router({name}_router)Checklist
- [ ] Schema with proper Pydantic v2 validation (Field constraints)
- [ ] All 4 CRUD operations (list, create, update, delete)
- [ ] User scoping via
eq("user_id", user.id)on ALL queries - [ ] 404 handling on update/delete
- [ ] Tests written FIRST (TDD RED phase)
- [ ] Router registered in main.py
- [ ]
datetime.now(UTC)for timestamps (notutcnow()) - [ ] ruff check passes (
ruff check app/) - [ ] Full test suite passes (
pytest)