# Blueprints

# Overview

Blueprints are objects that can be used for sub-routing within an application. Instead of adding routes to the application instance, blueprints define similar methods for adding routes, which are then registered with the application in a flexible and pluggable manner.

Blueprints are especially useful for larger applications, where your application logic can be broken down into several groups or areas of responsibility.

# Creating and registering

First, you must create a blueprint. It has a very similar API as the Sanic() app instance with many of the same decorators.

# ./my_blueprint.py
from sanic.response import json
from sanic import Blueprint
bp = Blueprint("my_blueprint")
async def bp_root(request):
    return json({"my": "blueprint"})

Next, you register it with the app instance.

from sanic import Sanic
from my_blueprint import bp
app = Sanic(__name__)

Blueprints also have the same websocket() decorator and add_websocket_route method for implementing websockets.

# Blueprint groups

Blueprints may also be registered as part of a list or tuple, where the registrar will recursively cycle through any sub-sequences of blueprints and register them accordingly. The Blueprint.group method is provided to simplify this process, allowing a ‘mock’ backend directory structure mimicking what’s seen from the front end. Consider this (quite contrived) example:

│ ├──authors.py
│ ├──static.py
│ └──__init__.py

# First blueprint

# api/content/authors.py
from sanic import Blueprint
authors = Blueprint("content_authors", url_prefix="/authors")

# Second blueprint

# api/content/static.py
from sanic import Blueprint
static = Blueprint("content_static", url_prefix="/static")

# Blueprint group

# api/content/__init__.py
from sanic import Blueprint
from .static import static
from .authors import authors
content = Blueprint.group(static, authors, url_prefix="/content")

# Third blueprint

# api/info.py
from sanic import Blueprint
info = Blueprint("info", url_prefix="/info")

# Another blueprint group

# api/__init__.py
from sanic import Blueprint
from .content import content
from .info import info
api = Blueprint.group(content, info, url_prefix="/api")

# Main server

All blueprints are now registered

# app.py
from sanic import Sanic
from .api import api
app = Sanic(__name__)

# Middleware

Blueprints can also have middleware that is specifically registered for its endpoints only.

async def print_on_request(request):
    print("I am a spy")
async def halt_request(request):
    return text("I halted the request")
async def halt_response(request, response):
    return text("I halted the response")

Similarly, using blueprint groups, it is possible to apply middleware to an entire group of nested blueprints.

bp1 = Blueprint("bp1", url_prefix="/bp1")
bp2 = Blueprint("bp2", url_prefix="/bp2")
async def bp1_only_middleware(request):
    print("applied on Blueprint : bp1 Only")
async def bp1_route(request):
    return text("bp1")
async def bp2_route(request, param):
    return text(param)
group = Blueprint.group(bp1, bp2)
async def group_middleware(request):
    print("common middleware applied for both bp1 and bp2")
# Register Blueprint group under the app

# Exceptions

Just like other exception handling, you can define blueprint specific handlers.

def ignore_404s(request, exception):
    return text("Yep, I totally found the page: {}".format(request.url))

# Static files

Blueprints can also have their own static handlers

bp = Blueprint("bp", url_prefix="/bp")
bp.static("/web/path", "/folder/to/serve")
bp.static("/web/path", "/folder/to/server", name="uploads")

Which can then be retrieved using url_for(). See routing for more information.

>>> print(app.url_for("static", name="bp.uploads", filename="file.txt"))

# Listeners

Blueprints can also implement listeners.

async def before_server_start(app, loop):
async def after_server_stop(app, loop):

# Versioning

As discussed in the versioning section, blueprints can be used to implement different versions of a web API.

The version will be prepended to the routes as /v1 or /v2, etc.

auth1 = Blueprint("auth", url_prefix="/auth", version=1)
auth2 = Blueprint("auth", url_prefix="/auth", version=2)

When we register our blueprints on the app, the routes /v1/auth and /v2/auth will now point to the individual blueprints, which allows the creation of sub-sites for each API version.

from auth_blueprints import auth1, auth2
app = Sanic(__name__)

It is also possible to group the blueprints under a BlueprintGroup entity and version multiple of them together at the same time.

auth = Blueprint("auth", url_prefix="/auth")
metrics = Blueprint("metrics", url_prefix="/metrics")
group = Blueprint.group([auth, metrics], version="v1")
# This will provide APIs prefixed with the following URL path
# /v1/auth/ and /v1/metrics

# Composable

NEW in v21.6

A Blueprint may be registered to multiple groups, and each of BlueprintGroup itself could be registered and nested further. This creates a limitless possibility Blueprint composition.

Take a look at this example and see how the two handlers are actually mounted as five (5) distinct routes.

app = Sanic(__name__)
blueprint_1 = Blueprint("blueprint_1", url_prefix="/bp1")
blueprint_2 = Blueprint("blueprint_2", url_prefix="/bp2")
group = Blueprint.group(
primary = Blueprint.group(group, url_prefix="/primary")
def blueprint_1_default_route(request):
    return text("BP1_OK")
def blueprint_2_default_route(request):
    return text("BP2_OK")
# The mounted paths:
# /api/v1/grouped/bp1/
# /api/v1/grouped/bp2/
# /api/v1/primary/grouped/bp1
# /api/v1/primary/grouped/bp2
# /bp1

# Generating a URL

When generating a url with url_for(), the endpoint name will be in the form:

MIT Licensed
Copyright © 2018-present Sanic Community Organization

~ Made with ❤️ and ☕️ ~