Flask to FastAPI: A Comparative Guide to Common Patterns

Installation and Basic Application

Flask

pip install flask

Other package managers such as Poetry, Pipenv, and Conda also work. For example:

poetry add flask

FastAPI

pip install fastapi uvicorn

FastAPI does not ship with a built-in development server; Uvicorn is the recommended ASGI server.

A minimal "Hello, world" application:

Flask

# flask_app.py
from flask import Flask

application = Flask(__name__)

@application.route("/")
def home():
    return {"hello": "flask"}

if __name__ == "__main__":
    application.run()

FastAPI

# fastapi_app.py
import uvicorn
from fastapi import FastAPI

api = FastAPI()

@api.get("/")
def home():
    return {"hello": "fastapi"}

if __name__ == "__main__":
    uvicorn.run("fastapi_app:api", reload=True)

Alternatively, start the server from the command line:

uvicorn fastapi_app:api --reload

Application Configuration

Both frameworks support configuration through environment variables, config files, and class-based settings.

Flask

import os
from flask import Flask

class AppConfig:
    SECRET = os.environ.get("APP_SECRET")

application = Flask(__name__)
application.config.from_object(AppConfig)

@application.route("/secret")
def show_secret():
    return {"secret": application.config["SECRET"]}

FastAPI

from fastapi import FastAPI
from pydantic_settings import BaseSettings

class AppConfig(BaseSettings):
    secret: str

api = FastAPI()
config = AppConfig()

@api.get("/secret")
def show_secret():
    return {"secret": config.secret}

Set the environment variable before starting the server:

export APP_SECRET="my-secret"

For FastAPI, the field name in the settings class maps to the environment variable automatically.

Routing, Templates, and Views

HTTP Methods

Flask
Flask uses the methods argument to specify allowed verbs:

from flask import request

@application.route("/data", methods=["GET", "POST"])
def handle_data():
    if request.method == "POST":
        return {"action": "created"}
    return {"action": "fetched"}

FastAPI
FastAPI provides separate decorators for each HTTP method:

@api.get("/data")
def get_data():
    return {"action": "fetched"}

@api.post("/data")
def post_data():
    return {"action": "created"}

Decorators for @api.put, @api.delete, @api.patch are also available.

URL Parameters

Flask

@application.route("/item/<int:item_id>")
def show_item(item_id):
    return {"id": item_id}

FastAPI

@api.get("/item/{item_id}")
def show_item(item_id: int):
    return {"id": item_id}

FastAPI leverages type hints for automatic validation.

Query Parameters

Flask

from flask import request

@application.route("/search")
def search():
    query = request.args.get("q")
    return {"query": query}

FastAPI

@api.get("/search")
def search(q: str = None):
    return {"query": q}

Template Rendering

Flask
Flask looks for templates inside a templates folder by default:

from flask import render_template

@application.route("/page")
def page():
    return render_template("index.html")

FastAPI
You need to install Jinja2 (pip install jinja2) and configure the template directory:

from fastapi import Request
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse

templates = Jinja2Templates(directory="templates")

