- 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 devThe 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 checkThis 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 resourcesExample 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 todoUnlike 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.pyfor REST endpoint conventions - Services: See
services/todo.pyfor 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.pyfor reference - Import it in
models/__init__.pywith# noqa: F401:Thefrom models.your_model import YourModel # noqa: F401
noqa: F401comment 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.pyfor reference
- Add
-
Create the service layer
- Add
services/your_model.py(singular) - Implement CRUD operations
- See
services/todo.pyfor reference
- Add
-
Create the API routes
- Add
api/your_models.py(plural) - Implement REST endpoints
- See
api/todos.pyfor reference
- Add
-
Register the router
- Update
app.pyto 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.