Skip to main content

Overview

Routing in Flask maps URL patterns to Python functions (view functions). Flask uses Werkzeug’s routing system to match incoming requests to the appropriate view.

Basic Routing

The @app.route() decorator is the primary way to register routes:
from flask import Flask

app = Flask(__name__)

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

@app.route('/about')
def about():
    return 'About Page'

Route Decorator

From scaffold.py:336-365, the route decorator is syntactic sugar for add_url_rule():
@app.route("/")
def index():
    return "Hello, World!"

# Equivalent to:
def index():
    return "Hello, World!"

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

HTTP Methods

Specifying Methods

By default, routes only respond to GET requests. Specify other methods:
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # Process login
        return redirect('/dashboard')
    return render_template('login.html')
HEAD and OPTIONS methods are automatically added. HEAD returns the same response as GET but without the body.

Method-Specific Shortcuts

Flask provides shortcuts for common HTTP methods (from scaffold.py:296-333):
@app.get('/users')
def list_users():
    return jsonify(users)

@app.post('/users')
def create_user():
    return jsonify(user), 201

@app.put('/users/<int:id>')
def update_user(id):
    return jsonify(user)

@app.delete('/users/<int:id>')
def delete_user(id):
    return '', 204

@app.patch('/users/<int:id>')
def partial_update_user(id):
    return jsonify(user)

Variable Rules

URL Parameters

Capture parts of the URL as parameters:
@app.route('/user/<username>')
def show_user(username):
    return f'User: {username}'

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'Post ID: {post_id}'

Converters

Flask supports several built-in converters:
ConverterDescriptionExample
stringDefault, accepts text without slashes/user/<username>
intAccepts integers/post/<int:id>
floatAccepts floating point numbers/price/<float:amount>
pathLike string but accepts slashes/file/<path:filename>
uuidAccepts UUID strings/api/<uuid:id>
@app.route('/files/<path:filepath>')
def serve_file(filepath):
    return send_file(filepath)

@app.route('/api/resource/<uuid:resource_id>')
def get_resource(resource_id):
    return jsonify(resource)

URL Building

Using url_for()

Generate URLs dynamically using endpoint names:
from flask import url_for

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

@app.route('/user/<username>')
def profile(username):
    return f'Profile: {username}'

with app.test_request_context():
    print(url_for('index'))  # /
    print(url_for('profile', username='john'))  # /user/john
    print(url_for('profile', username='jane', page=2))  # /user/jane?page=2

External URLs

Generate absolute URLs with scheme and domain:
url_for('index', _external=True)
# https://example.com/

url_for('profile', username='john', _external=True, _scheme='https')
# https://example.com/user/john

URL Parameters

From app.py:1102-1222, url_for() supports several special parameters:
url_for('index', _anchor='section')  # /#section
url_for('api.users', _method='POST')  # Build URL for specific method
url_for('static', filename='style.css')  # /static/style.css

Endpoint Names

Each route has an endpoint name, which defaults to the function name:
@app.route('/users')
def list_users():  # endpoint: 'list_users'
    return jsonify(users)

# Custom endpoint name
@app.route('/users', endpoint='user_list')
def show_all_users():
    return jsonify(users)

url_for('user_list')  # /users

URL Registration Methods

add_url_rule()

Register routes programmatically (from scaffold.py:368-433):
def index():
    return 'Hello, World!'

app.add_url_rule('/', 'index', index)

# With options
app.add_url_rule(
    '/api/users',
    'api.users',
    view_func=list_users,
    methods=['GET', 'POST']
)

Endpoint Decorator

Separate URL rule registration from view function:
app.add_url_rule('/admin', endpoint='admin')

@app.endpoint('admin')
def admin_panel():
    return 'Admin Panel'

Unique URLs and Trailing Slashes

Flask distinguishes between URLs with and without trailing slashes:
@app.route('/projects/')
def projects():
    return 'Projects'

# /projects/ works
# /projects redirects to /projects/ (308 redirect)

@app.route('/about')
def about():
    return 'About'

# /about works
# /about/ returns 404
Use trailing slashes for “folders” and no trailing slash for “files” to follow common conventions.

Request Dispatching

From app.py:966-990, Flask’s request dispatch process:
  1. URL Matching - Match request URL to a registered rule
  2. View Args - Extract variables from URL
  3. View Function - Call the matched view function
  4. Response - Convert return value to a Response object
def dispatch_request(self, ctx):
    req = ctx.request
    if req.routing_exception is not None:
        self.raise_routing_exception(req)
    
    rule = req.url_rule
    view_args = req.view_args
    return self.view_functions[rule.endpoint](**view_args)

Advanced Routing

Subdomain Routing

Route based on subdomains:
app.config['SERVER_NAME'] = 'example.com'

@app.route('/', subdomain='api')
def api_index():
    return 'API Index'

@app.route('/', subdomain='<user>')
def user_subdomain(user):
    return f'User subdomain: {user}'

Host Matching

Match routes based on the full host:
app.url_map.host_matching = True

@app.route('/', host='example.com')
def main_site():
    return 'Main Site'

@app.route('/', host='api.example.com')
def api_site():
    return 'API Site'

URL Value Preprocessors

Modify URL values before they reach the view:
@app.url_value_preprocessor
def pull_lang_code(endpoint, values):
    if values is not None:
        g.lang_code = values.pop('lang_code', None)

@app.route('/<lang_code>/page')
def show_page():
    # lang_code is in g.lang_code, not as a parameter
    return render_template('page.html')

URL Defaults

Provide default values for URL generation:
@app.url_defaults
def add_language_code(endpoint, values):
    if 'lang_code' in values or not g.lang_code:
        return
    if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
        values['lang_code'] = g.lang_code

Error Handling for Routes

404 Not Found

Handle missing routes:
@app.errorhandler(404)
def page_not_found(error):
    return render_template('404.html'), 404

405 Method Not Allowed

Handle wrong HTTP methods:
@app.errorhandler(405)
def method_not_allowed(error):
    return jsonify({'error': 'Method not allowed'}), 405

Best Practices

Use url_for()

Always use url_for() instead of hardcoding URLs for flexibility

Consistent Naming

Use descriptive endpoint names that reflect the resource

RESTful Routes

Follow REST conventions for API endpoints

Trailing Slashes

Be consistent with trailing slash usage across your app

Build docs developers (and LLMs) love