The skeleton is designed so a new resource is mechanical. We'll add a projects
resource. Use the existing items files as a copy-paste template.
Rule of thumb: logic goes in the service layer. Routers stay thin, repositories stay dumb, schemas define the wire contract.
-
ORM model —
apps/api/app/db/models/project.py, then export it fromapp/db/models/__init__.pyso Alembic sees it. -
Schemas —
apps/api/app/schemas/project.py:ProjectCreate,ProjectUpdate,ProjectRead. -
Repository —
apps/api/app/repositories/project.pyclass ProjectRepository(BaseRepository[ProjectModel]): model = ProjectModel
-
Service —
apps/api/app/services/project.py: your business rules, raisingNotFoundError/ConflictErroras needed. -
Router —
apps/api/app/api/v1/routes/projects.py: thin handlers that call the service. Add aget_project_servicedependency indeps.py. -
Register the router in
apps/api/app/api/v1/router.py. -
Migrate:
make migration m="add projects" make migrate -
Test — copy
tests/test_items.pytotests/test_projects.py.
- Add types to the shared contract (
packages/api-contract/src/index.ts) or regenerate from OpenAPI. - Add a typed client in
apps/web/lib/api-client.ts(mirroritemsApi). - Add routes/components under
apps/web/app/.
Once you have real resources, remove the items files across all layers and the
ItemModel (plus a migration to drop the table). The skeleton stands without it.