fastapi-structure-guide
π―Skillfrom chasepassion/skills
Generates a structured FastAPI project with strict layered architecture and clean code practices.
Part of
chasepassion/skills(2 items)
Installation
npx skills add chasepassion/skills --skill fastapi-structure-guideSkill Details
"Trigger when the user wants to create a new FastAPI project, add new files/folders (features), refactor existing code, or asks about architectural best practices. This skill enforces a strict layered architecture and specific development workflow."
Overview
# FastAPI Structure Guide
Intent
Use this guide whenever generating code for a FastAPI project, specifically when:
- Scaffolding a brand new project.
- Adding a new feature (e.g., "Add an Order module") which requires creating files across multiple layers.
- Refactoring existing code to meet clean architecture standards.
You must strictly adhere to the Core Principles, Project Structure, Development Workflow, and Coding Rules defined below.
---
I. Core Principles
Before writing a single line of code, adhere to these four guiding principles:
- Separation of Concerns:
- API Layer: Responsible only for "Reception" (parsing requests, validating parameters).
- Service Layer: Responsible for "Business" (logic calculation, decision making).
- DB/Model Layer: Responsible for "Data" (storage access, shape definition).
- Rule: Never write business logic inside an API Route function.
- Dependency Injection:
- Do not instantiate components directly (e.g.,
service = UserService()). - Use FastAPI's
Dependsfor injection. - Flow: DB Session -> injected into -> Service -> injected into -> API Route.
- Config Centralization:
- Strictly prohibit hardcoded passwords, keys, or URLs in the code.
- Manage all configurations via Pydantic Settings and read from environment variables (
.env).
- Mirrored Testing:
- The test directory structure must mirror the source code directory structure 1:1.
- Use SQLite In-Memory and Dependency Overrides to mock the real environment.
---
II. Recommended Project Structure
Use this standardized directory structure when creating files or folders:
```text
my-fastapi-project/
βββ app/ # Core Application Source
β βββ __init__.py
β βββ main.py # π App Entry: Routes mounting, Exception handling
β βββ api/ # π Interface Layer (Routes)
β β βββ __init__.py
β β βββ v1/ # Version Control
β β βββ __init__.py
β β βββ api.py # Router Aggregation (Include Routers)
β β βββ endpoints/ # Specific Business Endpoints
β β βββ __init__.py
β β βββ users.py
β β βββ items.py
β βββ core/ # βοΈ Infrastructure Configuration
β β βββ __init__.py
β β βββ config.py # Pydantic Settings (Env Config)
β β βββ logging.py # Logging Config
β β βββ security.py # Auth/Hashing Tools
β βββ db/ # ποΈ Database Layer
β β βββ __init__.py
β β βββ session.py # DB Connection & Session Factory
β β βββ tables.py # SQLAlchemy ORM Definitions (DB Schema)
β βββ models/ # π Data Transfer Objects (DTOs)
β β βββ __init__.py
β β βββ user.py # Pydantic Models (Request/Response)
β β βββ item.py
β βββ services/ # π§ Business Logic Layer
β βββ __init__.py
β βββ base.py # Optional: Base Service Class
β βββ user_service.py # User-related business logic
β βββ item_service.py
βββ tests/ # β Test Cases (Mirrored Structure)
β βββ __init__.py
β βββ conftest.py # Pytest Fixtures (DB override, Client)
β βββ api/
β βββ v1/
β βββ endpoints/
β βββ test_users.py
βββ .env # π Local Env Vars (Gitignored)
βββ .gitignore
βββ docker-compose.yaml # Local Dev Orchestration
βββ Dockerfile # Image Build
βββ pyproject.toml # Dependency Management (Recommend uv)
βββ README.md
```
Directory Responsibilities
app/api/(Interface Layer)
- Responsibility: Handle HTTP protocol specifics only.
- Contains: Path definitions, HTTP methods (GET/POST), status codes, dependency injection declarations.
- Input/Output: Pydantic Schemas.
- Rule: This layer must be "thin". Functions should strictly be 5-10 lines, calling the Service layer and returning results.
app/services/(Business Logic Layer)
- Responsibility: The brain of the application. Handles complex business rules, calculations, and permission checks.
- Contains: CRUD operations, 3rd-party API logic, data processing.
- Input/Output: Pydantic Schemas or Raw Data -> ORM Objects or Pydantic Schemas.
- Rule: Service classes must accept
Sessionvia__init__for easy testing mocks.
app/models/(Data Transfer Object Layer)
- Responsibility: Define data "shape" and validation rules for the API.
- Contains: Pydantic Models (
BaseModel). - Distinction: These are not DB tables. They are for API input/output (e.g.,
UserCreatevsUserResponse).
app/db/(Data Access Layer)
- Responsibility: Physical DB connection and Table definitions.
- Contains:
session.py(Engine/SessionLocal),tables.py(SQLAlchemy Base models/Columns).
app/core/(Cross-Cutting Concerns)
- Responsibility: Infrastructure supporting the app.
- Contains: Config loading (
config.py), logging, security tools. Code here is business-agnostic.
---
III. Creation Rules (General Development Workflow)
When implementing a new feature, follow these 5 Standard Steps in order:
Step A: Define Data Storage (Database Layer)
- Principle: Everything starts with data. Determine how the resource looks in the DB.
- Action: Add new SQLAlchemy ORM class in
db/tables.py(or specific file indb/models/). - Naming: PascalCase for Class (Singular), snake_case for Table Name (Plural).
Step B: Define Interaction Contract (Schemas/DTO Layer)
- Principle: Define how data moves over the network. Validate Input (Create/Update) and Normalize Output (Response).
- Action: Create a new file in
models/. - Naming:
resource_name.py. Typically includesCreate,Update,Responsevariants.
Step C: Implement Business Logic (Service Layer)
- Principle: Write the "Verbs". This is the bridge between DB and API.
- Action: Create a new file in
services/. - Naming:
resource_name_service.py. Class name ends withService.
Step D: Expose API Interface (API Layer)
- Principle: Define the entry point for external access.
- Action: Create a new file in
api/v1/endpoints/. - Naming:
resource_names.py(Plural) to reflect RESTful style.
Step E: Registration & Wiring
- Principle: New router files are isolated by default; they must be explicitly registered.
- Action: Modify
api/v1/api.pyto include the new router.
---
IV. Coding Rules
Rule 1: API Routes Must Be "Dumb"
β Wrong (Logic Leakage):
```python
@router.post("/users")
def create_user(user: UserCreate, db: Session = Depends(get_db)):
# Error: Logic and DB ops directly in route
hashed_password = hash_pw(user.password)
db_user = User(email=user.email, password=hashed_password)
db.add(db_user)
db.commit()
return db_user
```
β Correct (Call Service):
```python
@router.post("/users")
def create_user(
user: UserCreate,
service: UserService = Depends(get_user_service) # Injected Service
):
# Correct: Only forwards the request
return service.create_user(user)
```
Rule 2: Service Layer Must Use Dependency Injection
Service classes should not create the DB Session themselves; they must receive it in __init__.
```python
class UserService:
def __init__(self, session: Session):
self.session = session # β Dependency Injection
def create_user(self, data: UserCreate):
# Business logic...
pass
```
Rule 3: Config Must Use Pydantic
Never use os.getenv("KEY") scattered in the code.
```python
# app/core/config.py
class Settings(BaseSettings):
DB_URL: str
SECRET_KEY: str
settings = Settings()
# Usage in other files
from app.core.config import settings
print(settings.DB_URL)
```