Flask: Subdomain Routing
Flask’s routing system supports subdomain routing, enabling developers to map specific subdomains to distinct parts of an application. This is particularly useful for multi-tenant applications, regional endpoints, or separating features like APIs and dashboards. This guide explores Flask subdomain routing, covering key techniques, best practices, and practical applications for building flexible, scalable web applications.
01. Why Use Subdomain Routing in Flask?
Subdomain routing allows Flask applications to handle requests based on the subdomain (e.g., api.example.com
vs. dashboard.example.com
), improving modularity and user experience. It’s ideal for segregating functionality, supporting multi-tenancy, or providing localized services. Combined with Flask’s Blueprints and NumPy Array Operations for data-heavy routes, subdomain routing enhances the organization and scalability of complex applications.
Example: Basic Subdomain Routing
# app.py
from flask import Flask
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com:5000'
@app.route('/', subdomain='api')
def api_index():
return {'message': 'Welcome to the API'}
@app.route('/', subdomain='www')
def www_index():
return {'message': 'Welcome to the main site'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Output (curl http://api.example.com:5000/):
{
"message": "Welcome to the API"
}
Explanation:
SERVER_NAME
- Enables subdomain routing by setting the base domain.subdomain
- Maps routes to specific subdomains.- Note: For local testing, modify
/etc/hosts
to map subdomains (e.g.,127.0.0.1 api.example.com
).
02. Key Subdomain Routing Techniques
Subdomain routing in Flask leverages the subdomain
parameter in routes and Blueprints, supporting static and dynamic subdomains. The table below summarizes key techniques and their applications:
Technique | Description | Use Case |
---|---|---|
Static Subdomains | Map routes to fixed subdomains | API, dashboard, admin interfaces |
Dynamic Subdomains | Handle variable subdomains | Multi-tenant apps, user-specific domains |
Blueprint Subdomains | Apply subdomains to Blueprint routes | Modular feature separation |
URL Generation | Generate URLs with subdomains | Dynamic links, redirects |
Error Handling | Handle invalid subdomains | Custom 404s, fallback routes |
2.1 Static Subdomains
Example: API and Admin Subdomains
# app.py
from flask import Flask
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com:5000'
@app.route('/health', subdomain='api')
def api_health():
return {'status': 'API healthy'}
@app.route('/dashboard', subdomain='admin')
def admin_dashboard():
return {'message': 'Admin dashboard'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Output (curl http://api.example.com:5000/health):
{
"status": "API healthy"
}
Explanation:
- Static subdomains (
api
,admin
) segregate functionality. - Clear separation of API and admin routes.
2.2 Dynamic Subdomains
Example: Multi-Tenant Subdomains
# app.py
from flask import Flask, request
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com:5000'
@app.route('/profile', subdomain='<tenant>')
def tenant_profile(tenant):
return {'tenant': tenant, 'profile': f'{tenant} profile'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Output (curl http://acme.example.com:5000/profile):
{
"tenant": "acme",
"profile": "acme profile"
}
Explanation:
<tenant>
- Captures dynamic subdomain as a route parameter.- Ideal for multi-tenant applications (e.g.,
acme.example.com
).
2.3 Blueprint Subdomains
Example: Subdomain in Blueprint
# myapp/__init__.py
from flask import Flask
from myapp.api import api_bp
def create_app():
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com:5000'
app.register_blueprint(api_bp, subdomain='api')
return app
# myapp/api.py
from flask import Blueprint, jsonify
api_bp = Blueprint('api', __name__)
@api_bp.route('/users')
def get_users():
return jsonify({'users': ['Alice', 'Bob']})
Output (curl http://api.example.com:5000/users):
{
"users": ["Alice", "Bob"]
}
Explanation:
- Blueprint routes inherit the
api
subdomain. - Modularizes API functionality under a subdomain.
2.4 URL Generation
Example: Generating Subdomain URLs
# app.py
from flask import Flask, url_for
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com:5000'
@app.route('/link', subdomain='www')
def www_link():
api_url = url_for('api_data', _external=True, subdomain='api')
return {'api_url': api_url}
@app.route('/data', subdomain='api')
def api_data():
return {'message': 'API data'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Output (curl http://www.example.com:5000/link):
{
"api_url": "http://api.example.com:5000/data"
}
Explanation:
url_for
- Generates URLs with the correct subdomain._external=True
- Includes the full domain.
2.5 Error Handling
Example: Invalid Subdomain Handling
# app.py
from flask import Flask, jsonify
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com:5000'
@app.errorhandler(404)
def not_found(error):
if not request.host.startswith(app.config['SERVER_NAME'].split(':')[0]):
return jsonify({'error': 'Invalid subdomain'}), 404
return jsonify({'error': 'Not found'}), 404
@app.route('/', subdomain='app')
def app_index():
return {'message': 'App home'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Output (curl http://invalid.example.com:5000/):
{
"error": "Invalid subdomain"
}
Explanation:
- Custom 404 handler detects invalid subdomains.
- Improves user experience with clear error messages.
2.6 Incorrect Subdomain Usage
Example: Missing SERVER_NAME
# app.py (Incorrect)
from flask import Flask
app = Flask(__name__)
# Missing SERVER_NAME configuration
@app.route('/', subdomain='api')
def api_index():
return {'message': 'API home'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Output (curl http://api.example.com:5000/):
RuntimeError: No SERVER_NAME configured
Explanation:
- Subdomain routing requires
SERVER_NAME
. - Solution: Set
app.config['SERVER_NAME']
.
03. Effective Usage
3.1 Recommended Practices
- Use Blueprints with subdomains for modular routing.
Example: Comprehensive Subdomain Setup
# myapp/__init__.py
from flask import Flask, jsonify
from myapp.api import api_bp
from myapp.dashboard import dashboard_bp
def create_app():
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com:5000'
app.register_blueprint(api_bp, subdomain='api')
app.register_blueprint(dashboard_bp, subdomain='dashboard')
@app.errorhandler(404)
def not_found(error):
return jsonify({'error': 'Invalid subdomain or resource'}), 404
return app
# myapp/api.py
from flask import Blueprint, jsonify
api_bp = Blueprint('api', __name__)
@api_bp.route('/health')
def health():
return jsonify({'status': 'API healthy'})
# myapp/dashboard.py
from flask import Blueprint, jsonify, request
dashboard_bp = Blueprint('dashboard', __name__)
@dashboard_bp.before_request
def check_auth():
if not request.headers.get('Authorization'):
return jsonify({'error': 'Unauthorized'}), 401
@dashboard_bp.route('/stats')
def stats():
return jsonify({'stats': 'System metrics'})
Output (curl http://api.example.com:5000/health):
{
"status": "API healthy"
}
- Blueprints segregate API and dashboard under distinct subdomains.
- Middleware enforces subdomain-specific logic.
- Custom 404 handler improves error clarity.
3.2 Practices to Avoid
- Avoid conflicting subdomains in Blueprints.
Example: Conflicting Subdomain Routes
# app.py (Incorrect)
from flask import Flask
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com:5000'
@app.route('/data', subdomain='app')
def app_data():
return {'data': 'App data'}
@app.route('/data', subdomain='app')
def app_data_conflict():
return {'data': 'Conflicting data'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Output (app start):
AssertionError: View function mapping is overwriting an existing endpoint
- Duplicate routes for the same subdomain cause conflicts.
- Solution: Ensure unique endpoint names or use Blueprints.
04. Common Use Cases
4.1 Multi-Tenant Applications
Route tenant-specific subdomains to customized resources.
Example: Tenant-Based Routing
# app.py
from flask import Flask, request
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com:5000'
@app.route('/dashboard', subdomain='<tenant>')
def tenant_dashboard(tenant):
return {'tenant': tenant, 'message': f'{tenant} dashboard'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Output (curl http://client1.example.com:5000/dashboard):
{
"tenant": "client1",
"message": "client1 dashboard"
}
Explanation:
- Dynamic subdomains support tenant-specific dashboards.
- Scalable for SaaS applications.
4.2 API and Web Separation
Segregate API and web interfaces via subdomains.
Example: API/Web Subdomains
# myapp/__init__.py
from flask import Flask
from myapp.api import api_bp
from myapp.web import web_bp
def create_app():
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com:5000'
app.register_blueprint(api_bp, subdomain='api')
app.register_blueprint(web_bp, subdomain='www')
return app
# myapp/api.py
from flask import Blueprint, jsonify
api_bp = Blueprint('api', __name__)
@api_bp.route('/products')
def products():
return jsonify({'products': ['Product A', 'Product B']})
# myapp/web.py
from flask import Blueprint, render_template
web_bp = Blueprint('web', __name__, template_folder='templates/web')
@web_bp.route('/')
def index():
return render_template('index.html')
# myapp/templates/web/index.html
<!DOCTYPE html>
<html>
<head><title>Home</title></head>
<body><h1>Welcome to the Web</h1></body>
</html>
Output (curl http://api.example.com:5000/products):
{
"products": ["Product A", "Product B"]
}
Explanation:
- API and web interfaces are isolated via subdomains.
- Blueprints enhance modularity with subdomain-specific templates.
Conclusion
Flask subdomain routing enables flexible, modular applications by mapping subdomains to specific routes or Blueprints. Key takeaways:
- Use static subdomains for feature separation (e.g., API, admin).
- Leverage dynamic subdomains for multi-tenant apps.
- Integrate with Blueprints for modular routing.
- Support URL generation and error handling for robustness.
- Avoid missing
SERVER_NAME
or conflicting routes.
With subdomain routing, Flask applications become scalable, organized, and user-friendly!
Comments
Post a Comment