Skip to main content
友田 陽大
Flask in production
Python
Flask
FastAPI
Django
技術選定
アーキテクチャ設計
バックエンド

Flask vs. FastAPI vs. Django technology-selection guide: which to choose in which situation (2026 edition, production-operation decision axes)

A technology-selection guide comparing the differences of Flask, FastAPI, and Django from the viewpoint of production operation. It organizes architecture philosophy, sync/async, type validation, the admin screen, the learning curve, and current versions in a table, and shows the recommended framework by scenario — REST API, high-concurrency IO, instant admin screen, staged migration. The definitive guide to the Flask FastAPI Django difference, comparison, and selection.

Published
Reading time
17 min read
Author
友田 陽大
Share
Contents

Introduction: "which is superior" is the wrong question

"Flask, FastAPI, Django — in the end, which should I choose?" — there's no single correct answer to this question. The three are bundled into the same category of "Python web framework," but the problems they try to solve differ. It's not superiority/inferiority but a matter of fit: "for your requirements, which reaches the goal with the least debt."

This article is a decision framework to judge that fit. I designed and implemented the backend of a B2B SaaS that won the Minister of Economy, Trade and Industry Award with Flask / SQLAlchemy / PostgreSQL and operate it in production (the lumber-distribution DX case study), and on a separate project I've built an API service with FastAPI too. So this comparison is the viewpoint of "someone who has run both in production." That's exactly why I'll clearly write the situations where I shouldn't recommend Flask too. Read it not as a sales pitch but as a tool for technology selection.

💡 The versions handled in this article (as of June 2026): Flask 3.1 family (latest stable 3.1.3, 2026-02 / min Python 3.9), FastAPI 0.13x family (around 0.138.x in mid-2026, still below 1.0 / requires Python 3.10+), Django 6.0 family (the current LTS is 5.2 LTS, extended support until April 2028 / the next LTS is 6.2 LTS planned for April 2027). Framework APIs and recommendations are based on the latest official documentation.


1. Conclusion first: a 30-second TL;DR and overall comparison table

The one-line guideline when in doubt is this.

  • Want to build a REST/JSON API type-safely and high-concurrency → FastAPI
  • Want to stand up the "whole web app" with admin, auth, ORM included in the shortest path → Django
  • Want to choose the configuration one by one / build small-to-mid scale thinly → Flask

Let me first make this intuition into a big picture with one table. Each section below digs into a row of this table.

AspectFlaskFastAPIDjango
Philosophymicro (core only, assemble yourself)API-first (type-driven)batteries-included (all-in)
FoundationWSGI (Werkzeug + Jinja)ASGI (Starlette + Pydantic)own (WSGI/ASGI both)
Sync/asyncsync-based (async bolted on)async-native (async def)sync-based (async partial support)
Type validationnone built in (add marshmallow / Pydantic)type hints = validation with PydanticForms / DRF Serializer
Admin screennone (homemade or Flask-Admin)none (homemade)auto-generated by default
ORMnone (add SQLAlchemy, etc.)none (add SQLAlchemy, etc.)Django ORM included
Migrationnone (Flask-Migrate/Alembic)none (Alembic, etc.)makemigrations included
Authnone (Flask-Login, etc.)none (homemade / library)auth/permissions included
Auto API docsnoneOpenAPI + Swagger UI autoseparately with DRF
Learning curvelow (small core) / design needs honingmedium (need to understand types and async)medium-high (many conventions)
Representative usesmall-to-mid services, internal tools, projects needing selection freedomhigh-concurrency API, microservices, type-contract emphasiscontent/CRUD-centric whole web apps
Current version (2026/06)3.1.3 (stable)0.138.x (0.x family)6.0 / LTS is 5.2

⚠️ Don't read this table as a "score sheet." "No admin screen" is not a defect of Flask/FastAPI but a design judgment. Precisely because it doesn't have features you don't need, it's thin and fast. Conversely, if the admin screen is a requirement, Django, which "has" it, pulls a step ahead. The key of comparison is not the abundance of features but the match with your requirements.


2. Flask: a minimal core and the freedom of design (and its responsibility)

