| Component | Technology |
|---|---|
| Language | Python 3.12+ |
| API Framework | FastAPI |
| Server | Uvicorn (ASGI) |
| Database | PostgreSQL |
| ORM | SQLAlchemy |
| Migrations | Alembic |
| Data Validation | Pydantic |
| Authentication | JWT (JSON Web Token) |
| Area | Tool |
|---|---|
| Development Environment | Docker + Docker Compose |
| Testing | Pytest |
| DB Migrations | Alembic |
| Security | JWT + bcrypt |
| Environment Management | .env |
fastapi-oauth-base/
β
βββ alembic
β βββ versions
β βββ env.py
β βββ script.py.mako
βββ app
β βββ api
β β βββ routes
β β β βββ __init__.py
β β β βββ users.py
β β βββ __init__.py
β βββ auth
β β βββ api
β β β βββ routes
β β β β βββ __init__.py
β β β β βββ auth.py
β β β βββ __init__.py
β β βββ core
β β β βββ __init__.py
β β β βββ security.py
β β βββ managers
β β β βββ __init__.py
β β β βββ auth_manager.py
β β βββ schemas
β β β βββ auth_schema.py
β β βββ services
β β β βββ __init__.py
β β β βββ auth_service.py
β β βββ __init__.py
β βββ config
β β βββ __init__.py
β β βββ config.py
β βββ db
β β βββ models
β β β βββ __init__.py
β β β βββ base.py
β β β βββ user.py
β β βββ __init__.py
β β βββ session.py
β βββ managers
β β βββ __init__.py
β β βββ base_manager.py
β β βββ user_manager.py
β βββ schemas
β β βββ __init__.py
β β βββ base.py
β β βββ user.py
β βββ services
β β βββ __init__.py
β β βββ base_service.py
β β βββ user_service.py
β βββ __init__.py
β βββ main.py
β βββ pytest.ini
βββ tests
β βββ __pycache__
β βββ __init__.py
β βββ test_users.py
βββ CHANGELOG.md
βββ Dockerfile
βββ README.md
βββ __init__.py
βββ alembic.ini
βββ docker-compose.yml
βββ pyproject.toml
- Create and activate the virtual environment
python3 -m venv .venv
source .venv/bin/activate-
Select the Python version from the virtual environment (in your IDE)
-
Install and update poetry
pip install poetry
poetry install --no-root- Create the
.envfile
cp .env.example .env- Build and start services with docker
docker compose up --build -d- Run alembic migrations inside the container
docker exec -it fastapi_app_container alembic revision --autogenerate -m "init schema"
docker exec -it fastapi_app_container alembic upgrade head- Run automated tests (opzionale)
docker exec -it fastapi_test_container pytest tests/This project uses a modular architecture based on reusable base classes (BaseService, BaseManager, BaseSchema, Base) designed to avoid repeating the same logic in every new module.
Thanks to these base classes, adding a new entity (e.g., Product, Article, Category, etc.) becomes fast and consistent.
Below is a step-by-step guide for anyone who clones this repository.
All models inherit from the Base class:
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
passTo create a new model, add a file inside:
app/db/models/Example: Product model
from sqlalchemy.orm import Mapped, mapped_column
from app.db.models.base import Base
class Product(Base):
__tablename__ = "products"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
code: Mapped[str]After creating the model generate and apply migrations:
alembic revision --autogenerate -m "add product"
alembic upgrade headSchemas define the request/response structure of your API.
Create a file inside:
app/schemas/product.pyExample:
from pydantic import BaseModel
class ProductCreate(BaseModel):
name: str
code: str
class ProductRead(BaseModel):
id: int
name: str
code: strServices handle database operations only. They inherit from BaseService, so you donβt need to rewrite CRUD logic.
Create:
app/services/product_service.pyExample:
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.db.models.product import Product
from app.services.base_service import BaseService
class ProductService(BaseService[Product]):
model = Product
@staticmethod
async def get_by_code(db: AsyncSession, code: str) -> Optional[Product]:
result = await db.execute(
select(Product).where(Product.code == code)
)
return result.scalar_one_or_none()It provides generic methods such as:
get_list()get_by_id()create()update()delete()
This keeps your code DRY and consistent across the project.
Managers sit above Services and handle:
- validation
- error handling
- workflows
- business rules
- orchestration
Create:
app/managers/product_manager.pyExample:
from app.managers.base_manager import BaseManager
from app.services.product_service import ProductService
from app.db.models.product import Product
class ProductManager(BaseManager[Product, ProductService]):
service = ProductServiceIt provides:
get_or_404()get_all()create()with safe error handlingupdate()with existence checksdelete()returning a standardizedBaseDeleteschema
This ensures a consistent behavior across all endpoints.
Create a route file:
app/api/routes/product.pyExample:
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.managers.product_manager import ProductManager
from app.schemas.product import ProductCreate, ProductRead
from app.db.session import get_db
router = APIRouter(prefix="/products", tags=["Products"])
@router.get("/", response_model=list[ProductRead])
async def list_products(db: AsyncSession = Depends(get_db)):
return await ProductManager.get_all(db)
@router.post("/", response_model=ProductRead)
async def create_product(
payload: ProductCreate,
db: AsyncSession = Depends(get_db),
):
return await ProductManager.create(db, payload.model_dump())Finally, register the router inside:
app/main.py