Skip to main content
Glama
copilot-instructions.mdβ€’8.31 kB
# Copilot Instructions: FastAPI Backend with Hexagonal Architecture You are a Python/FastAPI expert. Create a backend following hexagonal architecture, SOLID principles, and KISS. ## πŸ—οΈ Project Structure ``` β”œβ”€β”€ .github/workflows/ # CI/CD (tests, linting, deploy) β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ main.py # FastAPI app β”‚ β”œβ”€β”€ config.py # Pydantic Settings β”‚ β”œβ”€β”€ dependencies.py # Dependency injection β”‚ β”œβ”€β”€ domain/ # Business core (pure Python) β”‚ β”‚ β”œβ”€β”€ entities.py # Business entities (dataclasses) β”‚ β”‚ β”œβ”€β”€ ports.py # Interfaces (ABC) β”‚ β”‚ └── services.py # Business services (optional) β”‚ β”œβ”€β”€ application/ β”‚ β”‚ β”œβ”€β”€ requests.py # Input DTOs (Pydantic) β”‚ β”‚ β”œβ”€β”€ use_cases.py # Application logic β”‚ β”‚ └── api.py # FastAPI routes β”‚ └── infrastructure/ # One folder = one implementation β”‚ β”œβ”€β”€ postgres/ # adapter.py + models.py β”‚ β”œβ”€β”€ mongodb/ # adapter.py + models.py β”‚ └── email/ # adapter.py └── tests/ β”œβ”€β”€ unit/ # Unit tests └── doubles/ # Test doubles (fakes) ``` ## 🎯 Core Principles ### Hexagonal Architecture - **Domain**: Completely independent, ZERO external imports (no FastAPI, SQLAlchemy, etc.) - **Application**: Orchestrates use cases, depends only on domain - **Infrastructure**: Implements ports (adapters), can depend on everything ### SOLID - **SRP**: 1 class = 1 responsibility (1 use case = 1 business action) - **OCP**: Extension via new adapters, never modify ports - **LSP**: All implementations respect their port's contract - **ISP**: Specific and targeted interfaces (no ports with 20 methods) - **DIP**: Use cases depend on ports (abstractions), never on concrete adapters ### KISS (Keep It Simple, Stupid) - Direct transformations: `Class(**other.__dict__)` or `Class(**model.model_dump())` - **No methods** like `create()`, `from_entity()`, `to_entity()` β†’ too complex - **No Response DTOs** β†’ return domain entities directly in API - Readable code > "clever" code - One file per concept (no unnecessary subdirectories) ## πŸ“‹ Code Rules ### Python Best Practices - **Python 3.11+** minimum with latest features (match/case, StrEnum, Self type) - Type hints **mandatory** everywhere (use `from typing import ...`) - Async/await for all I/O operations (database, HTTP calls, file operations) - **Dataclasses** for domain entities (simple, immutable when possible with `frozen=True`) - **ABC (Abstract Base Classes)** for ports/interfaces - Google-style docstrings for all public methods/classes - **Error handling**: Custom exceptions hierarchy (inherit from base domain exception) - **Naming conventions**: - Classes: `PascalCase` - Functions/variables: `snake_case` - Constants: `UPPER_SNAKE_CASE` - Private attributes: `_leading_underscore` - Use **context managers** (`async with`) for resource management - Prefer **composition over inheritance** - **No mutable default arguments** (use `None` and initialize in function body) - Use **Enum** for constants/status values - **List/Dict comprehensions** over loops when readable - Dependency manager: **UV** (not Poetry/pip) ### FastAPI Best Practices - **Dependency injection** via `Depends()` for all services, repositories, and configurations - **Annotated types** for cleaner dependency injection: `Annotated[Service, Depends(get_service)]` - **Pydantic V2** for all request/response validation with Field constraints - **Explicit HTTP status codes**: Use `status.HTTP_201_CREATED` instead of `201` - **Router organization**: Group related endpoints with `APIRouter(prefix="/api/v1/resource", tags=["resource"])` - **Error handling**: - Custom `HTTPException` with clear error messages - Exception handlers with `@app.exception_handler()` - Consistent error response format - **Request/Response models**: - Separate models for input (Request) and output (Response) when needed - Use `response_model` parameter to control what gets returned - Use `response_model_exclude_unset=True` to skip null values - **Validation**: - Use Pydantic `Field()` with validators: `min_length`, `max_length`, `ge`, `le`, `regex` - Custom validators with `@field_validator` - Model validators with `@model_validator` - **Documentation**: - Docstrings on every endpoint with Args, Returns, Raises sections - Use `summary`, `description`, `response_description` in decorators - Provide examples in Pydantic models with `model_config = ConfigDict(json_schema_extra={...})` - **Security**: - JWT authentication with `python-jose` - Password hashing with `passlib[bcrypt]` - OAuth2PasswordBearer for protected routes - CORS configuration explicit and restrictive - Rate limiting (use `slowapi` or custom middleware) - Security headers middleware - **Performance**: - Use `BackgroundTasks` for non-blocking operations (emails, logs) - Connection pooling for databases - Caching with Redis/in-memory for frequent queries - Pagination for list endpoints: `limit`/`offset` or cursor-based - **Startup/Shutdown events**: - Database connection initialization in `lifespan` context manager - Graceful shutdown handling - **Middleware**: - Request ID tracking - Logging middleware for all requests - CORS middleware properly configured - Compression middleware for large responses - **Testing**: - Use `TestClient` for API testing - Override dependencies with `app.dependency_overrides` - Test error cases and edge cases ### Tests - **Test doubles** (fakes) for internal components (repositories, services) - **Mocks** ONLY for external services (third-party APIs, email, S3, etc.) - pytest + pytest-asyncio - Minimum coverage: 80% - Fixtures for test doubles injection ### Data Transformations ```python # βœ… GOOD: Direct transformation user_entity = User(**user_model.__dict__) db_user = UserModel(**user_entity.__dict__) request_dto = CreateUserRequest(**pydantic_model.model_dump()) # ❌ BAD: Unnecessary intermediate methods user_entity = User.from_model(user_model) db_user = UserModel.from_entity(user_entity) ``` ### Infrastructure - One folder per implementation: `postgres/`, `mongodb/`, `rag/`, `email/` - Each folder contains: `adapter.py` (port implementation) + `models.py` (if needed) - Adapters implement ports defined in `domain/ports.py` ### API - Routes in `application/api.py` - Return domain entities directly (FastAPI serializes them automatically) - No need for separate Response DTOs ## βœ… Checklist ### Architecture - [ ] 3 distinct layers: domain / application / infrastructure - [ ] Domain without any external dependencies - [ ] Use cases depend on ports (interfaces), not adapters - [ ] Infrastructure: one folder per adapter with `adapter.py` + `models.py` ### SOLID & KISS - [ ] SRP: One class = one responsibility - [ ] DIP: Depend on abstractions (ports) - [ ] Direct transformations with `**.__dict__` or `**model.model_dump()` - [ ] No methods like `create()`, `from_entity()`, `to_entity()` ### Code Quality - [ ] Type hints everywhere - [ ] Async/await for I/O - [ ] Pydantic V2 for validation - [ ] Tests with test doubles (no internal mocks) - [ ] Coverage β‰₯ 80% ### DevOps - [ ] GitHub Actions CI/CD (tests, linting, security) - [ ] Docker + Docker Compose - [ ] Documented `.env.example` - [ ] README.md with quick start ## 🚫 Absolutely Avoid - ❌ Importing FastAPI, SQLAlchemy, etc. in domain - ❌ Creating Response DTOs (return domain entities directly) - ❌ Complex transformation methods (`from_entity`, `to_entity`) - ❌ Mocking internal components in tests (use test doubles) - ❌ Over-engineering (unnecessary builders, factories) - ❌ Too-wide interfaces with too many methods ## πŸ“Œ Critical Reminders 1. **Domain** = pure Python, zero external imports 2. **Use cases** depend on **ports** (abstractions), never on **adapters** 3. Transformations: `Class(**other.__dict__)` or `Class(**model.model_dump())` 4. Tests: test doubles (fakes) for internal, mocks for external only 5. SOLID + KISS above all: simplicity and design principles

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Kaiohz/mcp-perplexica'

If you have feedback or need assistance with the MCP directory API, please join our Discord server