2.1 Philosophy: you decide what to load

Flask is a WSGI microframework. Its foundation is Werkzeug (the WSGI/HTTP layer) and Jinja (templates), and what Flask itself provides is only the core of "routing, request/response, templates, configuration, context." It doesn't include an ORM, form validation, an admin screen, or auth.

The official calling it "micro" doesn't mean "poor in features" but "you decide what to load." If you use a DB, Flask-SQLAlchemy; forms/CSRF, Flask-WTF; migrations, Flask-Migrate; API input/output validation, marshmallow — explicitly incorporate only what you need as extensions.

2.2 What it includes / doesn't include

CategoryFlask's handlingTypical addition
Routing, requestbuilt in
Templatesbuilt in (Jinja)
ORMnoneFlask-SQLAlchemy / SQLAlchemy
MigrationnoneFlask-Migrate (Alembic wrapper)
Forms/CSRFnoneFlask-WTF
Input/output validationnonemarshmallow / Pydantic
Admin screennoneFlask-Admin (or homemade)
AuthnoneFlask-Login / Authlib

The minimal form is literally a few lines.

# hello.py — Flask の「核」だけが見える最小形
from flask import Flask

app = Flask(__name__)


@app.get("/health")
def health():
    return {"status": "ok"}

2.3 The async story: it's bolted on, and throughput doesn't rise

Flask supports async def views from 2.0 (pip install flask[async]). But misunderstand this and you'll get hurt. As the official clearly cautions, each request still occupies 1 worker, and making a view async doesn't change "the number of requests handled concurrently." Flask's async works for IO concurrency like "calling multiple external APIs in parallel within one view," not throughput improvement.

If full-fledged async (high concurrency, WebSocket, streaming) is a requirement, the official position is to consider Quart, which is ASGI-native with the same API as Flask. In other words, the moment you choose Flask, it's the correct design judgment to resign that "raw high-concurrency async isn't the main battlefield."

2.4 The type/validation story: protect the boundary yourself

Flask has no type-driven validation. You protect the API's entrance (request → internal) and exit (internal → response) yourself with marshmallow or Pydantic. What I've used in production is marshmallow, where load() handles input validation and dump() handles response formatting, and dump_only / load_only structurally prevent mass assignment and confidentiality leaks. I wrote this design in detail in designing a production REST API with marshmallow × Flask × SQLAlchemy.

2.5 Ecosystem maturity

Flask is long-lived and its extension ecosystem is vast. Its strength is that general-purpose libraries not specific to Flask like SQLAlchemy, Alembic, Celery, and Authlib can be used as-is. Conversely, since there's no "official correct set," the good or bad of selection is left to the team.

2.6 Suited situations / honest weaknesses

Suited: small-to-mid services, internal tools, projects where you want to scrutinize the configuration one by one, projects with existing SQLAlchemy assets, staged replacement of legacy.

Honest weaknesses:

  • You must design the structure yourself. Without designing the application factory, Blueprints, configuration, context, and deployment, a small app slides straight into "legacy dominated by global variables and import order." This is not a feature deficiency of Flask but a matter of design responsibility; the big picture of production design is summarized in the Flask production-operations guide (pillar), and how to build a large-scale configuration in the application-factory / Blueprint large-app structure guide.
  • Async is bolted on, and high concurrency isn't the main battlefield (§2.3).
  • No auto API docs. If you want OpenAPI/Swagger, incorporate it separately.

💡 In my experience, most Flask failures come not from "Flask was weak" but from "skipping structural design." Conversely, a Flask with create_app and layer separation (Router → Schema → Model) in from the start becomes a thinner, more readable, more testable backend than FastAPI or Django. Flask is a framework that demands "design responsibility" in exchange for "freedom."


3. FastAPI: type-driven validation and async-native

3.1 Philosophy: get validation, conversion, and docs at once with standard type hints

FastAPI is a modern framework that builds APIs on the foundation of standard Python type hints. Its foundation is Starlette (the ASGI web layer) and Pydantic (data validation), and the async def path operation is a first-class citizen. The biggest differentiator is that just by writing type hints, validation, conversion, OpenAPI-schema generation, and interactive docs come automatically.

