- Python 3.9+
- Poetry
- PostgreSQL
- Set up environment variables:
cp .env.example .env
# Edit .env with your database credentials
- Run migrations:
poetry run alembic upgrade head
- Install dependencies:
poetry install
# Install pre-commit hooks
poetry run pre-commit install
- Run the application:
poetry run dev
The API will be available at http://localhost:8000
This project uses several tools to ensure code quality:
- Ruff: For linting and formatting
- MyPy: For static type checking
Before committing your changes, run the following command to ensure all code quality checks pass:
poetry run check
This will:
- Check code formatting
- Run type checks
- Verify naming conventions
- Ensure all linting rules are followed
Fix any issues reported before committing your changes.
- Table names: plural, snake_case (e.g.,
todos
,user_profiles
) - Model files: singular, snake_case (e.g.,
todo.py
,user_profile.py
) - Model classes: singular, PascalCase (e.g.,
Todo
,UserProfile
) - Column names: snake_case (e.g.,
created_at
,user_id
)
- API route files: plural, snake_case (e.g.,
todos.py
,user_profiles.py
) - Route paths: plural, kebab-case (e.g.,
/todos
,/user-profiles
)
For REST endpoints, use simple verbs that match the HTTP method:
# Collection endpoints
@router.post("/") def create() # Create a new resource
@router.get("/") def list_all() # List all resources
# Single resource endpoints
@router.get("/{id}") def get() # Get a single resource
@router.put("/{id}") def update() # Update a resource
@router.delete("/{id}") def delete() # Delete a resource
# Nested resource endpoints
@router.post("/{id}/sub") def create_sub() # Create a nested resource
@router.get("/{id}/sub") def list_subs() # List nested resources
Example URL patterns:
GET /todos
- List all todosPOST /todos
- Create a new todoGET /todos/1
- Get todo with ID 1PUT /todos/1
- Update todo with ID 1DELETE /todos/1
- Delete todo with ID 1POST /todos/1/comments
- Create a comment on todo 1GET /todos/1/comments
- List comments for todo 1
- Service files: singular, snake_case (matches model name)
- Schema files: singular, snake_case (matches model name)
- Schema classes: PascalCase with purpose suffix (e.g.,
TodoCreate
,UserUpdate
)
For service layer functions, use descriptive names that include both verb and entity:
# CRUD operations in services
def create_todo() # Create a new todo
def get_todos() # Get all todos
def get_todo() # Get single todo
def update_todo() # Update a todo
def delete_todo() # Delete a todo
Unlike API routes where the context is provided by HTTP method and URL path, service functions should be self-descriptive as they might be called from different contexts (API routes, background jobs, CLI commands, etc.).
Reference implementations:
- API Routes: See
api/todos.py
for REST endpoint conventions - Services: See
services/todo.py
for service layer conventions
For the best development experience, install these VSCode extensions:
your-repo-name/
├── api/
│ └── todos.py
├── models/
│ ├── __init__.py
│ └── todo.py
├── schemas/
│ ├── database.py
│ └── todo.py
├── services/
│ └── todo.py
├── app.py
├── pyproject.toml
└── .pre-commit-config.yaml
Using the todos
implementation as reference, follow these steps:
-
Create the database migration
# Create a new migration file poetry run alembic revision -m "create_your_table_name" # Edit the generated file in migrations/versions/ # See migrations/versions/*_create_todos_table.py for reference
-
Create the model
- Add
models/your_model.py
(singular) - See
models/todo.py
for reference - Import it in
models/__init__.py
with# noqa: F401
:Thefrom models.your_model import YourModel # noqa: F401
noqa: F401
comment tells linters to ignore the "unused import" warning, as these imports are needed for SQLAlchemy/Alembic model registration.
- Add
-
Create the schemas
- Add
schemas/your_model.py
(singular) - Define base model and CRUD variants (Create, Update)
- See
schemas/todo.py
for reference
- Add
-
Create the service layer
- Add
services/your_model.py
(singular) - Implement CRUD operations
- See
services/todo.py
for reference
- Add
-
Create the API routes
- Add
api/your_models.py
(plural) - Implement REST endpoints
- See
api/todos.py
for reference
- Add
-
Register the router
- Update
app.py
to include your new router
from api.your_models import router as your_models_router app.include_router(your_models_router, prefix="/your-models", tags=["your-models"])
- Update
-
Run the migration
poetry run alembic upgrade head
-
Test your endpoints
# Example CRUD operations curl -X POST "http://localhost:8000/your-models/" -d '{"field": "value"}' curl "http://localhost:8000/your-models/"
Always follow the naming conventions and use the existing implementations as reference. Each layer (models, schemas, services, api) has its own conventions as documented above.