Skip to content

Barebones FastAPI OAuth2 with Password (and hashing), Bearer with JWT tokens, and SQLAlchemy integration

License

Notifications You must be signed in to change notification settings

alessiocsassu/fastapi-oauth-barebone

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

10 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🧱 Barebone FastAPI OAuth2

Use this template Latest Release License

βš™οΈ Key Technologies

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)

🧰 Tooling & DevOps

Area Tool
Development Environment Docker + Docker Compose
Testing Pytest
DB Migrations Alembic
Security JWT + bcrypt
Environment Management .env

πŸ—‚οΈ Project Structure

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

πŸš€ Setup Commands

  1. Create and activate the virtual environment
python3 -m venv .venv
source .venv/bin/activate
  1. Select the Python version from the virtual environment (in your IDE)

  2. Install and update poetry

pip install poetry
poetry install --no-root
  1. Create the .env file
cp .env.example .env
  1. Build and start services with docker
docker compose up --build -d
  1. 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
  1. Run automated tests (opzionale)
docker exec -it fastapi_test_container pytest tests/

πŸ“Œ Guide on how to create Model, Schema, Service, Manager and Endpoints

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.


🧱 1. Create a New Model (SQLAlchemy)

All models inherit from the Base class:

from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

To 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 head

πŸ“„ 2. Create the Pydantic Schemas

Schemas define the request/response structure of your API.

Create a file inside:

app/schemas/product.py

Example:

from pydantic import BaseModel

class ProductCreate(BaseModel):
    name: str
    code: str

class ProductRead(BaseModel):
    id: int
    name: str
    code: str

βš™οΈ 3. Create the Service (CRUD Layer)

Services handle database operations only. They inherit from BaseService, so you don’t need to rewrite CRUD logic.

Create:

app/services/product_service.py

Example:

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()

Why BaseService exists?

It provides generic methods such as:

  • get_list()
  • get_by_id()
  • create()
  • update()
  • delete()

This keeps your code DRY and consistent across the project.

🧠 4. Create the Manager (Business Logic Layer)

Managers sit above Services and handle:

  • validation
  • error handling
  • workflows
  • business rules
  • orchestration

Create:

app/managers/product_manager.py

Example:

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 = ProductService

Why BaseManager exists?

It provides:

  • get_or_404()
  • get_all()
  • create() with safe error handling
  • update() with existence checks
  • delete() returning a standardized BaseDelete schema

This ensures a consistent behavior across all endpoints.

πŸš€ 5. Create Routes (API Endpoints)

Create a route file:

app/api/routes/product.py

Example:

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