DEV Community

Cover image for I built a production-ready FastAPI SaaS boilerplate — here's what's in it
Lorenzo
Lorenzo

Posted on

I built a production-ready FastAPI SaaS boilerplate — here's what's in it

Every time I start a new backend project I waste the first two days on the exact same things: JWT auth, Stripe subscriptions, database migrations, Docker setup.

So I stopped doing that and packaged everything into a clean, working boilerplate.

Here's what's in it and why I made the choices I did.


Stack

  • FastAPI — async, fast, auto-generates OpenAPI docs at /docs
  • SQLAlchemy 2.0 + Alembic — ORM with proper migration support
  • Pydantic v2 — request/response validation throughout
  • python-jose — JWT access and refresh tokens
  • passlib + bcrypt — password hashing
  • Stripe — subscription billing
  • SlowAPI — per-route rate limiting
  • Docker + docker-compose — runs anywhere

SQLite out of the box, one line to switch to Postgres.


Project structure

app/
├── api/routes/     # auth.py, users.py, billing.py
├── core/           # config, database, security
├── models/         # SQLAlchemy models
└── schemas/        # Pydantic v2 schemas
Enter fullscreen mode Exit fullscreen mode

Clean, flat, easy to extend.


Auth

JWT with separate access and refresh tokens. Access tokens expire in 30 minutes, refresh tokens in 7 days. Rate limited at the route level — 5 requests/minute on register, 10 on login.

@router.post("/register", response_model=UserResponse, status_code=201)
@limiter.limit("5/minute")
def register(request: Request, user_data: UserCreate, db: Session = Depends(get_db)):
    if db.query(User).filter(User.email == user_data.email).first():
        raise HTTPException(status_code=400, detail="Email already registered")
    ...
Enter fullscreen mode Exit fullscreen mode

Stripe subscriptions

Full subscription flow: checkout session creation, success redirect, webhook handling for cancellations and failed payments.

Protecting a route behind an active subscription is one dependency:

from app.core.security import get_current_active_subscriber

@router.get("/premium-feature")
def premium(current_user: User = Depends(get_current_active_subscriber)):
    return {"data": "only for paying customers"}
Enter fullscreen mode Exit fullscreen mode

Returns 402 Payment Required automatically if the user has no active plan.


Database migrations

Alembic is wired up and the initial migration is included. Adding a new field to a model and generating a migration is one command:

alembic revision --autogenerate -m "add new field"
alembic upgrade head
Enter fullscreen mode Exit fullscreen mode

Up and running in 5 minutes

git clone https://github.com/thask8lo/fastapi-saas-starter
cd fastapi-saas-starter

python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

pip install -r requirements.txt
cp .env.example .env
alembic upgrade head
uvicorn app.main:app --reload
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:8000/docs — every endpoint is there, ready to test.


Get it

GitHub: github.com/thask8lo/fastapi-saas-starter

If you want the full packaged version ready to drop into a project: available on Gumroad

Happy to answer questions in the comments.

Top comments (0)