Skip to main content

Registering Routes and Handlers

In this tutorial, you will build a structured API for a Task Manager application. You will learn how to define URL rules, handle specific HTTP methods, manage errors gracefully, and organize your code using Blueprints.

Prerequisites​

To follow this tutorial, you need the vik-advani-flask-9fcd34c codebase installed in your environment. You should be familiar with basic Python decorators.

Step 1: Initialize the Application​

First, create the central application object. In this codebase, the Flask class (found in src/flask/app.py) inherits from App and Scaffold, providing the registry for all your routes.

from flask import Flask

app = Flask(__name__)

The import_name (here __name__) tells the Scaffold where the application is located, which is used to resolve resources like templates and static files relative to the root_path.

Step 2: Register Basic Routes​

Use the @app.route decorator to map a URL to a view function. By default, routes respond to GET requests.

@app.route("/")
def index():
return "Welcome to the Task API"

@app.route("/info")
def info():
return {"version": "1.0.0", "status": "active"}

The Scaffold.route method internally calls add_url_rule. When you return a dictionary from a view, the Flask.make_response method automatically converts it into a JSON response using the configured json_provider_class.

Step 3: Use Method Shortcuts​

For cleaner code, use the method-specific shortcuts provided by the Scaffold class, such as @app.get, @app.post, and @app.delete.

tasks = []

@app.get("/tasks")
def get_tasks():
return {"tasks": tasks}

@app.post("/tasks")
def add_task():
# In a real app, you would parse request data here
tasks.append("New Task")
return {"message": "Task added"}, 201

These shortcuts are wrappers around Scaffold.route that automatically set the methods argument (e.g., methods=["POST"]).

Step 4: Implement Request Hooks​

You can execute code before or after every request using lifecycle hooks. This is useful for logging, database connections, or authentication.

@app.before_request
def log_request():
app.logger.info(f"Handling request to {app.name}")

@app.after_request
def add_header(response):
response.headers["X-Content-Type-Options"] = "nosniff"
return response

Scaffold.before_request registers a function to run before the view. If it returns a value, that value is treated as the response, and the view function is never called. Scaffold.after_request functions receive and must return a response object.

Step 5: Handle Errors​

Use the @app.errorhandler decorator to define custom responses for HTTP errors or specific Python exceptions.

@app.errorhandler(404)
def not_found(error):
return {"error": "Resource not found"}, 404

@app.errorhandler(Exception)
def handle_unexpected_error(error):
app.logger.error(f"An error occurred: {error}")
return {"error": "Internal server error"}, 500

The Scaffold.register_error_handler method stores these in error_handler_spec. When an exception occurs during full_dispatch_request, the app searches this specification to find the most appropriate handler.

Step 6: Modularize with Blueprints​

As your application grows, use Blueprint to group related routes. You register them to the main app using App.register_blueprint.

from flask import Blueprint

# Define the blueprint (usually in a separate file)
admin_bp = Blueprint("admin", __name__, url_prefix="/admin")

@admin_bp.route("/stats")
def stats():
return {"users": 10, "uptime": "24h"}

# Register it to the main app
app.register_blueprint(admin_bp)

Blueprints also inherit from Scaffold, so they have their own route, before_request, and errorhandler methods. When registered, their routes are prefixed with the url_prefix.

Important: The Setup Constraint​

In this codebase, you must register all routes, handlers, and blueprints before the application starts handling requests.

# This will work
app.add_url_rule("/late-route", endpoint="late", view_func=lambda: "OK")

# If the app had already handled a request, this would raise an AssertionError
# via Scaffold._check_setup_finished

The App._check_setup_finished method ensures that the application configuration remains consistent once it begins processing traffic.

Complete Result​

Your final application structure combines these elements into a cohesive API:

from flask import Flask, Blueprint

app = Flask(__name__)
admin_bp = Blueprint("admin", __name__, url_prefix="/admin")

@app.before_request
def setup():
app.logger.debug("Initializing request")

@app.get("/")
def home():
return "Task Manager"

@admin_bp.get("/dashboard")
def dashboard():
return "Admin Dashboard"

@app.errorhandler(404)
def error_404(e):
return "Not Found", 404

app.register_blueprint(admin_bp)

if __name__ == "__main__":
app.run()

By following this pattern, you leverage the inheritance hierarchy of Scaffold -> App -> Flask to build a robust, maintainable application. For next steps, explore how to use url_for to generate dynamic links to these registered endpoints.