Toolmingo
Guides18 min read

50 Flask Interview Questions (With Answers)

Top Flask interview questions with clear answers and code examples — covering routing, templating, blueprints, REST APIs, authentication, testing, and Flask best practices.

Flask interviews test Python fluency, HTTP fundamentals, REST API design, and the ability to work with a micro-framework without the guardrails of batteries-included frameworks like Django. This guide covers 50 of the most common Flask interview questions with concise answers and code examples.

Quick reference

Topic Most asked questions
Core concepts WSGI, application context, request context
Routing URL rules, variable rules, url_for
Templates Jinja2 filters, template inheritance, render_template
Request/Response request object, make_response, cookies, redirects
Blueprints Modular apps, blueprint registration
REST APIs JSON responses, status codes, Flask-RESTful
Database SQLAlchemy, Flask-Migrate, ORM patterns
Auth Session, Flask-Login, JWT
Testing test_client, fixtures, pytest
Production Gunicorn, config patterns, error handlers

Core concepts

1. What is Flask and how does it differ from Django?

Flask is a lightweight WSGI web framework written in Python. It provides routing, request handling, and a templating engine (Jinja2) — nothing more by default. Everything else (ORM, auth, admin) you choose yourself.

Feature Flask Django
Philosophy Micro-framework Batteries-included
ORM Bring your own (e.g., SQLAlchemy) Built-in Django ORM
Admin panel Third-party or custom Built-in
Auth Flask-Login / JWT Built-in auth system
Project structure Flexible Convention-driven
Learning curve Gentle Steeper but opinionated
Best for APIs, microservices, custom stacks Full-featured web apps

2. What is WSGI and why does Flask use it?

WSGI (Web Server Gateway Interface — PEP 3333) is the Python standard for communication between a web server (e.g., Nginx, Gunicorn) and a Python web application.

# Minimal WSGI app — Flask replaces all this boilerplate
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return [b'Hello, World!']

Flask implements WSGI so you can deploy it behind any WSGI-compatible server. For async workloads Flask also supports ASGI via asgiref.


3. What is the application context vs the request context?

Flask maintains two context stacks:

Context Object Pushed when Provides
Application g, current_app App starts / app.app_context() App-level state (DB connection, config)
Request request, session HTTP request arrives Per-request data
from flask import current_app, g, request

@app.route('/info')
def info():
    # request context is active here automatically
    print(request.method)       # 'GET'
    print(current_app.name)     # app name
    g.user = 'Alice'            # per-request scratch space
    return 'ok'

# Manual app context (e.g., CLI commands, tests)
with app.app_context():
    do_database_work()

4. What is g in Flask?

g ("global") is a per-request namespace — a plain object where you can store data for the duration of a single request. It is reset for every new request.

Common use: store the current user after loading it from the database once, then reuse it in multiple helper functions during the same request.

from flask import g, request

@app.before_request
def load_user():
    user_id = request.cookies.get('user_id')
    g.user = User.query.get(user_id) if user_id else None

5. How do you create a minimal Flask application?

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello, Flask!'

if __name__ == '__main__':
    app.run(debug=True)

Flask(__name__) tells Flask where to find templates and static files relative to the module.


Routing

6. How does Flask routing work?

Flask maps URLs to Python functions using the @app.route() decorator, which calls app.add_url_rule() internally.

@app.route('/users/<int:user_id>', methods=['GET', 'PUT', 'DELETE'])
def user_detail(user_id):
    ...

Variable converters:

Converter Example Matches
string (default) <name> Any text without a slash
int <int:id> Positive integers
float <float:value> Positive floats
path <path:subpath> Like string but includes slashes
uuid <uuid:token> UUID strings

7. What is url_for and why should you use it?

url_for generates a URL for a given view function by name, avoiding hardcoded strings that break when routes change.

from flask import url_for, redirect

@app.route('/login')
def login():
    ...

@app.route('/dashboard')
def dashboard():
    return redirect(url_for('login'))   # '/login'