# main.py — 型ヒントが、そのまま検証とドキュメントになる
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


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


@app.post("/items")
async def create_item(item: ItemIn) -> ItemIn:
    # item は検証済み。不正な JSON は FastAPI が 422 で弾く
    return item

With just this code, interactive API docs are auto-generated at /docs (Swagger UI) and /redoc (ReDoc). This is FastAPI-specific immediacy that Flask/Django don't have.

3.2 Async-native: this is the decisive difference from Flask

Because FastAPI stands on ASGI, it can truly concurrently handle IO-bound processing written with async def. High-concurrency network IO, WebSocket, and streaming responses are the main battlefield. But there's a different kind of pitfall here from Flask — call synchronous blocking processing (a DB library that can't be awaited, etc.) inside async def and it stops the event loop and freezes the whole API. Using async def and ordinary def separately requires discipline. This criterion is detailed in this site's FastAPI production-operations guide (where to use async is FastAPI's biggest leverage and its biggest pitfall).

3.3 The type/validation story: Pydantic at the core

FastAPI's validation is all Pydantic. Separate the input model and output model, and instantly model an external API's JSON with model_validate too — the design of "killing data at the boundary" can be written with type hints alone. Pydantic v2's core validation is written in Rust and fast. For the design of Pydantic itself, see the Pydantic v2 production-validation / type-safety guide. Choosing FastAPI = placing Pydantic at the core.

3.4 What it includes / doesn't include

CategoryFastAPI's handling
Routing, asyncbuilt in (Starlette)
Input/output validationbuilt in (Pydantic type hints)
OpenAPI / Swagger UI / ReDocauto-generated
Dependency injection (DI)built in (Depends)
ORMnone (add SQLAlchemy, etc.)
Migrationnone (Alembic, etc.)
Admin screennone
Authhas primitives (OAuth2, etc., utilities). Implement yourself

In other words, FastAPI is in an intermediate position of "the parts needed for an API (validation, docs, DI) are batteries-included, but the whole web app (ORM, admin, auth) you assemble yourself."

3.5 Suited situations / honest weaknesses

Suited: high-concurrency IO-bound APIs, microservices, projects where you want to share a type-safe API contract across a team, projects where you want to align types with the frontend (TypeScript) via OpenAPI, AI/ML inference APIs.

Honest weaknesses:

  • Still in the 0.x family. It's used stably and widely, but the version is below 1.0 and minor updates can include breaking changes. It needs the operational discipline of version pinning (==) and CHANGELOG following.
  • The batteries stop at the API. ORM, migration, admin screen, and auth you still assemble yourself. Expect "all-in" like Django and you'll be let down.
  • The discipline of async correctness is essential (§3.2).

4. Django: batteries-included and the power of conventions

4.1 Philosophy: follow conventions and the "whole web app" stands up in the shortest path

Django is a "batteries-included" full-stack framework. It includes ORM, an auto admin screen, auth/permissions, migrations, forms, and templates from the start, and you assemble along the convention of the MTV (Model-Template-View) architecture. It supports both WSGI and ASGI.

Its biggest weapon is django-admin (the auto admin screen). Just define a model and register a few lines, and a management UI with CRUD, search, and permissions is auto-generated. This is Django-specific immediacy that doesn't exist in Flask/FastAPI.

# models.py — モデルを定義し…
from django.db import models


