flask
Flask Framework Guide
Applies to: Flask 3.0+, REST APIs, Microservices, Web Applications Language Guide: @.claude/skills/python-guide/SKILL.md
Overview
Flask is a lightweight WSGI web framework providing the basics for building web applications while allowing flexibility in choosing components.
Use Flask when:
- Building microservices or small-to-medium APIs
- You want flexibility to choose your own ORM, auth, etc.
- Rapid prototyping is needed
- You prefer explicit over implicit behavior
Consider alternatives when:
- You need async support (use FastAPI or Quart)
- You want batteries-included (use Django)
- High performance async is critical (use FastAPI)
Guardrails
Flask-Specific Guidelines
- Use the application factory pattern (never use global app instances)
- Use blueprints for modular routing
- Use Flask extensions appropriately (init in extensions.py)
- Configure via environment variables with class-based config
- Use Marshmallow for validation/serialization
- Implement proper error handlers for all error types
- Use Flask-Migrate for database migrations
- Use Flask-JWT-Extended for authentication
Security Guidelines
- Never store plain passwords (use werkzeug.security)
- Use environment variables for all secrets
- Implement proper authentication and authorization
- Validate all user input with Marshmallow schemas
- Use HTTPS in production
- Set secure cookie options (HTTPONLY, SECURE, SAMESITE)
- Sanitize all database queries via SQLAlchemy ORM
- Configure CORS restrictively in production
Testing Guidelines
- Use pytest with Flask test client
- Use fixtures for test data and app context
- Test both success and error cases for every endpoint
- Mock external services (never call real APIs in tests)
- Use a separate test database
- Coverage target: >80% for business logic
Project Structure
myproject/
├── app/
│ ├── __init__.py # Application factory
│ ├── config.py # Configuration classes
│ ├── extensions.py # Flask extensions (single init point)
│ ├── models/
│ │ ├── __init__.py
│ │ ├── base.py # Base model class with mixins
│ │ └── user.py
│ ├── api/
│ │ ├── __init__.py # API blueprint registration
│ │ ├── users.py # User endpoints
│ │ └── auth.py # Auth endpoints
│ ├── services/
│ │ ├── __init__.py
│ │ └── user_service.py # Business logic (no Flask imports)
│ ├── schemas/
│ │ ├── __init__.py
│ │ └── user.py # Marshmallow schemas
│ └── utils/
│ ├── __init__.py
│ ├── errors.py # Custom exceptions and handlers
│ └── decorators.py # Auth and role decorators
├── migrations/ # Alembic migrations (via Flask-Migrate)
├── tests/
│ ├── conftest.py # Fixtures: app, client, db_session
│ ├── test_api/
│ │ └── test_users.py
│ └── test_services/
│ └── test_user_service.py
├── .env.example
├── requirements.txt
├── requirements-dev.txt
├── pyproject.toml
└── run.py # Entry point
Key conventions:
app/__init__.pycontainscreate_app()factory onlyapp/extensions.pycentralizes all extension instancesapp/services/holds business logic, no Flask importsapp/schemas/holds Marshmallow validation/serializationapp/utils/errors.pydefines custom exceptions and handlers
Application Factory
Always use the factory pattern. Never use a global app = Flask(__name__).
"""Flask application factory."""
from flask import Flask
from app.config import config
from app.extensions import db, migrate, ma, jwt, cors
def create_app(config_name: str = "development") -> Flask:
"""Create and configure the Flask application."""
app = Flask(__name__)
app.config.from_object(config[config_name])
register_extensions(app)
register_blueprints(app)
register_error_handlers(app)
register_commands(app)
return app
def register_extensions(app: Flask) -> None:
"""Initialize Flask extensions."""
db.init_app(app)
migrate.init_app(app, db)
ma.init_app(app)
jwt.init_app(app)
cors.init_app(app)
def register_blueprints(app: Flask) -> None:
"""Register Flask blueprints."""
from app.api import api_bp
app.register_blueprint(api_bp, url_prefix="/api/v1")
def register_error_handlers(app: Flask) -> None:
"""Register error handlers."""
from app.utils.errors import (
handle_app_error,
handle_validation_error,
handle_not_found,
handle_internal_error,
)
from marshmallow import ValidationError
app.register_error_handler(ValidationError, handle_validation_error)
app.register_error_handler(404, handle_not_found)
app.register_error_handler(500, handle_internal_error)
def register_commands(app: Flask) -> None:
"""Register CLI commands."""
from app.commands import seed_db
app.cli.add_command(seed_db)
Blueprints
Organize routes into blueprints. Register them in the factory.
"""API blueprint registration."""
from flask import Blueprint
api_bp = Blueprint("api", __name__)
# Import routes to register them
from app.api import users, auth # noqa: F401, E402
Blueprint endpoints follow REST conventions:
| Method | Route | Handler | Description |
|---|---|---|---|
| GET | /resources |
get_resources() |
List with pagination |
| GET | /resources/<id> |
get_resource(id) |
Get single resource |
| POST | /resources |
create_resource() |
Create resource |
| PATCH | /resources/<id> |
update_resource(id) |
Partial update |
| DELETE | /resources/<id> |
delete_resource(id) |
Delete resource |
Configuration
Use class-based configuration with environment variable overrides.
"""Application configuration."""
import os
from datetime import timedelta
from typing import Type
class Config:
"""Base configuration."""
SECRET_KEY = os.getenv("SECRET_KEY", "change-in-production")
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ENGINE_OPTIONS = {
"pool_pre_ping": True,
"pool_recycle": 300,
}
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", SECRET_KEY)
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=1)
JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30)
CORS_ORIGINS = os.getenv("CORS_ORIGINS", "*").split(",")
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = os.getenv(
"DATABASE_URL",
"postgresql://postgres:postgres@localhost:5432/myapp_dev"
)
SQLALCHEMY_ECHO = True
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = os.getenv(
"TEST_DATABASE_URL",
"postgresql://postgres:postgres@localhost:5432/myapp_test"
)
class ProductionConfig(Config):
DEBUG = False
SQLALCHEMY_DATABASE_URI = os.environ["DATABASE_URL"]
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Lax"
config: dict[str, Type[Config]] = {
"development": DevelopmentConfig,
"testing": TestingConfig,
"production": ProductionConfig,
}
Extensions
Centralize all Flask extension instances in a single file.
"""Flask extensions initialization."""
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_marshmallow import Marshmallow
from flask_jwt_extended import JWTManager
from flask_cors import CORS
db = SQLAlchemy()
migrate = Migrate()
ma = Marshmallow()
jwt = JWTManager()
cors = CORS()
Common extensions and their purposes:
| Extension | Purpose |
|---|---|
| Flask-SQLAlchemy | ORM and database integration |
| Flask-Migrate | Alembic database migrations |
| Flask-Marshmallow | Serialization and validation |
| Flask-JWT-Extended | JWT authentication |
| Flask-CORS | Cross-origin resource sharing |
| Flask-Limiter | Rate limiting |
| Flask-Caching | Response and data caching |
| Flask-Mail | Email sending |
Error Handling
Define a custom exception hierarchy and register handlers.
"""Custom exceptions and error handlers."""
from flask import jsonify
class AppError(Exception):
"""Base application error."""
def __init__(self, message: str, status_code: int = 400):
super().__init__(message)
self.message = message
self.status_code = status_code
class NotFoundError(AppError):
def __init__(self, message: str = "Resource not found"):
super().__init__(message, status_code=404)
class UnauthorizedError(AppError):
def __init__(self, message: str = "Unauthorized"):
super().__init__(message, status_code=401)
class ForbiddenError(AppError):
def __init__(self, message: str = "Forbidden"):
super().__init__(message, status_code=403)
class ConflictError(AppError):
def __init__(self, message: str = "Conflict"):
super().__init__(message, status_code=409)
def handle_validation_error(error):
return jsonify({"error": "Validation error", "details": error.messages}), 400
def handle_app_error(error: AppError):
return jsonify({"error": error.message}), error.status_code
def handle_not_found(error):
return jsonify({"error": "Not found"}), 404
def handle_internal_error(error):
return jsonify({"error": "Internal server error"}), 500
Jinja2 Templates
When building server-rendered pages (not just APIs):
- Templates live in
app/templates/with abase.htmllayout - Use template inheritance:
{% extends "base.html" %} - Use
{% block content %}for page-specific content - Escape user content automatically (Jinja2 autoescape is on by default)
- Use
url_for()for all URLs in templates - Keep logic out of templates; use filters and context processors
{# app/templates/base.html #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}My App{% endblock %}</title>
</head>
<body>
{% with messages = get_flashed_messages(with_categories=true) %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endwith %}
{% block content %}{% endblock %}
</body>
</html>
CLI Commands
Register custom CLI commands via Click.
"""Custom CLI commands."""
import click
from flask.cli import with_appcontext
from app.extensions import db
@click.command("seed-db")
@with_appcontext
def seed_db():
"""Seed database with initial data."""
# Create initial records
db.session.commit()
click.echo("Database seeded.")
Commands Reference
# Install dependencies
pip install -r requirements.txt
pip install -r requirements-dev.txt
# Set environment variables
export FLASK_APP=run.py
export FLASK_ENV=development
# Initialize database
flask db init
flask db migrate -m "Initial migration"
flask db upgrade
# Seed database
flask seed-db
# Run development server
flask run
# Run with Gunicorn (production)
gunicorn -w 4 -b 0.0.0.0:5000 "app:create_app('production')"
# Run tests
pytest
pytest -v --cov=app --cov-report=html
# Lint and format
black .
isort .
ruff check .
mypy app/
Dependencies
requirements.txt
Flask>=3.0.0
Flask-SQLAlchemy>=3.1.0
Flask-Migrate>=4.0.0
Flask-Marshmallow>=0.15.0
Flask-JWT-Extended>=4.6.0
Flask-CORS>=4.0.0
marshmallow-sqlalchemy>=0.29.0
psycopg2-binary>=2.9.9
python-dotenv>=1.0.0
gunicorn>=21.0.0
requirements-dev.txt
-r requirements.txt
pytest>=7.4.0
pytest-flask>=1.2.0
pytest-cov>=4.1.0
black>=23.0.0
isort>=5.12.0
mypy>=1.5.0
ruff>=0.1.0
Advanced Topics
For detailed code examples and advanced patterns, see:
- references/patterns.md -- Models, schemas, services, API endpoints, authentication, decorators, testing, and deployment patterns