# Coming from Flask URL: /docs/tutorials/coming-from-flask/ Section: tutorials Tags: tutorial, flask, migration -------------------------------------------------------------------------------- Overview If you know Flask, you already know 80% of Chirp. This guide maps Flask concepts to their Chirp equivalents. App Setup Flask Chirp from flask import Flask app = Flask(__name__) app.config["SECRET_KEY"] = "secret" from chirp import App, AppConfig config = AppConfig(secret_key="secret") app = App(config=config) Chirp uses a frozen dataclass instead of a dict. No app.config["SCRET_KEY"] typos. Routes Flask Chirp @app.route("/users/<int:id>") def user(id): return render_template("user.html", user=get_user(id)) @app.route("/users/{id:int}") def user(id: int): return Template("user.html", user=get_user(id)) Differences: Path parameters use {name:type} instead of <type:name> Return Template(...) instead of calling render_template() Type annotations on handler parameters Request Access Flask Chirp from flask import request @app.route("/search") def search(): q = request.args.get("q", "") return render_template("search.html", q=q) from chirp import Request @app.route("/search") def search(request: Request): q = request.query.get("q", "") return Template("search.html", q=q) Differences: request is a parameter, not a global import request.query instead of request.args The request is frozen (immutable) JSON Responses Flask Chirp from flask import jsonify @app.route("/api/users") def api_users(): return jsonify({"users": get_all_users()}) @app.route("/api/users") def api_users(): return {"users": get_all_users()} No jsonify() needed. Return a dict and Chirp serializes it as JSON. Error Handling Flask Chirp @app.errorhandler(404) def not_found(error): return render_template("404.html"), 404 @app.error(404) def not_found(request: Request): return Template("404.html", path=request.path) Chirp's error handlers use the same return-value system. No tuple return for status codes. Template Filters Flask Chirp @app.template_filter() def currency(value): return f"${value:,.2f}" @app.template_filter() def currency(value: float) -> str: return f"${value:,.2f}" Identical decorator pattern. Chirp adds type annotations. Middleware Flask Chirp # Flask uses WSGI middleware or before/after_request @app.before_request def before(): g.start = time.monotonic() @app.after_request def after(response): elapsed = time.monotonic() - g.start response.headers["X-Time"] = f"{elapsed:.3f}" return response async def timing(request: Request, next: Next) -> Response: start = time.monotonic() response = await next(request) return response.with_header("X-Time", f"{time.monotonic() - start:.3f}") app.add_middleware(timing) Chirp uses a single middleware function instead of separate before/after hooks. Sessions and Auth Flask Chirp from flask import session, redirect from flask_login import login_user, login_required @app.route("/login", methods=["POST"]) def do_login(): session["user_id"] = user.id login_user(user) return redirect("/dashboard") @app.route("/dashboard") @login_required def dashboard(): return render_template("dashboard.html") from chirp import login, logout, login_required, get_user, is_safe_url, Redirect, Template @app.route("/login", methods=["POST"]) async def do_login(request: Request): form = await request.form() user = await verify_credentials(form["username"], form["password"]) if user: login(user) # regenerates session automatically next_url = request.query.get("next", "/dashboard") if not is_safe_url(next_url): next_url = "/dashboard" return Redirect(next_url) return Template("login.html", error="Invalid credentials") @app.route("/dashboard") @login_required def dashboard(): user = get_user() return Template("dashboard.html", user=user) Chirp has built-in login() / logout() helpers and @login_required — no Flask-Login equivalent needed. Both login() and logout() regenerate the session to prevent session fixation attacks. Use is_safe_url() to validate ?next= redirects (prevents open redirects). Requires SessionMiddleware + AuthMiddleware. See Built-in Middleware for setup. What Chirp Adds Beyond Flask equivalents, Chirp offers: Fragment rendering -- Fragment("page.html", "block_name") renders a named block Streaming HTML -- Stream("page.html") for progressive rendering Server-Sent Events -- EventStream(generator()) for real-time updates Typed contracts -- app.check() validates htmx references at startup Free-threading -- Designed for Python 3.14t from day one Quick Reference Flask Chirp Flask(__name__) App() app.config["KEY"] AppConfig(key=...) render_template() Template(...) jsonify() Return a dict request.args request.query request (global) request (parameter) redirect() Redirect(...) session["key"] get_session()["key"] login_user(user) login(user) (regenerates session) @login_required @login_required N/A is_safe_url(url) @app.errorhandler @app.error <int:id> {id:int} N/A Fragment(...) N/A Stream(...) N/A EventStream(...) Next Steps htmx Patterns -- Common htmx + Chirp workflows Quickstart -- Build your first app Fragments -- The key differentiator -------------------------------------------------------------------------------- Metadata: - Word Count: 536 - Reading Time: 3 minutes