# 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.

Beginning in v21.12, a Blueprint may be registered before or after adding objects to it. Previously, only objects attached to the Blueprint at the time of registration would be loaded into application instance.

async def bp_root(request):

# Copying

Blueprints along with everything that is attached to them can be copied to new instances using the copy() method. The only required argument is to pass it a new name. However, you could also use this to override any of the values from the old blueprint.

v1 = Blueprint("Version1", version=1)
def something(request):
v2 = v1.copy("Version2", version=2)
Available routes:

# 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

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 ☕️ ~