Files
gh-greyhaven-ai-claude-code…/skills/api-design-standards/templates/fastapi-crud-endpoint.py
2025-11-29 18:29:15 +08:00

131 lines
4.3 KiB
Python

# Grey Haven Studio - FastAPI CRUD Endpoint Template
# Copy this template for new resource endpoints
from fastapi import APIRouter, Depends, HTTPException, status
from sqlmodel import Session
from typing import Optional
from app.core.dependencies import get_current_user, get_db
from app.models.user import User
from app.repositories.resource_repository import ResourceRepository # TODO: Update import
from app.schemas.resource import ResourceCreate, ResourceRead, ResourceUpdate, PaginatedResponse # TODO: Update import
# TODO: Update prefix, tags, and model name
router = APIRouter(prefix="/api/v1/resources", tags=["resources"])
@router.get("", response_model=PaginatedResponse[ResourceRead], status_code=status.HTTP_200_OK)
async def list_resources(
skip: int = 0,
limit: int = 100,
# TODO: Add filter parameters as needed
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
) -> PaginatedResponse[ResourceRead]:
"""
List all resources in the current tenant with pagination.
- **skip**: Number of records to skip (pagination offset)
- **limit**: Maximum number of records to return (max 100)
- **Returns**: Paginated list of resources
"""
repository = ResourceRepository(db, tenant_id=current_user.tenant_id)
# TODO: Build filter criteria if needed
filters = {}
resources = await repository.list(filters=filters, skip=skip, limit=limit)
total = await repository.count(filters=filters)
return PaginatedResponse(
items=resources,
total=total,
skip=skip,
limit=limit,
has_more=(skip + limit) < total,
)
@router.get("/{resource_id}", response_model=ResourceRead, status_code=status.HTTP_200_OK)
async def get_resource(
resource_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
) -> ResourceRead:
"""Get a single resource by ID (tenant-isolated)."""
repository = ResourceRepository(db, tenant_id=current_user.tenant_id)
resource = await repository.get_by_id(resource_id)
if not resource:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Resource with ID {resource_id} not found",
)
return resource
@router.post("", response_model=ResourceRead, status_code=status.HTTP_201_CREATED)
async def create_resource(
resource_data: ResourceCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
) -> ResourceRead:
"""
Create a new resource in the current tenant.
**Errors**:
- 409 Conflict: Duplicate resource
- 422 Validation Error: Invalid data
"""
repository = ResourceRepository(db, tenant_id=current_user.tenant_id)
try:
resource = await repository.create(resource_data)
return resource
except IntegrityError:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Resource with this identifier already exists",
)
@router.put("/{resource_id}", response_model=ResourceRead, status_code=status.HTTP_200_OK)
async def update_resource(
resource_id: str,
resource_data: ResourceUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
) -> ResourceRead:
"""
Update an existing resource (tenant-isolated).
All fields are optional - only provided fields will be updated.
"""
repository = ResourceRepository(db, tenant_id=current_user.tenant_id)
resource = await repository.update(resource_id, resource_data)
if not resource:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Resource with ID {resource_id} not found",
)
return resource
@router.delete("/{resource_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_resource(
resource_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
) -> None:
"""
Soft-delete a resource (tenant-isolated).
Returns 204 No Content on success (no response body).
"""
repository = ResourceRepository(db, tenant_id=current_user.tenant_id)
success = await repository.delete(resource_id)
if not success:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Resource with ID {resource_id} not found",
)