<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://johnwick.cc/index.php?action=history&amp;feed=atom&amp;title=FastAPI_vs_Flask_in_2025</id>
	<title>FastAPI vs Flask in 2025 - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://johnwick.cc/index.php?action=history&amp;feed=atom&amp;title=FastAPI_vs_Flask_in_2025"/>
	<link rel="alternate" type="text/html" href="https://johnwick.cc/index.php?title=FastAPI_vs_Flask_in_2025&amp;action=history"/>
	<updated>2026-05-07T07:24:13Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.44.1</generator>
	<entry>
		<id>https://johnwick.cc/index.php?title=FastAPI_vs_Flask_in_2025&amp;diff=3153&amp;oldid=prev</id>
		<title>PC: Created page with &quot;650px  FastAPI vs Flask in 2025: a practical, async-first guide with code, trade-offs, pitfalls, and a 10-minute blueprint to ship your indie SaaS backend.    You have a weekend, a coffee, and a SaaS idea. The question isn’t “what stack?” — it’s “what ships fastest without painting me into a corner?” Let’s be real: in 2025, that often means choosing between FastAPI and Flask.    Why this comparison still matters Fl...&quot;</title>
		<link rel="alternate" type="text/html" href="https://johnwick.cc/index.php?title=FastAPI_vs_Flask_in_2025&amp;diff=3153&amp;oldid=prev"/>
		<updated>2025-12-13T22:26:32Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;&lt;a href=&quot;/index.php?title=File:FastAPI_vs_Flask_in_2025.jpg&quot; title=&quot;File:FastAPI vs Flask in 2025.jpg&quot;&gt;650px&lt;/a&gt;  FastAPI vs Flask in 2025: a practical, async-first guide with code, trade-offs, pitfalls, and a 10-minute blueprint to ship your indie SaaS backend.    You have a weekend, a coffee, and a SaaS idea. The question isn’t “what stack?” — it’s “what ships fastest without painting me into a corner?” Let’s be real: in 2025, that often means choosing between FastAPI and Flask.    Why this comparison still matters Fl...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;[[file:FastAPI_vs_Flask_in_2025.jpg|650px]]&lt;br /&gt;
&lt;br /&gt;
FastAPI vs Flask in 2025: a practical, async-first guide with code, trade-offs, pitfalls, and a 10-minute blueprint to ship your indie SaaS backend.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You have a weekend, a coffee, and a SaaS idea. The question isn’t “what stack?” — it’s “what ships fastest without painting me into a corner?” Let’s be real: in 2025, that often means choosing between FastAPI and Flask.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Why this comparison still matters&lt;br /&gt;
Flask is the seasoned minimalist. FastAPI is the async-native sprinter with batteries included. Both are excellent — but they push you toward different development rhythms and deployment shapes. If you’re building a small-but-serious indie SaaS, the defaults you pick today will echo in every feature, cron, and incident you handle later.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TL;DR: Which should you choose?&lt;br /&gt;
* 		Pick FastAPI if you want async I/O, built-in Pydantic validation, OpenAPI docs out of the box, and you expect real-time features or external API fan-out (think Stripe + Slack + Notion in one request).&lt;br /&gt;
* 		Pick Flask if you value utter simplicity, huge ecosystem familiarity, and you’re okay adding pieces yourself — or you’re migrating an older WSGI stack.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Architecture at a glance (10-minute Indie SaaS)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Browser/CLI]&lt;br /&gt;
     |&lt;br /&gt;
     v&lt;br /&gt;
+-------------+        Webhooks      +-----------+&lt;br /&gt;
|  API (ASGI) |  &amp;lt;-----------------  | Providers |&lt;br /&gt;
|  FastAPI    |  --&amp;gt; Task Queue ---&amp;gt; | Stripe    |&lt;br /&gt;
|  Uvicorn    |  &amp;lt;-- Cache (Redis)   | GitHub    |&lt;br /&gt;
+-------------+                      +-----------+&lt;br /&gt;
      |            ^&lt;br /&gt;
      v            |&lt;br /&gt;
  Postgres &amp;lt;-------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Why this works: ASGI + async DB drivers let you overlap I/O: external APIs, database, cache, and webhooks. You gain latency headroom without horizontal sprawl.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The 10-minute FastAPI backend (real, minimal, async)&lt;br /&gt;
1) Project layout&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
saas/&lt;br /&gt;
  app.py&lt;br /&gt;
  models.py&lt;br /&gt;
  schema.py&lt;br /&gt;
  requirements.txt&lt;br /&gt;
