Oct 23, 20264 min read

Flask vs Quart

Quart reimplements the Flask API on asyncio. How the two compare on concurrency, WebSockets, extensions and migration effort, with the same endpoint written in each.

Quart is Flask's API rebuilt on asyncio, and it lives under the same Pallets organization. If you know Flask, the Quart documentation says you already know Quart. The comparison is mostly about the server model underneath, and whether your app has enough concurrent I/O to justify moving to it.

The main differences:

  • Flask is WSGI and synchronous, while Quart is ASGI and async throughout.
  • Flask async views start an event loop in a thread per request, while Quart runs every request on one event loop.
  • Quart supports WebSockets and streaming natively, while Flask needs extra libraries or a different server for WebSockets.
  • Flask has the larger extension ecosystem, and some extensions do not work under Quart.

Flask vs Quart at a glance

CapabilityEncoreFlaskQuart
RoleInfrastructure SDK added to either frameworkMicroframeworkMicroframework
Server interfaceFrom the frameworkWSGIASGI
ConcurrencyFrom the frameworkOne worker per requestMany requests per event loop
WebSocketsFrom the frameworkThrough extensionsBuilt in
Typical production serverFrom the frameworkGunicorn or another WSGI serverHypercorn or another ASGI server
ExtensionsFrom the frameworkLarge ecosystemSmaller, with Quart-specific ports

Flask: synchronous by design

The Flask async guide explains that async views run in a thread with their own event loop and that each request still ties up one worker. It also says Flask's async support is less performant than async-first frameworks and points to Quart for mainly async codebases. A typical Flask endpoint stays synchronous:

import os import psycopg from flask import Flask, request app = Flask(__name__) @app.post("/users") def create_user(): email = request.get_json()["email"] with psycopg.connect(os.environ["DATABASE_URL"]) as conn: conn.execute("INSERT INTO users (email) VALUES (%s)", (email,)) return {"email": email}, 201

This model is simple to reason about, and most Flask extensions assume it.

Quart: the same API on asyncio

Quart runs on asyncio coroutines and tasks, and its Flask migration guide describes the move as changing imports and adding async and await. The same endpoint with an asyncpg pool opened before serving:

import os import asyncpg from quart import Quart, request app = Quart(__name__) @app.before_serving async def open_pool() -> None: app.config["POOL"] = await asyncpg.create_pool(os.environ["DATABASE_URL"]) @app.after_serving async def close_pool() -> None: await app.config["POOL"].close() @app.post("/users") async def create_user(): body = await request.get_json() await app.config["POOL"].execute("INSERT INTO users (email) VALUES ($1)", body["email"]) return {"email": body["email"]}, 201

request.get_json() becomes awaitable, which is an easy change for an AI coding agent to miss when porting Flask code to Quart. Quart apps are usually served with Hypercorn, an ASGI server from the same author.

When the switch pays off

Quart helps when requests spend most of their time waiting on other services, or when the app needs WebSockets and server-sent events. For a CRUD app on a local database with modest traffic, Flask with a few more workers is often the simpler path.

Adding infrastructure to either framework with Encore

Encore combines the framework you chose with infrastructure declared in Python. The infrastructure SDK is the encore-dev package, and Flask or Quart keeps routing and request handling. Postgres databases, Pub/Sub topics, object storage buckets, cron jobs and secrets are module-level declarations that Encore discovers and provisions.

In Quart, encore.start() runs in before_serving, and routes await the declared database directly:

import encore from encore.storage import postgres from quart import Quart, request db = postgres.Database("users", migrations="./migrations") app = Quart(__name__) @app.before_serving async def start_encore() -> None: await encore.start() @app.post("/users") async def create_user(): body = await request.get_json() await db.exec("INSERT INTO users (email) VALUES ($1)", body["email"]) return {"email": body["email"]}, 201

In Flask, the same declaration works from synchronous views. Code that uses psycopg connects with db.connection_string, and async SDK calls such as bucket uploads go through asgiref.sync.async_to_sync.

encore run starts Postgres locally and applies the migrations, along with any topics and buckets, and the CLI reference covers encore test.

A deploy provisions the same declarations in your own AWS or GCP account, so a Flask app moving to Quart keeps its database and buckets.

Cloud access sits with Encore's IAM role, not the agent, so an agent porting routes from Flask to Quart changes code and declarations rather than cloud configuration. The AI integration guide covers how to give an agent Encore's conventions.

Python support is in beta, and you can request access.

Try Encore →

How to choose

  • Flask for synchronous apps and teams that rely on Flask extensions.
  • Quart for apps with heavy concurrent I/O or WebSockets that want to keep the Flask API.

Encore works with either when you want Postgres and buckets running locally and provisioned in your own cloud account.

Frequently asked questions

What is the difference between Flask and Quart?

Flask is a WSGI framework where each request holds a worker. Quart is an asyncio reimplementation of the Flask API on ASGI, so one process can serve many concurrent requests and WebSockets.

Should I use Flask async views or switch to Quart?

Flask async views run each request's event loop in a thread and still hold a worker per request. They suit a view that makes a few concurrent calls. If most of the codebase is async, the Flask documentation itself suggests considering Quart.

How hard is it to migrate from Flask to Quart?

Quart's migration guide describes two steps: change imports from flask to quart, then add async and await where Quart's API is asynchronous, such as route functions and request.get_json(). Extensions that depend on Flask internals may need Quart-specific versions.

How do I add Postgres to a Quart app?

Install the encore-dev package, declare postgres.Database at module scope and call await encore.start() in a before_serving hook. Routes then call db.exec and db.query directly. encore run starts Postgres locally, and a deploy provisions it in your own AWS or GCP account.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders