Skip to main content

Routing and View Function Registration

When you want to map a URL like /user/profile to a Python function so that Flask knows which code to execute when a request arrives, you use the routing system. If you forget to register a route or misconfigure the HTTP methods, Flask will return a 404 Not Found or 405 Method Not Allowed error.

The @app.route Decorator

The most common way to register a route is using the @app.route decorator. It associates a URL rule with a view function.

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
return "Hello, World!"

@app.route("/login", methods=["GET", "POST"])
def login():
return "Login Page"

Internally, the Scaffold.route method in flask.sansio.scaffold acts as a wrapper around add_url_rule. It extracts the endpoint (defaulting to the function name) and passes the rule and options down for registration.

Method-Specific Shortcuts

Flask provides shortcuts for common HTTP methods. These are often more readable than passing a methods list to @app.route.

@app.get("/api/data")
def get_data():
return {"data": "some value"}

@app.post("/api/data")
def post_data():
return "Data received", 201

These shortcuts (@app.get, @app.post, @app.put, @app.delete, @app.patch) are implemented in Scaffold by calling self.route with the specific method pre-filled:

# From flask.sansio.scaffold
@setupmethod
def get(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
return self._method_route("GET", rule, options)

Programmatic Registration with add_url_rule

In some cases, such as when building a CMS or dynamically generating routes from a database, decorators are not suitable. You can use app.add_url_rule directly.

def profile(username):
return f"User: {username}"

app.add_url_rule("/user/<username>", view_func=profile)

The App.add_url_rule method in flask.sansio.app is the core implementation. It performs several critical tasks:

  1. Endpoint Resolution: If no endpoint is provided, it uses view_func.__name__.
  2. Method Normalization: It ensures methods is a set of uppercase strings. If you pass a single string like "GET POST", Flask raises a TypeError.
  3. Rule Creation: It creates a werkzeug.routing.Rule object.
  4. Map Integration: It adds the rule to self.url_map, which Werkzeug uses for matching.
  5. View Function Mapping: It stores the function in self.view_functions[endpoint].

If you attempt to register a different function to an existing endpoint, Flask raises an AssertionError to prevent accidental overwrites.

Endpoints and View Functions

An endpoint is a unique identifier for a route, used primarily for URL generation via url_for. While the endpoint usually matches the function name, you can decouple them.

Using @app.endpoint

You can register a view function for an endpoint without immediately attaching a URL rule. This is useful when the same logic should handle multiple distinct URLs registered elsewhere.

@app.endpoint("user_profile")
def profile_logic(username):
return f"Profile for {username}"

app.add_url_rule("/user/<username>", endpoint="user_profile")
app.add_url_rule("/u/<username>", endpoint="user_profile")

The @app.endpoint decorator simply populates the self.view_functions dictionary in the Scaffold instance.

Automatic OPTIONS and HEAD Handling

Flask automatically handles OPTIONS and HEAD requests for your routes to comply with HTTP standards.

  • HEAD: If a route handles GET, Flask automatically adds HEAD. It calls your view function but discards the body, returning only the headers.
  • OPTIONS: Flask generates an Allow header containing the methods supported by the URL.

You can control this behavior using the provide_automatic_options parameter in add_url_rule or by setting a function attribute:

def custom_options():
return "Custom OPTIONS response"

custom_options.provide_automatic_options = False
app.add_url_rule("/special", view_func=custom_options, methods=["GET", "OPTIONS"])

Internally, App.dispatch_request checks rule.provide_automatic_options. If True and the request is OPTIONS, it calls make_default_options_response, which inspects the url_adapter to find all allowed methods for that path.

View Function Return Requirements

Every view function must return a value that Flask can convert into a Response object. If a function returns None (e.g., by forgetting a return statement), Flask raises a TypeError.

The Flask.make_response method in flask.app handles the conversion of the following types:

  • str / bytes: Becomes the response body.
  • dict / list: Automatically converted to a JSON response.
  • tuple: Can be (body, status), (body, headers), or (body, status, headers).
  • Response object: Returned as-is (or coerced to the app's response_class).
  • WSGI callable: Executed as a standard WSGI application.
@app.route("/api/status")
def status():
# Returning a tuple (body, status_code)
return {"status": "ok"}, 200

If make_response receives a type it doesn't recognize, it will raise a TypeError explaining that the return type must be a string, dict, list, tuple, Response instance, or WSGI callable.