Files
ponytail/examples/api-endpoint.md
T

64 lines
1.7 KiB
Markdown

# API Endpoint
**Task:** "Add an endpoint that returns a user by id."
## Without Ponytail
```
app/
├── controllers/user_controller.py
├── services/user_service.py
├── repositories/user_repository.py
├── schemas/user_schemas.py
└── exceptions/user_exceptions.py
```
```python
# schemas/user_schemas.py
class UserResponseDTO(BaseModel):
id: int
name: str
email: str
# repositories/user_repository.py
class UserRepository:
def __init__(self, db: Session):
self.db = db
def find_by_id(self, user_id: int) -> User | None:
return self.db.get(User, user_id)
# services/user_service.py
class UserService:
def __init__(self, repo: UserRepository):
self.repo = repo
def get_user(self, user_id: int) -> User:
user = self.repo.find_by_id(user_id)
if user is None:
raise UserNotFoundError(user_id)
return user
# controllers/user_controller.py
@router.get("/users/{user_id}", response_model=UserResponseDTO)
def get_user(user_id: int, service: UserService = Depends(get_user_service)):
try:
return service.get_user(user_id)
except UserNotFoundError:
raise HTTPException(status_code=404, detail="User not found")
```
Five files, three classes, a custom exception, and a dependency-injection chain — wrapping one database call.
## With Ponytail
```python
# ponytail: it's one query
@app.get("/users/{user_id}")
def get_user(user_id: int, db: Session = Depends(get_db)):
user = db.get(User, user_id)
if not user:
raise HTTPException(404)
return user
```
**5 files → 5 lines.** Layers earn their place when there are two implementations, not before. Add the service layer when a second caller shows up — if it ever does.