Skip to main content

FastAPI Template Review 2026: Python SaaS Boilerplate

·StarterPick Team
fastapipythonreviewboilerplate2026

TL;DR

The FastAPI full-stack template (by Tiangolo/FastAPI) is the definitive Python SaaS foundation. Built by FastAPI's creator, it's production-quality with SQLModel, Alembic migrations, React frontend, Docker, and CI/CD. Free and MIT licensed. Best for Python/ML teams building SaaS who don't want to switch to Node.js.

What You Get

Source: github.com/fastapi/full-stack-fastapi-template

Core features:

  • FastAPI + Python 3.12+
  • Frontend: React + TypeScript (Vite)
  • Database: PostgreSQL via SQLModel + Alembic
  • Auth: JWT + OAuth2 password flow
  • Email: Emails via SMTP/Mailgun
  • Docker Compose for local development
  • Docker/Kubernetes for production
  • CI/CD: GitHub Actions
  • Testing: pytest setup
  • Auto-generated API docs (OpenAPI/Swagger)
  • Free / MIT licensed

The FastAPI Advantage

FastAPI's automatic API documentation is a significant differentiator:

# main.py — FastAPI with auto-documentation
from fastapi import FastAPI, Depends, HTTPException
from sqlmodel import Session, select
from app.api.deps import get_current_user, get_db
from app.models import User, Item

app = FastAPI(
    title="Your SaaS",
    description="API documentation automatically generated",
    version="1.0.0",
)

@app.get("/api/v1/items/", response_model=list[Item])
async def read_items(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
    skip: int = 0,
    limit: int = 100,
) -> list[Item]:
    """Get all items for the current user."""
    items = db.exec(
        select(Item)
        .where(Item.owner_id == current_user.id)
        .offset(skip)
        .limit(limit)
    ).all()
    return items

Every endpoint automatically generates:

  • OpenAPI schema at /docs
  • ReDoc at /redoc
  • Type-safe client generation (openapi-typescript-codegen)

Data Models with SQLModel

SQLModel unifies Pydantic validation with SQLAlchemy ORM:

# models.py — single model definition for API + database
from sqlmodel import SQLModel, Field, Relationship
from pydantic import EmailStr
from typing import Optional
import uuid

class UserBase(SQLModel):
    email: EmailStr = Field(unique=True, index=True, max_length=255)
    is_active: bool = True
    is_superuser: bool = False
    full_name: Optional[str] = Field(default=None, max_length=255)

class User(UserBase, table=True):
    """Database model"""
    id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
    hashed_password: str
    items: list["Item"] = Relationship(back_populates="owner")

class UserCreate(UserBase):
    """API input model"""
    password: str = Field(min_length=8, max_length=40)

class UserPublic(UserBase):
    """API output model (no password)"""
    id: uuid.UUID

class UserUpdate(SQLModel):
    """Partial update model"""
    email: Optional[EmailStr] = None
    full_name: Optional[str] = None
    password: Optional[str] = None

This pattern eliminates the duplication between validation models and database models common in Django or raw SQLAlchemy.


Authentication

# security.py — JWT auth implementation
from datetime import datetime, timedelta
from jose import jwt, JWTError
from passlib.context import CryptContext
from app.core.config import settings

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def create_access_token(subject: str) -> str:
    expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode = {"exp": expire, "sub": str(subject)}
    return jwt.encode(to_encode, settings.SECRET_KEY, algorithm="HS256")

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)

# deps.py — reusable auth dependency
async def get_current_user(
    token: str = Depends(oauth2_scheme),
    db: Session = Depends(get_db)
) -> User:
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
        user_id = payload.get("sub")
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid credentials")

    user = db.get(User, user_id)
    if not user or not user.is_active:
        raise HTTPException(status_code=401, detail="Inactive user")
    return user

Adding Stripe

The template doesn't include Stripe, but the pattern is clean:

# api/v1/billing.py
import stripe
from fastapi import APIRouter, Depends
from app.api.deps import get_current_user
from app.core.config import settings

router = APIRouter()
stripe.api_key = settings.STRIPE_SECRET_KEY

@router.post("/create-checkout-session")
async def create_checkout_session(
    price_id: str,
    current_user: User = Depends(get_current_user),
):
    session = stripe.checkout.Session.create(
        customer_email=current_user.email,
        line_items=[{"price": price_id, "quantity": 1}],
        mode="subscription",
        success_url=f"{settings.FRONTEND_URL}/dashboard?success=true",
        cancel_url=f"{settings.FRONTEND_URL}/pricing",
        metadata={"user_id": str(current_user.id)},
    )
    return {"url": session.url}

@router.post("/webhook")
async def stripe_webhook(request: Request):
    payload = await request.body()
    sig_header = request.headers.get("stripe-signature")

    event = stripe.Webhook.construct_event(
        payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
    )

    if event["type"] == "checkout.session.completed":
        user_id = event["data"]["object"]["metadata"]["user_id"]
        await activate_subscription(user_id)

    return {"status": "ok"}

Python vs Node.js for SaaS

FactorFastAPI (Python)Node.js (Next.js)
AI/ML integrationExcellentPossible but harder
NPM ecosystemLimitedExtensive
Auto API docsBuilt-inExternal (Swagger-UI)
Type safetyPydantic + mypyTypeScript
Database ORMSQLModel/SQLAlchemyPrisma/Drizzle
Team familiarityPython devsJS/TS devs
Boilerplate ecosystemSmallerLarge

Choose FastAPI when:

  • Your team is Python-first
  • AI/ML is a core product feature (seamless Hugging Face, PyTorch integration)
  • You want built-in OpenAPI documentation
  • Data processing pipelines are central to the product

Limitations

  • Stripe not included — Must add yourself
  • Frontend is basic React SPA — No SSR (Vite, not Next.js)
  • Community size — Smaller than Node.js boilerplate community
  • More DevOps required — Separate API + frontend deployment
  • No blog/marketing pages — Pure application framework

Who Should Use FastAPI Template

Good fit:

  • Python/data science teams building SaaS
  • AI-powered products (RAG, ML inference, data pipelines)
  • Teams building APIs with multiple frontend clients
  • Products needing auto-generated API documentation
  • Developers who know Python and don't want to learn Node.js

Bad fit:

  • JavaScript-first teams
  • Teams needing comprehensive SaaS features out of the box
  • Products requiring SSR/SEO from the frontend (use Next.js)

Final Verdict

Rating: 4/5 for Python SaaS

FastAPI template is the best starting point for Python-based SaaS. The SQLModel integration, automatic API docs, and clean architecture are production-quality. The absence of Stripe and blog features requires more setup vs Node.js alternatives. For Python teams, it's an easy recommendation — for JavaScript teams, stick with Next.js boilerplates.


Compare FastAPI Template with other backend-focused starters on StarterPick.

Comments