Quart is an async Flask-compatible web framework. pip install quart. Basic: from quart import Quart; app = Quart(__name__); @app.route("/"); async def index(): return "Hello". Run: app.run() or hypercorn app:app. JSON: from quart import jsonify; return jsonify({"ok": True}). Request: from quart import request; data = await request.get_json(). Form: await request.form. Files: await request.files. Blueprint: from quart import Blueprint; bp = Blueprint("api", __name__). app.register_blueprint(bp, url_prefix="/api"). WebSocket: @app.websocket("/ws"); async def ws(): while True: msg = await websocket.receive(); await websocket.send(f"Echo: {msg}"). Before: @app.before_request async def check_auth(): .... After: @app.after_request async def add_cors(response): .... g: from quart import g; g.user = .... current_app. Template: from quart import render_template; return await render_template("index.html", name=name). Error handler: @app.errorhandler(404) async def not_found(e): return jsonify({"error": "not found"}), 404. Testing: async with app.test_client() as c: resp = await c.get("/"). Config: app.config["DEBUG"] = True. Hypercorn: hypercorn app:app --bind 0.0.0.0:8000. --workers 4. Claude Code generates Quart REST APIs, WebSocket servers, Blueprint modules, and async middleware.
CLAUDE.md for Quart
## Quart Stack
- Version: quart >= 0.19 | pip install quart hypercorn
- App: app = Quart(__name__) | async def view() with await
- Request: await request.get_json() | await request.form | await request.files
- WebSocket: @app.websocket("/ws") | await websocket.receive() / .send()
- Blueprint: bp = Blueprint("name", __name__); app.register_blueprint(bp)
- Deploy: hypercorn app:app --bind 0.0.0.0:8000 --workers 4
Quart Async Web Pipeline
# app/quart_app.py — Quart REST API, WebSocket, Blueprint, middleware, and testing
from __future__ import annotations
import asyncio
import json
import time
from functools import wraps
from typing import Any
from quart import (
Blueprint,
Quart,
Response,
current_app,
g,
jsonify,
make_response,
render_template_string,
request,
websocket,
)
# ─────────────────────────────────────────────────────────────────────────────
# 1. Application factory
# ─────────────────────────────────────────────────────────────────────────────
def create_app(config: dict | None = None) -> Quart:
"""
Application factory pattern.
config: dict of config overrides.
Usage:
app = create_app({"DEBUG": True})
app.run()
"""
app = Quart(__name__)
# Defaults
app.config.update({
"DEBUG": False,
"JSON_SORT_KEYS": False,
})
if config:
app.config.update(config)
# Register blueprints
app.register_blueprint(api_bp, url_prefix="/api")
app.register_blueprint(ws_bp, url_prefix="/ws")
# Middleware
@app.before_request
async def time_start():
g.start = time.monotonic()
@app.after_request
async def add_timing(response: Response) -> Response:
elapsed = time.monotonic() - g.start
response.headers["X-Processing-Time"] = f"{elapsed * 1000:.1f}ms"
response.headers["Access-Control-Allow-Origin"] = "*"
return response
@app.errorhandler(404)
async def not_found(e):
return jsonify({"error": "not found", "status": 404}), 404
@app.errorhandler(422)
async def unprocessable(e):
return jsonify({"error": str(e), "status": 422}), 422
return app
# ─────────────────────────────────────────────────────────────────────────────
# 2. Auth decorator
# ─────────────────────────────────────────────────────────────────────────────
VALID_TOKENS = {"secret-token-123", "dev-token"}
def require_auth(fn):
"""Simple bearer token auth decorator for Quart routes."""
@wraps(fn)
async def wrapper(*args, **kwargs):
auth = request.headers.get("Authorization", "")
if not auth.startswith("Bearer "):
return jsonify({"error": "missing token"}), 401
token = auth[len("Bearer "):]
if token not in VALID_TOKENS:
return jsonify({"error": "invalid token"}), 403
return await fn(*args, **kwargs)
return wrapper
# ─────────────────────────────────────────────────────────────────────────────
# 3. REST API blueprint
# ─────────────────────────────────────────────────────────────────────────────
api_bp = Blueprint("api", __name__)
# In-memory "database"
_items: dict[int, dict] = {}
_next_id = 1
def _next() -> int:
global _next_id
_next_id += 1
return _next_id - 1
@api_bp.get("/items")
async def list_items():
"""GET /api/items — list all items."""
limit = int(request.args.get("limit", 20))
offset = int(request.args.get("offset", 0))
items = list(_items.values())[offset: offset + limit]
return jsonify({"items": items, "total": len(_items)})
@api_bp.post("/items")
async def create_item():
"""POST /api/items — create an item."""
data = await request.get_json()
if not data or "name" not in data:
return jsonify({"error": "name required"}), 422
item = {"id": _next(), "name": data["name"],
"tags": data.get("tags", []), "active": True}
_items[item["id"]] = item
return jsonify(item), 201
@api_bp.get("/items/<int:item_id>")
async def get_item(item_id: int):
"""GET /api/items/:id"""
item = _items.get(item_id)
if not item:
return jsonify({"error": "not found"}), 404
return jsonify(item)
@api_bp.put("/items/<int:item_id>")
async def update_item(item_id: int):
"""PUT /api/items/:id"""
item = _items.get(item_id)
if not item:
return jsonify({"error": "not found"}), 404
data = await request.get_json() or {}
item.update({k: v for k, v in data.items() if k != "id"})
return jsonify(item)
@api_bp.delete("/items/<int:item_id>")
async def delete_item(item_id: int):
"""DELETE /api/items/:id"""
if item_id not in _items:
return jsonify({"error": "not found"}), 404
del _items[item_id]
return jsonify({"deleted": item_id})
@api_bp.get("/protected")
@require_auth
async def protected():
"""Protected endpoint — needs Authorization: Bearer <token>"""
return jsonify({"message": "secret data", "user": "authenticated"})
# ─────────────────────────────────────────────────────────────────────────────
# 4. WebSocket blueprint
# ─────────────────────────────────────────────────────────────────────────────
ws_bp = Blueprint("ws", __name__)
_ws_clients: set = set()
@ws_bp.websocket("/echo")
async def ws_echo():
"""Bidirectional echo WebSocket."""
while True:
msg = await websocket.receive()
await websocket.send(f"Echo: {msg}")
@ws_bp.websocket("/chat")
async def ws_chat():
"""Broadcast chat: every message sent to all connected clients."""
global _ws_clients
_ws_clients.add(websocket._get_current_object())
try:
while True:
msg = await websocket.receive()
data = json.loads(msg) if msg.startswith("{") else {"text": msg}
payload = json.dumps({"user": data.get("user", "anon"),
"text": data.get("text", msg)})
dead = set()
for client in list(_ws_clients):
try:
await client.send(payload)
except Exception:
dead.add(client)
_ws_clients -= dead
finally:
_ws_clients.discard(websocket._get_current_object())
# ─────────────────────────────────────────────────────────────────────────────
# 5. Health / HTML endpoints on main app
# ─────────────────────────────────────────────────────────────────────────────
_HTML = """<!doctype html>
<html><head><title>Quart Demo</title></head>
<body>
<h1>Quart Async API</h1>
<p>Endpoints: <code>GET /api/items</code>, <code>POST /api/items</code></p>
<p>WebSocket: <code>ws://localhost:5000/ws/echo</code></p>
</body></html>"""
def register_main_routes(app: Quart) -> None:
@app.get("/")
async def index():
return await render_template_string(_HTML)
@app.get("/health")
async def health():
return jsonify({"status": "ok", "version": "1.0.0"})
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
app = create_app({"DEBUG": True})
register_main_routes(app)
async def _demo():
async with app.test_client() as client:
print("=== GET /health ===")
resp = await client.get("/health")
print(f" {resp.status_code}: {await resp.get_json()}")
print("\n=== POST /api/items ===")
resp = await client.post("/api/items",
json={"name": "Widget", "tags": ["new"]})
print(f" {resp.status_code}: {await resp.get_json()}")
resp2 = await client.post("/api/items", json={"name": "Gadget"})
item = await resp2.get_json()
print(f" {resp2.status_code}: {item}")
print("\n=== GET /api/items ===")
resp = await client.get("/api/items")
data = await resp.get_json()
print(f" {resp.status_code}: total={data['total']}, items={[i['name'] for i in data['items']]}")
print("\n=== GET /api/items/:id ===")
resp = await client.get(f"/api/items/{item['id']}")
print(f" {resp.status_code}: {await resp.get_json()}")
print("\n=== DELETE /api/items/:id ===")
resp = await client.delete(f"/api/items/{item['id']}")
print(f" {resp.status_code}: {await resp.get_json()}")
print("\n=== Protected (no auth) ===")
resp = await client.get("/api/protected")
print(f" {resp.status_code}: {await resp.get_json()}")
print("\n=== Protected (with auth) ===")
resp = await client.get("/api/protected",
headers={"Authorization": "Bearer secret-token-123"})
print(f" {resp.status_code}: {await resp.get_json()}")
if __name__ == "__main__":
asyncio.run(_demo())
print("\nTo run the server:")
print(" hypercorn quart_app:app --bind 0.0.0.0:5000")
For the Flask alternative — Flask uses synchronous view functions and a WSGI server model; Quart is API-compatible with Flask (many Flask extensions work unchanged) but runs on ASGI with asyncio, enabling await db.fetch(), await httpx.get(), and WebSocket endpoints in the same codebase — migrate Flask to Quart by adding async def to views and await to I/O calls. For the FastAPI alternative — FastAPI has Pydantic-based request validation, automatic OpenAPI documentation, and a large ecosystem; Quart is closer to Flask’s minimal style with less magic — use Quart when you want Flask’s simplicity in an async context, FastAPI when you need auto-generated docs and type-validated request bodies. The Claude Skills 360 bundle includes Quart skill sets covering create_app() application factory, before_request/after_request timing middleware, require_auth() bearer token decorator, Blueprint CRUD (list/create/get/update/delete), ws_echo()/ws_chat() WebSocket endpoints, test_client() async testing, health endpoint, error handlers, and Hypercorn deployment. Start with the free tier to try async Flask-style web framework code generation.