Skip to main content

Defining Routes and Endpoints

In this codebase, routing is the mechanism that maps incoming URL patterns to specific Python functions, known as view functions. This system is built upon the Scaffold base class and leverages Werkzeug's routing primitives to manage URL matching and endpoint dispatching.

The Routing Foundation

The core routing interface is defined in the Scaffold class (found in src/flask/sansio/scaffold.py). Both the main application class (Flask) and Blueprint inherit from Scaffold, ensuring a consistent API for defining routes across the entire project.

The Scaffold class maintains a central registry of view functions:

# src/flask/sansio/scaffold.py

class Scaffold:
def __init__(self, ...):
# ...
#: A dictionary mapping endpoint names to view functions.
self.view_functions: dict[str, ft.RouteCallable] = {}

When you define a route, you are essentially creating a mapping between a URL rule and an endpoint, which then points to a view function in this dictionary.

Defining Routes with Decorators

The most common way to define routes is using decorators provided by the Scaffold class.

The @route Decorator

The @route decorator is the primary tool for mapping a URL rule to a function. It internally calls add_url_rule.

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

By default, @route handles GET requests. You can specify other methods using the methods parameter:

@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
return do_the_login()
else:
return show_the_login_form()

Method-Specific Shortcuts

For cleaner code, Scaffold provides shortcuts for standard HTTP methods. These were introduced to simplify common routing patterns:

  • @app.get(rule)
  • @app.post(rule)
  • @app.put(rule)
  • @app.delete(rule)
  • @app.patch(rule)

These shortcuts are implemented as wrappers around the route method:

# src/flask/sansio/scaffold.py

@setupmethod
def get(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
"""Shortcut for :meth:`route` with ``methods=["GET"]``."""
return self._method_route("GET", rule, options)

Programmatic Registration

While decorators are convenient, you can also register routes programmatically using the add_url_rule method. This is useful when the view function is defined elsewhere or when routes are generated dynamically.

def index():
return "Hello World!"

app.add_url_rule("/", view_func=index)

How add_url_rule Works

In the App class (found in src/flask/sansio/app.py), add_url_rule performs several critical tasks:

  1. Endpoint Resolution: If no endpoint name is provided, it defaults to the name of the view function using _endpoint_from_view_func.
  2. Method Normalization: It ensures methods are uppercase and includes required methods (like OPTIONS if automatic options are enabled).
  3. Rule Creation: It creates a Werkzeug Rule object.
  4. Map Integration: It adds the Rule to the application's url_map.
  5. View Function Mapping: It stores the view function in the view_functions dictionary.
# src/flask/sansio/app.py

def add_url_rule(
self,
rule: str,
endpoint: str | None = None,
view_func: ft.RouteCallable | None = None,
provide_automatic_options: bool | None = None,
**options: t.Any,
) -> None:
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)

# ... method normalization logic ...

rule_obj = self.url_rule_class(rule, methods=methods, **options)
self.url_map.add(rule_obj)

if view_func is not None:
# Ensure we don't overwrite an existing endpoint with a different function
old_func = self.view_functions.get(endpoint)
if old_func is not None and old_func != view_func:
raise AssertionError(
f"View function mapping is overwriting an existing endpoint function: {endpoint}"
)
self.view_functions[endpoint] = view_func

Endpoints and the @endpoint Decorator

An endpoint is a unique identifier for a route. While it defaults to the function name, you can explicitly name it. This is particularly important for url_for to generate URLs correctly.

If you add a rule without a view_func, you must later associate a function with that endpoint using the @endpoint decorator:

app.add_url_rule("/example", endpoint="example_endpoint")

@app.endpoint("example_endpoint")
def example():
return "This is the example view"

Routing Constraints and Safety

The routing system enforces strict rules to ensure application stability:

  • Setup Locking: Once the application handles its first request, you can no longer call setup methods like route or add_url_rule. Attempting to do so will raise an AssertionError via the _check_setup_finished check.
  • Unique Endpoints: Each endpoint must map to exactly one view function. If you try to register a different function for an existing endpoint, the system raises an AssertionError.
  • Method Restrictions: Shortcut decorators like @get do not accept a methods argument, as the method is already implied by the decorator name.

Blueprint Routing

Blueprints also use the Scaffold interface, but they do not immediately update the application's url_map. Instead, they record the add_url_rule calls. When the blueprint is registered on an application via app.register_blueprint(bp), these recorded operations are played back, applying the routes to the application's central url_map with any configured prefixes or subdomains.