# With parameters
url_for('user_detail', user_id=42)     # '/users/42'

# In Jinja2 templates
# <a href="{{ url_for('index') }}">Home</a>

8. What is the difference between redirect and abort?

from flask import redirect, abort, url_for

@app.route('/old')
def old():
    return redirect(url_for('new'), 301)    # permanent redirect

@app.route('/admin')
def admin():
    if not current_user.is_admin:
        abort(403)                           # raise HTTP error immediately
    ...

redirect sends the client to a new URL. abort immediately stops processing and raises an HTTP exception (handled by error handlers or Flask's default error pages).


9. How do you handle HTTP methods in Flask?

@app.route('/items', methods=['GET', 'POST'])
def items():
    if request.method == 'GET':
        return jsonify(items_list)
    if request.method == 'POST':
        data = request.get_json()
        # create item...
        return jsonify(new_item), 201

Or use MethodView for class-based views:

from flask.views import MethodView

class ItemView(MethodView):
    def get(self):
        return jsonify(items_list)
    def post(self):
        ...

app.add_url_rule('/items', view_func=ItemView.as_view('items'))

Templates

10. What templating engine does Flask use?

Flask uses Jinja2 — a fast, sandboxed templating engine for Python.

Key syntax:

{# Comment #}
{{ variable }}              {# output #}
{% if condition %}...{% endif %}
{% for item in items %}...{% endfor %}
{% block content %}{% endblock %}
{% extends "base.html" %}
{% include "partial.html" %}
{{ name | upper | truncate(20) }}   {# filters #}

11. What is template inheritance in Jinja2?

Template inheritance lets child templates override blocks defined in a parent template.

<!-- base.html -->
<!DOCTYPE html>
<html>
<head><title>{% block title %}My App{% endblock %}</title></head>
<body>
  {% block content %}{% endblock %}
</body>
</html>
<!-- page.html -->
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
  <h1>Welcome!</h1>
{% endblock %}

12. How do you pass data to templates?

from flask import render_template

@app.route('/profile/<username>')
def profile(username):
    user = User.query.filter_by(username=username).first_or_404()
    return render_template('profile.html', user=user, title='Profile')

Jinja2 also has access to request, session, g, config, and url_for automatically via Flask's template context.


13. What are Jinja2 filters and how do you create a custom one?

Filters transform values in templates: {{ value | filter_name }}.

# Register a custom filter
@app.template_filter('currency')
def currency_filter(value, symbol='$'):
    return f'{symbol}{value:,.2f}'
{{ product.price | currency }}          {# $1,234.56 #}
{{ product.price | currency('€') }}     {# €1,234.56 #}

Built-in filters: upper, lower, title, capitalize, trim, truncate, join, length, default, safe, escape, replace, tojson.


Request and Response

14. What data does the request object expose?

from flask import request

request.method          # 'GET', 'POST', etc.
request.args            # URL query params (?key=val) — ImmutableMultiDict
request.form            # POST form data — ImmutableMultiDict
request.json            # parsed JSON body (Content-Type: application/json)
request.get_json()      # safer JSON parse with force/silent options
request.files           # uploaded files — ImmutableMultiDict of FileStorage
request.cookies         # dict of cookies
request.headers         # HTTP headers
request.url             # full URL
request.path            # URL path only
request.host            # hostname
request.remote_addr     # client IP
request.is_json         # True if Content-Type is JSON

15. How do you return JSON from a Flask view?

from flask import jsonify

@app.route('/api/user/<int:uid>')
def get_user(uid):
    user = User.query.get_or_404(uid)
    return jsonify({
        'id': user.id,
        'name': user.name,
        'email': user.email
    })

# Custom status code + headers
return jsonify({'error': 'Not found'}), 404

# Flask 2.2+ — return dict directly (auto-converted to JSON)
return {'id': 1, 'name': 'Alice'}, 200

16. How do you set cookies and read them?

from flask import request, make_response

@app.route('/set-cookie')
def set_cookie():
    resp = make_response('Cookie set!')
    resp.set_cookie(
        'user_id', '42',
        max_age=60*60*24*30,    # 30 days
        httponly=True,
        samesite='Lax',
        secure=True             # HTTPS only
    )
    return resp

@app.route('/read-cookie')
def read_cookie():
    user_id = request.cookies.get('user_id', 'anonymous')
    return f'Hello user {user_id}'

17. What are Flask sessions?

Flask sessions store data client-side in a signed cookie (using the SECRET_KEY). The data is base64-encoded and signed — tamper-proof but not encrypted (visible to clients).

from flask import session

app.secret_key = 'your-secret-key'   # required

@app.route('/login', methods=['POST'])
def login():
    session['user_id'] = verified_user.id
    session.permanent = True          # respect PERMANENT_SESSION_LIFETIME
    return redirect(url_for('dashboard'))

@app.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))

For sensitive data or large payloads, use server-side sessions (Flask-Session with Redis/database backend).


Blueprints

18. What are Flask Blueprints?

Blueprints are modular components that group related routes, templates, and static files. They allow you to split a large Flask app into smaller, reusable pieces.

# auth/routes.py
from flask import Blueprint

auth_bp = Blueprint('auth', __name__, url_prefix='/auth')

@auth_bp.route('/login')
def login():
    ...

@auth_bp.route('/logout')
def logout():
    ...
# app.py
from auth.routes import auth_bp
from api.routes import api_bp

app = Flask(__name__)
app.register_blueprint(auth_bp)
app.register_blueprint(api_bp, url_prefix='/api/v1')

19. What is the typical Flask project structure for a medium app?

myapp/
├── app/
│   ├── __init__.py          # create_app() factory
│   ├── auth/
│   │   ├── __init__.py
│   │   └── routes.py        # Blueprint
│   ├── api/
│   │   ├── __init__.py
│   │   └── routes.py
│   ├── models.py
│   ├── extensions.py        # db = SQLAlchemy(), login_manager = ...
│   └── templates/
├── tests/
├── migrations/
├── config.py
├── requirements.txt
└── wsgi.py

20. What is the Application Factory pattern?

Instead of creating app = Flask(__name__) at module level (which makes testing hard), create a factory function:

# app/__init__.py
from flask import Flask
from .extensions import db, login_manager
from .auth.routes import auth_bp
from .api.routes import api_bp

def create_app(config_name='default'):
    app = Flask(__name__)
    app.config.from_object(config[config_name])

    db.init_app(app)
    login_manager.init_app(app)

    app.register_blueprint(auth_bp)
    app.register_blueprint(api_bp)

    return app

This enables:

  • Multiple app instances (testing vs. production)
  • Avoiding circular imports
  • Clean extension initialization

REST APIs

21. How do you build a REST API with Flask?

from flask import Flask, jsonify, request, abort

app = Flask(__name__)
items = {}

@app.route('/api/items', methods=['GET'])
def list_items():
    return jsonify(list(items.values()))

@app.route('/api/items', methods=['POST'])
def create_item():
    data = request.get_json(force=True)
    if not data or 'name' not in data:
        abort(400)
    item = {'id': len(items) + 1, 'name': data['name']}
    items[item['id']] = item
    return jsonify(item), 201

@app.route('/api/items/<int:item_id>', methods=['GET'])
def get_item(item_id):
    return jsonify(items.get(item_id) or abort(404))

@app.route('/api/items/<int:item_id>', methods=['PUT'])
def update_item(item_id):
    if item_id not in items:
        abort(404)
    data = request.get_json(force=True)
    items[item_id].update(data)
    return jsonify(items[item_id])

@app.route('/api/items/<int:item_id>', methods=['DELETE'])
def delete_item(item_id):
    items.pop(item_id, None) or abort(404)
    return '', 204

22. How do you handle errors in Flask?

from flask import jsonify

@app.errorhandler(404)
def not_found(error):
    return jsonify({'error': 'Not found'}), 404

@app.errorhandler(422)
def unprocessable(error):
    return jsonify({'error': 'Unprocessable entity'}), 422

@app.errorhandler(Exception)
def handle_exception(e):
    app.logger.error(f'Unhandled exception: {e}', exc_info=True)
    return jsonify({'error': 'Internal server error'}), 500

23. How do you validate request data in Flask?

Common approaches:

# Option 1: marshmallow
from marshmallow import Schema, fields, ValidationError

class UserSchema(Schema):
    name  = fields.Str(required=True, validate=lambda x: len(x) > 0)
    email = fields.Email(required=True)
    age   = fields.Int(load_default=None)

@app.route('/users', methods=['POST'])
def create_user():
    try:
        data = UserSchema().load(request.get_json())
    except ValidationError as err:
        return jsonify(err.messages), 422
    ...

# Option 2: pydantic (v2)
from pydantic import BaseModel, EmailStr

class UserIn(BaseModel):
    name: str
    email: EmailStr
    age: int | None = None

@app.route('/users', methods=['POST'])
def create_user():
    try:
        user = UserIn.model_validate(request.get_json())
    except ValueError as e:
        return jsonify({'error': str(e)}), 422
    ...

24. What is CORS and how do you enable it in Flask?

Cross-Origin Resource Sharing restricts browser requests to same-origin by default. Use Flask-CORS:

from flask_cors import CORS

app = Flask(__name__)
CORS(app)                                   # allow all origins (dev only)

# Production — restrict to specific origins
CORS(app, resources={
    r'/api/*': {'origins': 'https://yourfrontend.com'}
})

25. How do you implement rate limiting in Flask?

from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(get_remote_address, app=app,
                  default_limits=['200 per day', '50 per hour'])

@app.route('/api/search')
@limiter.limit('10 per minute')
def search():
    ...

Database

26. How do you use SQLAlchemy with Flask?

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:pass@localhost/mydb'
db = SQLAlchemy(app)

class User(db.Model):
    id       = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email    = db.Column(db.String(120), unique=True, nullable=False)
    posts    = db.relationship('Post', backref='author', lazy='select')

    def __repr__(self):
        return f'<User {self.username}>'

27. What are common SQLAlchemy query patterns?

# Read
User.query.all()
User.query.get(1)                           # deprecated in SQLAlchemy 2.x
db.session.get(User, 1)                     # SQLAlchemy 2.x style
User.query.filter_by(username='alice').first()
User.query.filter(User.age > 18).order_by(User.name).limit(10).all()
User.query.filter_by(username='alice').first_or_404()

# Create
user = User(username='alice', email='alice@example.com')
db.session.add(user)
db.session.commit()

# Update
user.email = 'newalice@example.com'
db.session.commit()

# Delete
db.session.delete(user)
db.session.commit()

# Bulk
db.session.execute(db.update(User).where(User.active == False).values(active=True))
db.session.commit()

28. What is Flask-Migrate and how do you use it?

Flask-Migrate wraps Alembic to manage database migrations with version control.

flask db init        # creates migrations/ folder (once)
flask db migrate -m "add users table"   # auto-detect model changes
flask db upgrade     # apply migrations
flask db downgrade   # revert last migration
flask db history     # show migration history

29. What is the N+1 query problem in SQLAlchemy?

Loading related objects in a loop triggers N extra queries:

# BAD — N+1: 1 query for users + N queries for posts
users = User.query.all()
for user in users:
    print(user.posts)          # each access fires a SELECT

# GOOD — eager loading: 1 query with JOIN
users = User.query.options(db.joinedload(User.posts)).all()
for user in users:
    print(user.posts)          # no extra queries

Authentication

30. How do you implement session-based auth with Flask-Login?

from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user

login_manager = LoginManager(app)
login_manager.login_view = 'auth.login'

class User(UserMixin, db.Model):
    ...

@login_manager.user_loader
def load_user(user_id):
    return db.session.get(User, int(user_id))

@app.route('/login', methods=['POST'])
def login():
    user = User.query.filter_by(email=request.form['email']).first()
    if user and check_password_hash(user.password, request.form['password']):
        login_user(user, remember=True)
        return redirect(url_for('dashboard'))
    return 'Invalid credentials', 401

@app.route('/dashboard')
@login_required
def dashboard():
    return f'Hello, {current_user.username}'

@app.route('/logout')
def logout():
    logout_user()
    return redirect(url_for('index'))

31. How do you implement JWT authentication in Flask?

from flask_jwt_extended import (
    JWTManager, create_access_token, jwt_required, get_jwt_identity
)

app.config['JWT_SECRET_KEY'] = 'super-secret'
jwt = JWTManager(app)

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    user = User.query.filter_by(email=data['email']).first()
    if not user or not check_password_hash(user.password, data['password']):
        return jsonify({'error': 'Bad credentials'}), 401
    token = create_access_token(identity=user.id, expires_delta=timedelta(hours=1))
    return jsonify({'access_token': token})

@app.route('/me')
@jwt_required()
def me():
    user_id = get_jwt_identity()
    user = db.session.get(User, user_id)
    return jsonify({'id': user.id, 'email': user.email})

32. How do you hash passwords in Flask?

Use Werkzeug's (built-in to Flask) password hashing:

from werkzeug.security import generate_password_hash, check_password_hash

# Hash on registration
hashed = generate_password_hash('mypassword', method='pbkdf2:sha256')

# Verify on login
check_password_hash(hashed, 'mypassword')   # True
check_password_hash(hashed, 'wrongpass')    # False

Never store plain-text passwords. PBKDF2/bcrypt/scrypt are all safe choices.


Testing

33. How do you test a Flask application?

Flask provides a test client that simulates HTTP requests without starting a real server.

# conftest.py
import pytest
from app import create_app, db as _db

@pytest.fixture
def app():
    app = create_app('testing')
    with app.app_context():
        _db.create_all()
        yield app
        _db.drop_all()

@pytest.fixture
def client(app):
    return app.test_client()
# test_api.py
def test_get_items_empty(client):
    resp = client.get('/api/items')
    assert resp.status_code == 200
    assert resp.get_json() == []

def test_create_item(client):
    resp = client.post('/api/items',
                       json={'name': 'Widget'},
                       content_type='application/json')
    assert resp.status_code == 201
    data = resp.get_json()
    assert data['name'] == 'Widget'

def test_get_nonexistent(client):
    resp = client.get('/api/items/999')
    assert resp.status_code == 404

34. How do you test authenticated routes in Flask?

def test_protected_route(client, app):
    # Log in first
    with client:
        with client.session_transaction() as sess:
            sess['user_id'] = 1          # fake session data
        resp = client.get('/dashboard')
        assert resp.status_code == 200

# Or via login endpoint
def test_login_and_access(client):
    client.post('/login', json={'email': 'test@test.com', 'password': 'pass'})
    resp = client.get('/dashboard')
    assert resp.status_code == 200

35. How do you use before_request and after_request?

@app.before_request
def check_maintenance():
    if app.config.get('MAINTENANCE_MODE') and request.path != '/status':
        abort(503)

@app.before_request
def load_logged_in_user():
    user_id = session.get('user_id')
    g.user = db.session.get(User, user_id) if user_id else None

@app.after_request
def add_security_headers(response):
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'DENY'
    return response

@app.teardown_appcontext
def close_db(error):
    db.session.remove()

Configuration

36. What are the different ways to configure Flask?

# 1. Direct assignment
app.config['DEBUG'] = True

# 2. From a Python object
class ProductionConfig:
    DEBUG = False
    SQLALCHEMY_DATABASE_URI = 'postgresql://...'
    SECRET_KEY = os.environ['SECRET_KEY']

app.config.from_object(ProductionConfig)

# 3. From environment variable pointing to a file
app.config.from_envvar('APP_CONFIG')   # reads path from $APP_CONFIG

# 4. From a .env file (with python-dotenv)
from dotenv import load_dotenv
load_dotenv()
app.config['SECRET_KEY'] = os.environ['SECRET_KEY']

37. How do you handle different environments (dev/staging/prod)?

# config.py
import os

class BaseConfig:
    TESTING = False
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-secret')

class DevelopmentConfig(BaseConfig):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///dev.db'

class TestingConfig(BaseConfig):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'

class ProductionConfig(BaseConfig):
    SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']
    SECRET_KEY = os.environ['SECRET_KEY']

config = {
    'development': DevelopmentConfig,
    'testing': TestingConfig,
    'production': ProductionConfig,
    'default': DevelopmentConfig,
}

Performance and production

38. How do you deploy Flask in production?

Never use Flask's built-in dev server in production. Use a WSGI server:

# Gunicorn (most common)
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:8000 wsgi:app

# uWSGI
uwsgi --http :8000 --module wsgi:app --processes 4 --threads 2

# Hypercorn (ASGI/async Flask 2.x)
hypercorn wsgi:app --bind 0.0.0.0:8000

Typical production stack:

Client → Nginx (reverse proxy, TLS, static files) → Gunicorn → Flask app → PostgreSQL

39. How do you add caching to Flask?

from flask_caching import Cache

cache = Cache(app, config={'CACHE_TYPE': 'RedisCache',
                           'CACHE_REDIS_URL': 'redis://localhost:6379'})

@app.route('/expensive')
@cache.cached(timeout=300)           # cache for 5 minutes
def expensive_view():
    result = run_heavy_query()
    return jsonify(result)

# Cache a function result with a custom key
@cache.memoize(timeout=60)
def get_user_stats(user_id):
    ...

# Invalidate manually
cache.delete_memoized(get_user_stats, user_id)
cache.clear()

40. How do you log in Flask?

import logging
from flask import Flask

app = Flask(__name__)

# Flask uses Python's logging module
app.logger.info('Server started')
app.logger.warning('Missing config key')
app.logger.error('Database error', exc_info=True)

# Production: structured JSON logging
import json_log_formatter
formatter = json_log_formatter.JSONFormatter()
handler = logging.StreamHandler()
handler.setFormatter(formatter)
app.logger.addHandler(handler)
app.logger.setLevel(logging.INFO)

Advanced topics

41. What are Flask signals and when would you use them?

Flask uses blinker to provide signals — a lightweight pub/sub mechanism for decoupled event handling.

from flask import request_started, request_finished, got_request_exception

@request_started.connect_via(app)
def on_request_started(sender, **extra):
    print(f'Request started: {request.path}')

@got_request_exception.connect_via(app)
def on_exception(sender, exception, **extra):
    sentry_sdk.capture_exception(exception)

Use signals to hook into Flask's lifecycle without modifying core code — ideal for monitoring, logging, and cross-cutting concerns.


42. How do you run background tasks in Flask?

Flask itself is synchronous. For background work, use:

Tool Best for
Celery + Redis/RabbitMQ Long tasks, scheduled jobs, retries
RQ (Redis Queue) Simpler Celery alternative
threading.Thread Quick fire-and-forget (not for production)
APScheduler Scheduled/recurring tasks
# Celery example
from celery import Celery

celery = Celery(app.name, broker='redis://localhost:6379/0')
celery.conf.update(app.config)

@celery.task
def send_email_async(to, subject, body):
    # send email...
    pass

# In a route
@app.route('/send')
def send():
    send_email_async.delay('user@example.com', 'Hi', 'Hello!')
    return 'Email queued', 202

43. What is Flask-SocketIO?

Flask-SocketIO adds WebSocket support to Flask for real-time bidirectional communication.

from flask_socketio import SocketIO, emit

socketio = SocketIO(app, cors_allowed_origins='*')

@socketio.on('message')
def handle_message(data):
    emit('response', {'text': f'Echo: {data["text"]}'}, broadcast=True)

if __name__ == '__main__':
    socketio.run(app, debug=True)

44. What is the difference between @app.before_request and @app.before_first_request?

  • @app.before_request — runs before every request.
  • @app.before_first_request — ran before the first request only (removed in Flask 2.3; use with app.app_context() in create_app() instead or cli commands).

45. How do you serve static files in Flask?

Flask automatically serves files from the static/ folder:

# URL: /static/style.css → file: static/style.css

# In templates
url_for('static', filename='style.css')    # '/static/style.css'

# Custom static folder
app = Flask(__name__, static_folder='assets', static_url_path='/assets')

In production, serve static files via Nginx instead of Flask for performance.


Anti-patterns

Anti-pattern Problem Fix
App-level app = Flask(__name__) without factory Hard to test, circular imports Use create_app() factory
Storing secrets in source code Security breach Use os.environ or a secrets manager
Using app.run() in production Single-threaded, no restart on crash Use Gunicorn / uWSGI
debug=True in production Exposes code in browser error pages Always False in prod
db.session.commit() in every route without rollback Silent data inconsistency Use context managers or teardown
Importing app into blueprints (circular import) Import errors Use current_app proxy or factory pattern
SELECT in a loop (N+1) Slow queries joinedload() / subqueryload()
session for large data Cookie size limit (4KB) Server-side sessions (Flask-Session)

Flask vs Django vs FastAPI

Dimension Flask Django FastAPI
Type Micro-framework Full-framework Async API framework
Performance Good (sync) Good (sync) Excellent (async)
ORM Bring your own Django ORM (built-in) SQLAlchemy / SQLModel
Admin panel Third-party Built-in Third-party
Auto API docs Manual / Flask-RESTX DRF Spectacular Built-in (Swagger + ReDoc)
Request validation marshmallow / Pydantic DRF Serializers Pydantic v2 (built-in)
Learning curve Low Medium Low-Medium
Async support Partial (Flask 2.x) Partial (Django 3.1+) First-class
Best for APIs, microservices, flexibility Full web apps, CMS High-performance APIs, ML serving

Common mistakes

Mistake Why it's wrong Fix
app.run(debug=True) in production Exposes debugger PIN and source Set DEBUG=False via env var
Hardcoded SECRET_KEY = 'secret' Sessions compromised if leaked Use a 32-byte random value from secrets.token_hex()
Missing if __name__ == '__main__': guard Module re-runs on import Always guard app.run()
Mutating request.args or request.form They're ImmutableMultiDict Create a new dict: dict(request.args)
Using sqlite:///db.sqlite3 in production No concurrent writes Use PostgreSQL or MySQL
No input validation on API endpoints Injection / crashes Validate with marshmallow or Pydantic
Catching Exception without re-raising Swallows crashes silently Log + return 500, or use @app.errorhandler(Exception)
Blocking I/O without threading/async Request queue stalls Use Celery for slow tasks, or Flask 2.x async views

FAQ

Q: When should I choose Flask over Django? A: Flask when you need flexibility — microservices, APIs, or a custom tech stack (e.g., MongoDB + your own auth). Django when you need rapid full-stack development with admin, ORM, and auth out of the box.

Q: Is Flask suitable for large applications? A: Yes. Use the Application Factory pattern + Blueprints to keep it modular. Companies like Pinterest and LinkedIn have used Flask at scale.

Q: What is Flask 2.x vs older Flask? A: Flask 2.0 (2021) added native async view support, async def routes, and removed deprecated features like before_first_request. Flask 3.0 (2023) dropped Python 2 support and removed more legacy APIs.

Q: What is the difference between jsonify and json.dumps? A: jsonify creates a Flask Response object with Content-Type: application/json. json.dumps returns a plain string — you'd still need to wrap it in make_response with the right content type.

Q: How does Flask handle concurrency? A: By default, Flask's dev server is single-threaded. Gunicorn spawns multiple worker processes (each handling one request at a time). For I/O concurrency, use gevent workers or Flask's native async views with an ASGI server.

Q: What is flask shell used for? A: It starts a Python REPL with the application context pre-loaded (so you can run User.query.all() without any setup). Add objects to it via @app.shell_context_processor.

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools