A single app.py doesn't scale; blueprints are Flask's unit of modularity.
A blueprint is a set of routes and other registrations recorded for later, then attached to an app. It lets you split an application by feature: auth, blog, api.
bp = Blueprint("blog", __name__, url_prefix="/blog")
@bp.get("/")
def index(): ...Register it on the app with app.register_blueprint(bp). The url_prefix is prepended to every route, and endpoints are namespaced with the blueprint name: url_for("blog.index"). Inside the same blueprint, the short form url_for(".index") refers to its own endpoint.
A blueprint can have its own:
template_folderandstatic_folder@bp.errorhandler, which applies to errors raised in its views@bp.before_request, which runs only for its routes;@bp.before_app_requestruns for all requests@bp.app_template_filterand context processors
Blueprints can be registered more than once with different url_prefix or name values, and nested with parent.register_blueprint(child), useful for versioned APIs like /api/v1.
A common layout puts each blueprint in its own module in a package (myapp/auth.py, myapp/blog.py) and registers them all in the application factory. Blueprints never import the app object, which avoids circular imports: they use current_app when they need it.
Template folders from all blueprints share one search path, so namespace them: templates/blog/index.html rendered as "blog/index.html".
Example
from flask import Blueprint, Flask, url_for
blog = Blueprint("blog", __name__, url_prefix="/blog")
@blog.get("/")
def index():
return {"link_to_post": url_for(".post", slug="hello")}
@blog.get("/<slug>")
def post(slug):
return {"slug": slug}
api_v1 = Blueprint("v1", __name__, url_prefix="/api/v1")
@api_v1.get("/ping")
def ping():
return {"pong": True, "self": url_for("v1.ping")}
app = Flask(__name__)
app.register_blueprint(blog)
app.register_blueprint(api_v1)
c = app.test_client()
print(c.get("/blog/").json) # {'link_to_post': '/blog/hello'}
print(c.get("/api/v1/ping").json) # {'pong': True, 'self': '/api/v1/ping'}
print(sorted(r.endpoint for r in app.url_map.iter_rules()))
# ['blog.index', 'blog.post', 'static', 'v1.ping']Remember
- Blueprint(name, __name__, url_prefix=...) groups related routes
- Endpoints become name.view; url_for(".view") is relative to the blueprint
- Blueprints avoid importing the app, preventing circular imports
Watch out for
- Putting blueprint templates directly in templates/ so names collide across blueprints.