requirements.txt&lt;br /&gt;
fastapi&lt;br /&gt;
uvicorn[standard]&lt;br /&gt;
pydantic&lt;br /&gt;
sqlmodel&lt;br /&gt;
asyncpg&lt;br /&gt;
sqlalchemy&amp;gt;=2.0&lt;br /&gt;
python-jose[cryptography]&lt;br /&gt;
passlib[bcrypt]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2) Models and schema (SQLModel + Pydantic)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# models.py&lt;br /&gt;
from sqlmodel import SQLModel, Field&lt;br /&gt;
from typing import Optional&lt;br /&gt;
import datetime as dt&lt;br /&gt;
&lt;br /&gt;
class User(SQLModel, table=True):&lt;br /&gt;
    id: Optional[int] = Field(default=None, primary_key=True)&lt;br /&gt;
    email: str = Field(index=True, unique=True)&lt;br /&gt;
    password_hash: str&lt;br /&gt;
    created_at: dt.datetime = Field(default_factory=dt.datetime.utcnow)&lt;br /&gt;
&lt;br /&gt;
class Todo(SQLModel, table=True):&lt;br /&gt;
    id: Optional[int] = Field(default=None, primary_key=True)&lt;br /&gt;
    owner_id: int = Field(index=True)&lt;br /&gt;
    title: str&lt;br /&gt;
    done: bool = False&lt;br /&gt;
    created_at: dt.datetime = Field(default_factory=dt.datetime.utcnow)&lt;br /&gt;
&lt;br /&gt;
# schema.py&lt;br /&gt;
from pydantic import BaseModel, EmailStr&lt;br /&gt;
&lt;br /&gt;
class SignupIn(BaseModel):&lt;br /&gt;
    email: EmailStr&lt;br /&gt;
    password: str&lt;br /&gt;
&lt;br /&gt;
class LoginIn(BaseModel):&lt;br /&gt;
    email: EmailStr&lt;br /&gt;
    password: str&lt;br /&gt;
&lt;br /&gt;
class TodoIn(BaseModel):&lt;br /&gt;
    title: str&lt;br /&gt;
&lt;br /&gt;
class TodoOut(BaseModel):&lt;br /&gt;
    id: int&lt;br /&gt;
    title: str&lt;br /&gt;
    done: bool&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3) App with auth + CRUD (JWT + async DB)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# app.py&lt;br /&gt;
