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-Adminis the most popular choice. - FastAPI: options include
FastAPI-AdminandSQLAlchemy-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"]