π·πΊ Π ΡΡΡΠΊΠ°Ρ Π²Π΅ΡΡΠΈΡ
This tutorial provides a complete learning path from basic FastAPI concepts to advanced Domain-Driven Design architecture.
Example 01: Basic FastAPI Requests
- β GET, POST, PUT, DELETE methods
- β
Path parameters (
/items/{item_id}) - β
Query parameters (
?category=food&limit=10) - β Request body validation
- β Response models and status codes
- β All examples in ONE file
File: example_01/main.py
Topics covered:
- HTTP methods and when to use them
- URL path parameters vs query parameters
- Request body handling with Pydantic
- Proper HTTP status codes (200, 201, 204, 404)
- Async endpoints with
async def
Example 02: Pydantic + BaseModel
- β BaseModel basics and field types
- β Advanced validation (Field, field_validator, model_validator)
- β Nested models and complex structures
- β Model configuration (ConfigDict)
- β Custom validators and business logic
- β All examples in ONE file
File: example_02/main.py
Topics covered:
- Pydantic v2 syntax and best practices
- Field constraints (min_length, max_length, pattern, gt, le)
- EmailStr, HttpUrl, and other special types
- Nested models for complex data structures
- Request/Response model separation
- Error handling and validation errors
Example 03: Database Integration (SQLAlchemy Async)
- β Async SQLAlchemy 2.0 configuration
- β
Session management with
Depends(get_db) - β
Modern ORM:
mapped_columnandMapped[type](NOT oldColumn) - β
Using
db.execute(select(...))instead ofdb.query(...) - β Schema vs Model explained - Key difference!
- β Complete CRUD operations
- β All examples in ONE file
File: example_03/main.py
Key Concepts:
Schema (Pydantic) - API Layer:
- Validates incoming data from clients
- Serializes outgoing data to clients
- Lives in the application/API layer
- Examples:
UserCreate,UserUpdate,UserResponse
Model (SQLAlchemy) - Database Layer:
- Represents database table structure
- Maps Python objects to database rows
- Lives in the data/persistence layer
- Example:
Userclass withmapped_column
Why both?:
- Separation of concerns
- Database schema can differ from API contract
- Can hide sensitive fields (passwords, internal IDs)
- Multiple Pydantic schemas can use same Model
Example 04: Image Handling
- β File upload with validation
- β Image processing (resize, convert format)
- β PIL/Pillow library usage
- β File download (FileResponse, StreamingResponse)
- β Static file serving
- β On-the-fly thumbnail generation
- β File system operations
File: example_04/main.py
Key Operations:
- Upload image with type validation
- Resize images maintaining aspect ratio
- Convert between formats (JPEG, PNG, WEBP)
- Download processed images
- Stream thumbnails without saving
- List uploaded and processed files
Technologies:
- FastAPI file upload (
UploadFile) - PIL/Pillow for image processing
FileResponsefor downloadsStreamingResponsefor streamingStaticFilesfor serving directories
Example 05: Dependency Injection
- β
How
Depends()works internally - β Service layer pattern with DI
- β Repository pattern for data access
- β Multi-level dependency chain
- β Testing benefits of DI
- β Request-scoped dependency caching
Files: example_05/main.py, example_05/test_di.py
Dependency Chain:
Router β Service β Repository β Database Session
How Depends() Works:
- FastAPI analyzes function signature
- Recursively resolves all dependencies
- Caches instances per request (same session for all)
- Calls cleanup code (generators with
yield) - Injects resolved dependencies into function
Benefits:
- Easy to test (can override dependencies)
- Clean separation of concerns
- Automatic resource management
- Type-safe dependency injection
- Reusable components
Example 06: Domain-Driven Design (Full Architecture)
Complete professional architecture with clear separation of layers.
Files:
example_06/
βββ domain/
β βββ models.py # DAO: Database models (SQLAlchemy)
β βββ schemas.py # DTO: Data Transfer Objects (Pydantic)
βββ repositories/
β βββ base.py # Generic CRUD repository
β βββ user_repository.py # User-specific queries
βββ services/
β βββ user_service.py # Business logic layer
βββ factories/
β βββ user_factory.py # Object creation patterns
βββ routers/
β βββ users.py # HTTP API endpoints (thin layer)
βββ database.py # Database configuration
βββ unit_of_work.py # Transaction management
βββ main.py # Application entry point
Architecture Layers:
-
DAO (Data Access Objects) -
domain/models.py- SQLAlchemy ORM models
- Database table representation
-
DTO (Data Transfer Objects) -
domain/schemas.py- Pydantic models for API contracts
- Validation and serialization
-
Repositories - Data access abstraction
-
Services - Business logic layer
-
Factories - Object creation patterns
-
UnitOfWork - Transaction management
-
Routers - Thin HTTP layer
# Install all dependencies
pip install -r requirements.txt
# Or install for specific example
cd example_01
pip install fastapi uvicorn pydantic[email]Example 01-03 (single file):
cd example_01 # or example_02, example_03
uvicorn main:app --reloadExample 04-06 (multiple files or single file):
cd example_04 # or example_05, example_06
python main.pyAccess API docs:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
- Start with Example 01 - Learn basic HTTP methods
- Move to Example 02 - Master Pydantic validation
- Study Example 03 - Understand database integration
- Example 04 - Learn file handling and image processing
- Example 05 - Learn Dependency Injection pattern
- Understand Service and Repository layers
- Example 06 - Full DDD architecture
- Build production-ready applications
Schema vs Model (Example 03)
Schema (Pydantic):
- For API validation and serialization
- Lives in application layer
- Examples:
UserCreate,UserResponse
Model (SQLAlchemy):
- For database representation
- Lives in persistence layer
- Example:
Userwithmapped_column
β OLD (SQLAlchemy 1.x):
id = Column(Integer, primary_key=True)
user = db.query(User).filter(User.id == 1).first()β NEW (SQLAlchemy 2.0) - Used in this tutorial:
id: Mapped[int] = mapped_column(Integer, primary_key=True)
result = await db.execute(select(User).where(User.id == 1))
user = result.scalar_one_or_none()Benefits:
- Better type checking with Mapped[type]
- IDE autocomplete support
- Async/await native support
- Unified query API
- Better performance
β 100% Async Code
- All endpoints use
async def - All database operations use
await - AsyncSession for database
- Async context managers
β SQLAlchemy 2.0 Patterns
mapped_columninstead ofColumnMapped[type]for type hintsdb.execute(select(...))instead ofdb.query(...)
β File Organization
- Examples 1-4: All code in ONE file (simple structure)
- Examples 5-6: Proper layer separation (advanced architecture)
- No files in root folder (all in subdirectories)
Run tests for Example 05:
cd example_05
pytest test_di.py -vTests demonstrate:
- Integration testing with test database
- Dependency override patterns
- Mocking repositories
- Benefits of DI for testing
This is a teaching project. Each example is self-contained and can be studied independently.
Suggestions for improvement are welcome!
Happy Learning! π