import asyncio&lt;br /&gt;
from fastapi import FastAPI, Depends, HTTPException, status&lt;br /&gt;
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials&lt;br /&gt;
from sqlmodel import SQLModel, select&lt;br /&gt;
from sqlmodel.ext.asyncio.session import AsyncSession&lt;br /&gt;
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker&lt;br /&gt;
from jose import jwt, JWTError&lt;br /&gt;
from passlib.hash import bcrypt&lt;br /&gt;
from models import User, Todo&lt;br /&gt;
from schema import SignupIn, LoginIn, TodoIn, TodoOut&lt;br /&gt;
&lt;br /&gt;
SECRET = &amp;quot;change-me&amp;quot;&lt;br /&gt;
ALGO = &amp;quot;HS256&amp;quot;&lt;br /&gt;
&lt;br /&gt;
engine = create_async_engine(&amp;quot;postgresql+asyncpg://user:pass@localhost/saas&amp;quot;, future=True, echo=False)&lt;br /&gt;
Session = async_sessionmaker(engine, expire_on_commit=False)&lt;br /&gt;
auth_scheme = HTTPBearer()&lt;br /&gt;
&lt;br /&gt;
app = FastAPI(title=&amp;quot;Indie SaaS API&amp;quot;, version=&amp;quot;2025.1&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
@app.on_event(&amp;quot;startup&amp;quot;)&lt;br /&gt;
async def on_startup():&lt;br /&gt;
    async with engine.begin() as conn:&lt;br /&gt;
        await conn.run_sync(SQLModel.metadata.create_all)&lt;br /&gt;
&lt;br /&gt;
def create_token(user_id: int) -&amp;gt; str:&lt;br /&gt;
    return jwt.encode({&amp;quot;sub&amp;quot;: str(user_id)}, SECRET, algorithm=ALGO)&lt;br /&gt;
&lt;br /&gt;
async def get_user(creds: HTTPAuthorizationCredentials = Depends(auth_scheme)):&lt;br /&gt;
    try:&lt;br /&gt;
        payload = jwt.decode(creds.credentials, SECRET, algorithms=[ALGO])&lt;br /&gt;
        uid = int(payload[&amp;quot;sub&amp;quot;])&lt;br /&gt;
    except (JWTError, KeyError, ValueError):&lt;br /&gt;
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)&lt;br /&gt;
    async with Session() as s:&lt;br /&gt;
        user = await s.get(User, uid)&lt;br /&gt;
        if not user:&lt;br /&gt;
            raise HTTPException(status_code=401)&lt;br /&gt;
        return user&lt;br /&gt;
&lt;br /&gt;
@app.get(&amp;quot;/health&amp;quot;)&lt;br /&gt;
async def health():&lt;br /&gt;
    return {&amp;quot;ok&amp;quot;: True}&lt;br /&gt;
&lt;br /&gt;
@app.post(&amp;quot;/signup&amp;quot;)&lt;br /&gt;
async def signup(data: SignupIn):&lt;br /&gt;
    async with Session() as s:&lt;br /&gt;
        exists = (await s.exec(select(User).where(User.email == data.email))).first()&lt;br /&gt;
        if exists:&lt;br /&gt;
            raise HTTPException(409, &amp;quot;Email already used&amp;quot;)&lt;br /&gt;
        user = User(email=data.email, password_hash=bcrypt.hash(data.password))&lt;br /&gt;
        s.add(user)&lt;br /&gt;
        await s.commit()&lt;br /&gt;
        await s.refresh(user)&lt;br /&gt;
        return {&amp;quot;token&amp;quot;: create_token(user.id)}&lt;br /&gt;
&lt;br /&gt;
@app.post(&amp;quot;/login&amp;quot;)&lt;br /&gt;
async def login(data: LoginIn):&lt;br /&gt;
    async with Session() as s:&lt;br /&gt;
        user = (await s.exec(select(User).where(User.email == data.email))).first()&lt;br /&gt;
        if not user or not bcrypt.verify(data.password, user.password_hash):&lt;br /&gt;
            raise HTTPException(401, &amp;quot;Invalid credentials&amp;quot;)&lt;br /&gt;
        return {&amp;quot;token&amp;quot;: create_token(user.id)}&lt;br /&gt;
&lt;br /&gt;
@app.post(&amp;quot;/todos&amp;quot;, response_model=TodoOut)&lt;br /&gt;
async def create_todo(inp: TodoIn, user=Depends(get_user)):&lt;br /&gt;
    async with Session() as s:&lt;br /&gt;
        todo = Todo(owner_id=user.id, title=inp.title)&lt;br /&gt;
        s.add(todo)&lt;br /&gt;
        await s.commit()&lt;br /&gt;
        await s.refresh(todo)&lt;br /&gt;
        return TodoOut(id=todo.id, title=todo.title, done=todo.done)&lt;br /&gt;
&lt;br /&gt;
@app.get(&amp;quot;/todos&amp;quot;, response_model=list[TodoOut])&lt;br /&gt;
async def list_todos(user=Depends(get_user)):&lt;br /&gt;
    async with Session() as s:&lt;br /&gt;
        rows = await s.exec(select(Todo).where(Todo.owner_id == user.id))&lt;br /&gt;
        return [TodoOut(id=t.id, title=t.title, done=t.done) for t in rows]&lt;br /&gt;
Run it locally&lt;br /&gt;
uvicorn app:app --reload&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auto docs at /docs and /redoc—zero extra config. That’s minutes saved.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
“Can Flask do async in 2025?”&lt;br /&gt;
Short answer: yes, with caveats. Since Flask 2+, you can write async def routes, but Flask is still WSGI-first. You’ll need the right server or adapters to get true ASGI-style concurrency. If you want native ASGI and typed request/response models, FastAPI feels smoother. If your app is mostly synchronous templates + a few APIs, Flask remains a joy.&lt;br /&gt;
&lt;br /&gt;
Minimal Flask 3 route&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from flask import Flask, jsonify&lt;br /&gt;
app = Flask(__name__)&lt;br /&gt;
&lt;br /&gt;
@app.get(&amp;quot;/health&amp;quot;)&lt;br /&gt;
async def health():&lt;br /&gt;
    return jsonify(ok=True)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Great DX, huge ecosystem — but you’ll assemble validation, schema, and docs yourself (or via extensions).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Real-world numbers (indicative, not gospel)&lt;br /&gt;
On a base M2 laptop with a simple JSON endpoint:&lt;br /&gt;
* 		FastAPI + Uvicorn (ASGI): ~5,000–6,500 req/s with wrk -t4 -c128 -d30s.&lt;br /&gt;
* 		Flask (WSGI dev server): ~1,000–1,800 req/s; with a tuned production server and caching you can go higher.&lt;br /&gt;
Your mileage will vary — DB I/O, auth, and network hops dominate real apps. But async buys you headroom when calling 2–3 external APIs per request.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Developer ergonomics that compound&lt;br /&gt;
* 		Validation + types: FastAPI’s Pydantic models catch bad payloads early and generate docs. That can cut 20–30% off integration bugs in early builds.&lt;br /&gt;
* 		Docs for free: Clients can try endpoints live in /docs. Support tickets mysteriously drop.&lt;br /&gt;
* 		Async “fan-out” patterns: Call Stripe, Slack, and your DB concurrently with asyncio.gather. Latency feels smaller than the sum of its parts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When Flask is still the right move&lt;br /&gt;
* 		You’re migrating a legacy WSGI app or rely on battle-tested Flask extensions.&lt;br /&gt;
* 		Your app is mostly server-rendered pages; API is small and simple.&lt;br /&gt;
* 		You want the thinnest layer possible and prefer to hand-pick everything: Marshmallow, Flasgger, SQLAlchemy classic, Jinja2, Celery, etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Comparison table&lt;br /&gt;
Criterion FastAPI (ASGI) Flask (WSGI-first) Async support Native; first-class Supported in routes, but WSGI core; ASGI via adapters Validation &amp;amp; typing Pydantic models by default Optional via extensions Auto OpenAPI docs Built-in (/docs) Extension-based Performance under I/O Strong (concurrency) Good, often needs more processes Learning curve Moderate Very low Ecosystem age Newer but vibrant Mature, massive Best fit API-first, async SaaS Minimal sites, legacy, full control&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pitfalls to avoid&lt;br /&gt;
* 		Blocking the event loop: Don’t do CPU-heavy work in FastAPI routes. Offload to a worker (Celery/RQ) or run in a process pool.&lt;br /&gt;
* 		Mixing sync DB drivers in async apps: Use asyncpg/SQLAlchemy 2.0 async.&lt;br /&gt;
* 		JWT everywhere: For server-side sessions and revocation, consider fast session stores over pure JWT.&lt;br /&gt;
* 		Ignoring timeouts and retries: Wrap external calls with sensible timeouts; add circuit breakers.&lt;br /&gt;
* 		Staging ≠ prod: Measure under realistic load. Tiny features can cascade into big latencies when calling 3rd parties.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mini case study: “Ship in a weekend, survive for a year”&lt;br /&gt;
An indie founder launched a subscription notes API with FastAPI, Postgres, and Stripe webhooks. They started with two endpoints and a /health route, then added Slack notifications and a Notion sync. Because the code was async from day one, adding parallel calls cut response time from ~480 ms to ~190 ms under load. Costs stayed low—one small VM, one managed Postgres, and Redis for rate limits.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The 10-minute checklist (print this)&lt;br /&gt;
* 		FastAPI + Uvicorn template running&lt;br /&gt;
* 		Postgres + async SQLAlchemy connected&lt;br /&gt;
* 		Auth (JWT or server sessions)&lt;br /&gt;
* 		/health + /metrics (Prometheus)&lt;br /&gt;
* 		Request timeouts + retries&lt;br /&gt;
* 		CORS + rate limiting (e.g., middleware + Redis)&lt;br /&gt;
* 		Background tasks (Celery/RQ) for slow jobs&lt;br /&gt;
* 		Infrastructure as code (a single Terraform file beats none)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Conclusion (and what to do next)&lt;br /&gt;
Both frameworks can take you to production. FastAPI wins when you want async throughput, typed contracts, and autopilot docs. Flask wins when you crave minimalism and the comfort of a decade-deep ecosystem.&lt;br /&gt;
&lt;br /&gt;
If your indie SaaS leans API-first and integration-heavy, start FastAPI, keep it boring (Postgres, Redis), and iterate. If you’re migrating or templating-heavy, Flask is still a delight.&lt;br /&gt;
Your move: Which one are you using this weekend — and why?&lt;br /&gt;
&lt;br /&gt;
Read the full article here: https://medium.com/@komalbaparmar007/fastapi-vs-flask-in-2025-83ba0a5e9aba&lt;/div&gt;</summary>
		<author><name>PC</name></author>
	</entry>
</feed>