Error Handlers & Middleware
Centralized error handling, custom exceptions, and before/after request hooks
Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.
UpgradeKnowledge Debt detected
You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.
Explanation
Production Flask apps need consistent responses and cross-cutting behavior via .
Registering error handlers:
python from flask import Flask, jsonify app = Flask(__name__) @app.errorhandler(404) def not_found(error): return jsonify({"error": "Resource not found"}), 404 @app.errorhandler(405) def method_not_allowed(error): return jsonify({"error": "Method not allowed"}), 405 @app.errorhandler(500) def server_error(error): return jsonify({"error": "Internal server error"}), 500
Custom exception classes:
python class APIError(Exception): def __init__(self, message: str, status_code: int = 400): self.message = message self.status_code = status_code @app.errorhandler(APIError) def handle_api_error(error: APIError): return jsonify({"error": error.message}), error.status_code # Usage in routes: @app.route("/users/<int:user_id>") def get_user(user_id): user = db.get(user_id) if not user: raise APIError("User not found", 404) return jsonify(user)
Request lifecycle hooks:
python @app.before_request def log_request(): app.logger.info(f"{request.method} {request.path}") @app.after_request def add_cors_headers(response): response.headers["Access-Control-Allow-Origin"] = "*" return response @app.teardown_request def cleanup(exception): # Always runs, even if an exception occurred db.session.remove()
flask.g — request-scoped storage: ``python from flask import g @app.before_request def set_request_id(): g.request_id = str(uuid.uuid4()) @app.after_request def add_request_id_header(response): response.headers["X-Request-ID"] = g.request_id return response
Examples
Catch-all error handler with logging
Catch-all Exception handler ensures no stack traces leak to clients
import traceback
from flask import Flask, jsonify, request
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
class NotFoundError(Exception): pass
class ValidationError(Exception): pass
@app.errorhandler(NotFoundError)
def not_found(e):
return jsonify({"error": str(e)}), 404
@app.errorhandler(ValidationError)
def validation(e):
return jsonify({"error": str(e)}), 422
@app.errorhandler(Exception)
def unexpected(e):
app.logger.error(f"Unhandled exception on {request.path}:
{traceback.format_exc()}")
return jsonify({"error": "Unexpected error"}), 500Auth middleware via before_request
before_request auth middleware — set g.user_id for all authenticated routes
from functools import wraps
from flask import g, jsonify, request
EXEMPT_PATHS = {"/health", "/auth/login", "/auth/register"}
@app.before_request
def authenticate():
if request.path in EXEMPT_PATHS:
return None # skip auth for public routes
token = request.headers.get("Authorization", "").removeprefix("Bearer ")
if not token:
return jsonify({"error": "Token required"}), 401
payload = verify_jwt(token) # raises on invalid
if not payload:
return jsonify({"error": "Invalid token"}), 401
g.user_id = payload["user_id"]
g.user_role = payload.get("role", "user")How well did you understand this?
Next in Flask
Flask-SQLAlchemy