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
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")
...
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"}
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
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
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)