Organizing with blueprints

Flask · Practical · 12 min

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_folder and static_folder
  • @bp.errorhandler, which applies to errors raised in its views
  • @bp.before_request, which runs only for its routes; @bp.before_app_request runs for all requests
  • @bp.app_template_filter and 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.

Check yourself

  1. A view index is registered on Blueprint("admin", __name__). How do you build its URL from another blueprint?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: flask.palletsprojects.com/en/stable/blueprints/