@api.get("/page", response_class=HTMLResponse)
def page(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

Static Files

Flask serves files from a static directory automatically.
FastAPI requires explicit mounting:

from fastapi.staticfiles import StaticFiles

api.mount("/static", StaticFiles(directory="static"), name="static")

Asynchronous Tasks

Flask (version 2.0+) supports async route handlers:

@application.route("/async")
async def async_view():
    result = await some_async_operation()
    return result

For heavier background work, Celery or Redis Queue are commonly used.

FastAPI embraces asynchronous execution natively:

@api.get("/async")
async def async_view():
    result = await some_async_operation()
    return result

FastAPI also offers BackgroundTasks for fire‑and‑forget operations after a response has been sent:

from fastapi import BackgroundTasks

def process_file(file_name: str):
    # simulate long processing
    pass

@api.post("/upload/{file_name}")
async def upload(file_name: str, background_tasks: BackgroundTasks):
    background_tasks.add_task(process_file, file_name)
    return {"status": "processing started"}

Dependency Injection

Flask does not include a built-in DI mechanism; third‑party libraries such as flask-injector can fill the gap.
FastAPI provides a powerful Depends system:

from fastapi import Depends

def common_params(q: str = None, limit: int = 10):
    return {"q": q, "limit": limit}

@api.get("/items")
def read_items(params: dict = Depends(common_params)):
    return params

Dependencies can be reused across routes, handle database sessions, and perform authentication.

Data Validation

Flask has no built‑in validation; developers often use Pydantic via extensions like flask-pydantic.
FastAPI integrates Pydantic directly:

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float

@api.post("/items")
def create_item(item: Item):
    return item

Invalid payloads automatically return a descriptive validation error.

Serialisation and Deserialisation

Flask can use jsonify for simple dicts, or Marshmallow for complex structures:

from flask import jsonify

@application.route("/data")
def data():
    return jsonify({"status": "ok"})

FastAPI automatically serialises dict return values. For structured models, response_model is used:

class ItemOut(BaseModel):
    name: str
    # price excluded intentionally

@api.post("/items", response_model=ItemOut)
def create_item(item: Item):
    return item

Only the fields defined in ItemOut will be included in the resposne.

Middleware

Flask applies WSGI middleware:

import time

class TimerMiddleware:
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        start = time.time()
        response = self.app(environ, start_response)
        print(f"Request took {time.time() - start:.4f}s")
        return response

application.wsgi_app = TimerMiddleware(application.wsgi_app)

FastAPI uses the @app.middleware("http") decorator:

import time
from fastapi import Request

@api.middleware("http")
async def timer_middleware(request: Request, call_next):
    start = time.time()
    response = await call_next(request)
    print(f"Request took {time.time() - start:.4f}s")
    return response

Modularity with Blueprints and Routers

Flask – Blueprints:

# users.py
from flask import Blueprint

users_bp = Blueprint("users", __name__)

@users_bp.route("/users")
def list_users():
    return ["Alice", "Bob"]
# main
from users import users_bp
application.register_blueprint(users_bp)

FastAPI – APIRouter:

# users.py
from fastapi import APIRouter

router = APIRouter()

@router.get("/users")
def list_users():
    return ["Alice", "Bob"]
# main
from users import router
api.include_router(router)

Automatic API Documentation

Flask requires extensions such as flask-swagger or Flask-RESTX to auto‑generate docs.
FastAPI produces OpenAPI documentation out of the box. Visit /docs for Swagger UI and /redoc for ReDoc.

Admin Panels

  • Flask: Flask-Admin is the most popular choice.
  • FastAPI: options include FastAPI-Admin and SQLAlchemy-Admin.

Authentication

Flask relies on third‑party extensions like Flask-Login or Flask-JWT.
FastAPI provides the fastapi.security module with helpers for HTTP Basic, OAuth2, and more:

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials

security = HTTPBasic()

def authenticate(credentials: HTTPBasicCredentials = Depends(security)):
    if credentials.username != "admin" or credentials.password != "pass":
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
    return credentials.username

@api.get("/whoami")
def whoami(username: str = Depends(authenticate)):
    return {"username": username}

CORS

Flask needs the flask-cors package:

from flask_cors import CORS

CORS(application)

FastAPI has built‑in middleware:

from fastapi.middleware.cors import CORSMiddleware

origins = ["http://localhost:3000"]
api.add_middleware(CORSMiddleware, allow_origins=origins)

Testing

Flask

import pytest
from flask_app import application

def test_home():
    with application.test_client() as client:
        response = client.get("/")
        assert response.status_code == 200
        assert response.json == {"hello": "flask"}

FastAPI

from fastapi.testclient import TestClient
from fastapi_app import api

client = TestClient(api)

def test_home():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"hello": "fastapi"}

Deployment

For production, both frameworks commonly run behind an ASGI/WSGI server and often with Docker.

Flask – use Gunicorn:

pip install gunicorn
gunicorn flask_app:application

FastAPI – use Uvicorn (or Gunicorn with Uvicorn workers):

pip install uvicorn
uvicorn fastapi_app:api

For increased parallelism:

gunicorn -w 4 -k uvicorn.workers.UvicornWorker fastapi_app:api

Basic Dockerfiles:

Flask

FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["gunicorn", "flask_app:application"]

FastAPI

FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "fastapi_app:api", "--host", "0.0.0.0"]

Tags: Flask FastAPI python migration web development

Posted on Fri, 15 May 2026 01:45:57 +0000 by dakkonz