Routing

Routing binds URLs to view functions, determining which code handles a request to a given path. Flask declares routes with decorators, and they are the first building block of any application.

Basic Usage

@app.route("/")
def index():
    return "Index"

@app.get("/hello")        # Equivalent to @app.route("/hello", methods=["GET"])
def hello():
    return "Hello"

@app.post("/submit")      # POST only
def submit():
    return "Submitted"

@app.route("/items", methods=["GET", "POST"])  # One view handling multiple methods
def items():
    ...

@app.get / @app.post / @app.put / @app.delete / @app.patch are shortcut decorators added in Flask 2.0. Requests using an undeclared method get a 405 Method Not Allowed.

URL Parameters and Converters

Capture path segments with <variable> and optionally specify a type converter:

@app.get("/user/<username>")
def profile(username):
    return f"User: {username}"

@app.get("/post/<int:post_id>")
def post_detail(post_id):
    return f"Post #{post_id}"          # post_id is already an int

@app.get("/price/<float:amount>")
def price(amount):
    return f"{amount:.2f}"

@app.get("/files/<path:subpath>")      # path allows slashes
def files(subpath):
    return subpath

Built-in converters: string (default, no slashes), int, float, path (allows slashes), uuid. A type mismatch returns 404 automatically — no manual validation needed.

Building URLs: url_for

Never hardcode URLs in code or templates — generate them from endpoint names with url_for():

from flask import url_for

url_for("index")                      # "/"
url_for("post_detail", post_id=123)   # "/post/123"
url_for("profile", username="alice", tab="repos")  # Extra args become the query string: /user/alice?tab=repos
url_for("static", filename="css/style.css")        # Static files

The endpoint name defaults to the view function name; blueprint endpoints are prefixed with the blueprint name, e.g. url_for("blog.list_posts").

Redirects and abort

from flask import redirect, url_for, abort

@app.get("/old")
def old():
    return redirect(url_for("index"), code=301)   # Default is 302

@app.get("/admin")
def admin():
    if not current_user_is_admin():
        abort(403)                                 # Stop immediately with an error page
    ...

Trailing Slash Behavior

  • A route defined as /projects/ (with trailing slash): visiting /projects triggers a 308 redirect to /projects/.
  • A route defined as /about (no trailing slash): visiting /about/ returns 404.

Keeping a consistent style across the team avoids extra redirect round trips.

Custom Converters

For regex or special matching, register a custom converter:

from werkzeug.routing import BaseConverter

class SlugConverter(BaseConverter):
    regex = r"[a-z0-9]+(?:-[a-z0-9]+)*"

app.url_map.converters["slug"] = SlugConverter

@app.get("/article/<slug:slug>")
def article(slug):
    return slug

Things to Watch For

  • Routes are matched by registration order and specificity; avoid defining overlapping, ambiguous paths.
  • View function names (endpoint names) must be unique — duplicates raise an error at startup.
  • The flask routes command lists every route in the application, which is handy for debugging.