class Article(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    published_at = models.DateTimeField(null=True, blank=True)
# admin.py — 数行登録するだけで、CRUD 管理画面が自動で生える
from django.contrib import admin
from .models import Article

admin.site.register(Article)

4.2 What it includes / doesn't include

CategoryDjango's handling
ORMbuilt in (Django ORM)
Migrationbuilt in (makemigrations / migrate)
Admin screenbuilt in (auto-generated)
Auth/permissionsbuilt in
Formsbuilt in
Templatesbuilt in
REST APIadding DRF (Django REST Framework) is the standard
asyncpartial support (some of views and ORM)

4.3 The API story: DRF is standard

To build a JSON API with Django, loading Django REST Framework (DRF) is the standard. DRF's Serializer handles input/output validation/formatting. But there's no unity of "type hints = validation = auto OpenAPI" like FastAPI, and there's the writing volume of writing a separate Serializer. "FastAPI if API-only, Django + DRF if the API is also needed as part of an admin-screen-equipped web app" is the rough fork.

4.4 The async story: partial support

Django supports async views and some async ORM, but it's not async-native like FastAPI. Because the whole ecosystem still includes many sync-premised spots, if "high-concurrency async is the main purpose," FastAPI is more straightforward.

4.5 Suited situations / honest weaknesses

Suited: content-management / CRUD-centric whole web apps, projects where the admin screen and auth are instantly mandatory, projects where you want to build fast as a team riding the conventions, internal business systems.

Honest weaknesses:

  • Heavy and opinionated. For requirements that don't ride Django's conventions, it instead becomes a fight. It's overkill for projects that "want only the core."
  • Verbose for API-only. For a pure JSON API, DRF's writing volume and the ORM's constraints feel heavy against FastAPI's lightness.
  • Async is partial (§4.4).

5. The decision framework: scenario → recommendation

Let me land the details so far into actual requirements. Your starting point is decided by which row your project is closest to.

Your scenarioFirst candidateReasonRunner-up
REST/JSON API-centric. Want to share the type contract with the frontFastAPItype hints = validation = auto OpenAPIFlask + marshmallow
High-concurrency IO-bound, WebSocket, streamingFastAPItrue async concurrency with ASGIQuart
Admin screen, auth, ORM right now and mandatoryDjangoall included, admin auto-generated
Content/CRUD-centric whole web appDjangoshortest arrival with MTV conventionsFlask
Want to choose the configuration one by one, build thinlyFlaskcore only, assemble with extensionsFastAPI
Want to start small and expand to fit requirementsFlaskgrow incrementally from minimalFastAPI
Have existing SQLAlchemy / Flask assets, team skillsFlaskminimal learning and migration costFastAPI
AI/ML inference API (type-safe I/O)FastAPIstrict I/O with PydanticFlask
Internal business system (permission management is key)Djangoauth/permissions/admin includedFlask
Many lightweight microservices lined upFastAPIlightweight, async, auto-docsFlask

💡 It can also be organized with three decision questions. ① "Do you want the admin screen, auth, and ORM all right now?" → Yes → Django. ② (If No) "Is high-concurrency async / a type-driven API the main purpose?" → Yes → FastAPI. ③ (If No to both) "Do you want to choose the configuration yourself and build thinly?" → Yes → Flask. Ask in this order and most projects naturally converge to one.


6. When to choose Flask / when not to (the honest version)

Precisely because I've operated Flask in production myself, I'll write this clearly.

6.1 When to choose Flask

  • Want to scrutinize and choose the configuration: want to choose the ORM, validation, auth, and task queue one by one, optimal for the project.
  • Want to build small-to-mid scale thinly: want to not pile on features and fit the requirements minimally.
  • Have existing assets: have SQLAlchemy / marshmallow / an existing Flask codebase, and want to keep learning/migration cost down.
  • Sync processing is the main: typical CRUD / business logic, where ultra-high-concurrency async isn't a requirement.
  • Have the resolve to take on design: can put in create_app, Blueprints, and layer separation from the start (large-app structure guide).

6.2 When not to choose Flask

  • High-concurrency async is the main purpose: if WebSocket, massive simultaneous connections, or streaming is central, FastAPI (or Quart).
  • Want the admin screen, auth, and ORM all instantly: if you don't want to pay the homemade effort, Django.
  • Want auto API docs as a premise: if you want to "get OpenAPI/Swagger without writing it," FastAPI.
  • Want to make the type-driven API contract a team discipline: if you place Pydantic at the core, FastAPI.
  • No structure to take on design: if it'll become an operation that skips structural design, Django, which protects you with conventions, has fewer accidents.

⚠️ The last point is important. Flask's "freedom," unless paired with design discipline, becomes debt. If the team has no mechanism to guarantee design, the convention-strong Django is sometimes safer in the end. "Freedom = good" isn't true.


7. Migration and coexistence: the three are continuous ground

Technology selection isn't a "one-time bet." The three are more continuous than you think, and there are realistic paths for migration and coexistence.

7.1 Parts can be shared

  • Flask ↔ FastAPI: marshmallow / Pydantic / SQLAlchemy are all framework-independent. You can swap only the web layer while sharing the persistence layer (SQLAlchemy models) and validation logic. For SQLAlchemy 2.0 design, see the SQLAlchemy 2.0 typed ORM practical guide (the same ORM is usable in both Flask and FastAPI).

7.2 Coexist behind a gateway

Behind an API Gateway or ALB, you can coexist a Flask app and a FastAPI app by routing on path. A staged migration of writing only new endpoints in FastAPI and leaving the existing as Flask is realistic. The lumber-distribution SaaS I've operated also had a configuration of bundling services with API Gateway → ALB → ECS. You can migrate per endpoint, without concentrating the risk on a bulk migration.

7.3 Flask's async escape route: Quart

When a team used to Flask's syntax needs async, migrating to Quart (an ASGI framework with a Flask-compatible API) is sometimes a smaller step than learning FastAPI from scratch. Keep it in a corner of your mind as the option of "leaning to async while staying Flask."

[ client ]
       │
   [ API Gateway / ALB ]
     ├── /api/legacy/*  → Flask app (existing, sync)
     └── /api/v2/*      → FastAPI app (new, async)

💡 No need to be intense about "I must make a perfect choice at the start." Keep the parts (ORM, validation) in a shareable form and the web layer loosely coupled, and you can migrate later. Technology selection can leave reversibility — this is the wisdom of ordering that also connects to the viewpoint of vendor selection and procurement (system-development outsourcing guide: vendor selection and cost).


8. For those who want to know FastAPI deeply

This article is a selection guide for "which to choose." The production operation after deciding to choose FastAPI — the use of async def / def, Pydantic v2 boundary validation, dependency injection with Depends, observability with structured logs and OpenTelemetry, and the limits of BackgroundTasks and how to escape to a job foundation — is systematized in a separate article. For FastAPI-specific design judgments, see the FastAPI production-operations guide. Similarly, the production design after choosing Flask starts from the Flask production-operations guide.


Conclusion: the recommendation matrix

Finally, let me condense the judgment into one sheet.

If…ChooseA word
High-concurrency, type-safe API is the main purposeFastAPIASGI and Pydantic are the main battlefield. But keep the 0.x version discipline
Admin screen, auth, ORM instantly mandatoryDjangoall-in. Shortest if you ride the conventions. API is DRF
Content/CRUD whole web appDjangostands up fast with MTV conventions and admin
Want to choose the configuration yourself, build thinlyFlaskcore only. Design responsibility in exchange for freedom
Start small and expand incrementallyFlaskgrow from minimal. Migration possible later
Have existing SQLAlchemy/Flask assetsFlaskminimal learning/migration cost
No structure to guarantee designDjangoconventions reduce accidents

The three aren't competitors but a division of territory. Flask is "maximum control and minimal core," FastAPI is "type-driven validation, async, and auto-docs," Django is "shortest arrival with conventions and comprehensiveness." Run your requirements through §5's three questions and the starting point is naturally decided. And as in §7, keep the parts loosely coupled and you can migrate later — so don't fall into perfectionism, and choose the one that minimally fits your current requirements. That's the shortcut to a backend that holds value for a long time.

友田

友田 陽大

Developer of a METI Minister's Award–winning product. With TypeScript + Python + AWS, I deliver SaaS, industry DX, and production-grade generative AI (RAG) end to end — from requirements to infrastructure and operations — single-handedly.

Got a challenge?

From design to implementation and operations — solo × generative AI

Implementation like this article's, end to end from requirements to production. Start with a free 30-minute technical consult and tell me about your situation.

Available for both project-based (contract) and advisory engagements. Start with a free 30-minute consult.

Also worth reading