# Routing

So far we have seen a lot of this decorator in different forms.

But what is it? And how do we use it?

@app.route("/stairway")
...
@app.get("/to")
...
@app.post("/heaven")
...

# Adding a route

The most basic way to wire up a handler to an endpoint is with app.add_route().

See API docs (opens new window) for more details.

async def handler(request):
    return text("OK")
app.add_route(handler, "/test")

By default, routes are available as an HTTP GET call. You can change a handler to respond to one or more HTTP methods.

app.add_route(
    handler,
    '/test',
    methods=["POST", "PUT"],
)

Using the decorator syntax, the previous example is identical to this.

@app.route('/test', methods=["POST", "PUT"])
async def handler(request):
    return text('OK')

# HTTP methods

Each of the standard HTTP methods has a convenience decorator.

    # Path parameters

    Sanic allows for pattern matching, and for extracting values from URL paths. These parameters are then injected as keyword arguments in the route handler.

    @app.get("/tag/<tag>")
    async def tag_handler(request, tag):
        return text("Tag - {}".format(tag))
    

    You can declare a type for the parameter. This will be enforced when matching, and also will type cast the variable.

    @app.get("/foo/<foo_id:uuid>")
    async def uuid_handler(request, foo_id: UUID):
        return text("UUID - {}".format(foo_id))
    

    # Supported types

      # Regex Matching

      More often than not, compared with complex routing, the above example is too simple, and we use a completely different routing matching pattern, so here we will explain the advanced usage of regex matching in detail.

      Sometimes, you want to match a part of a route:

      /image/123456789.jpg
      

      If you wanted to match the file pattern, but only capture the numeric portion, you need to do some regex fun 😄:

      app.route(r"/image/<img_id:(?P<img_id>\d+)\.jpg>")
      

      Further, these should all be acceptable:

      @app.get(r"/<foo:[a-z]{3}.txt>")                # matching on the full pattern
      @app.get(r"/<foo:([a-z]{3}).txt>")              # defining a single matching group
      @app.get(r"/<foo:(?P<foo>[a-z]{3}).txt>")       # defining a single named matching group
      @app.get(r"/<foo:(?P<foo>[a-z]{3}).(?:txt)>")   # defining a single named matching group, with one or more non-matching groups
      

      Also, if using a named matching group, it must be the same as the segment label.

      @app.get(r"/<foo:(?P<foo>\d+).jpg>")  # OK
      @app.get(r"/<foo:(?P<bar>\d+).jpg>")  # NOT OK
      

      For more regular usage methods, please refer to Regular expression operations (opens new window)

      # Generating a URL

      Sanic provides a method to generate URLs based on the handler method name: app.url_for(). This is useful if you want to avoid hardcoding url paths into your app; instead, you can just reference the handler name.

      @app.route('/')
      async def index(request):
          # generate a URL for the endpoint `post_handler`
          url = app.url_for('post_handler', post_id=5)
          # Redirect to `/posts/5`
          return redirect(url)
      @app.route('/posts/<post_id>')
      async def post_handler(request, post_id):
          ...
      

      You can pass any arbitrary number of keyword arguments. Anything that is not a request parameter will be implemented as a part of the query string.

      >>> app.url_for(
          "post_handler",
          post_id=5,
          arg_one="one",
          arg_two="two",
      )
      '/posts/5?arg_one=one&arg_two=two'
      

      Also supported is passing multiple values for a single query key.

      >>> app.url_for(
          "post_handler",
          post_id=5,
          arg_one=["one", "two"],
      )
      '/posts/5?arg_one=one&arg_one=two'
      

      # Special keyword arguments

      See API Docs for more details.

      >>> app.url_for("post_handler", post_id=5, arg_one="one", _anchor="anchor")
      '/posts/5?arg_one=one#anchor'
      # _external requires you to pass an argument _server or set SERVER_NAME in app.config if not url will be same as no _external
      >>> app.url_for("post_handler", post_id=5, arg_one="one", _external=True)
      '//server/posts/5?arg_one=one'
      # when specifying _scheme, _external must be True
      >>> app.url_for("post_handler", post_id=5, arg_one="one", _scheme="http", _external=True)
      'http://server/posts/5?arg_one=one'
      # you can pass all special arguments at once
      >>> app.url_for("post_handler", post_id=5, arg_one=["one", "two"], arg_two=2, _anchor="anchor", _scheme="http", _external=True, _server="another_server:8888")
      'http://another_server:8888/posts/5?arg_one=one&arg_one=two&arg_two=2#anchor'
      

      # Customizing a route name

      A custom route name can be used by passing a name argument while registering the route.

      @app.get("/get", name="get_handler")
      def handler(request):
          return text("OK")
      

      Now, use this custom name to retrieve the URL

      >>> app.url_for("get_handler", foo="bar")
      '/get?foo=bar'
      

      # Websockets routes

      Websocket routing works similar to HTTP methods.

      async def handler(request, ws):
          messgage = "Start"
          while True:
              await ws.send(message)
              message = ws.recv()
      app.add_websocket_route(handler, "/test")
      

      It also has a convenience decorator.

      @app.websocket("/test")
      async def handler(request, ws):
          messgage = "Start"
          while True:
              await ws.send(message)
              message = ws.recv()
      

      Read the websockets section to learn more about how they work.

      # Strict slashes

      Sanic routes can be configured to strictly match on whether or not there is a trailing slash: /. This can be configured at a few levels and follows this order of precedence:

      1. Route
      2. Blueprint
      3. BlueprintGroup
      4. Application
      # provide default strict_slashes value for all routes
      app = Sanic(__file__, strict_slashes=True)
      
      # overwrite strict_slashes value for specific route
      @app.get("/get", strict_slashes=False)
      def handler(request):
          return text("OK")
      
      # it also works for blueprints
      bp = Blueprint(__file__, strict_slashes=True)
      @bp.get("/bp/get", strict_slashes=False)
      def handler(request):
          return text("OK")
      
      bp1 = Blueprint(name="bp1", url_prefix="/bp1")
      bp2 = Blueprint(
          name="bp2",
          url_prefix="/bp2",
          strict_slashes=False,
      )
      # This will enforce strict slashes check on the routes
      # under bp1 but ignore bp2 as that has an explicitly
      # set the strict slashes check to false
      group = Blueprint.group([bp1, bp2], strict_slashes=True)
      

      # Static files

      In order to serve static files from Sanic, use app.static().

      The order of arguments is important:

      1. Route the files will be served from
      2. Path to the files on the server

      See API docs for more details.

      app.static("/static", "/path/to/directory")
      

      You can also serve individual files.

      app.static("/", "/path/to/index.html")
      

      It is also sometimes helpful to name your endpoint

      app.static(
          "/user/uploads",
          "/path/to/uploads",
          name="uploads",
      )
      

      Retrieving the URLs works similar to handlers. But, we can also add the filename argument when we need a specific file inside a directory.

      >>> app.url_for(
          "static",
          name="static",
          filename="file.txt",
      )
      '/static/file.txt'
      ```python
      >>> app.url_for(
          "static",
          name="uploads",
          filename="image.png",
      )
      '/user/uploads/image.png'
      

      TIP

      If you are going to have multiple static() routes, then it is highly suggested that you manually name them. This will almost certainly alleviate potential hard to discover bugs.

      app.static("/user/uploads", "/path/to/uploads", name="uploads")
      app.static("/user/profile", "/path/to/profile", name="profile_pics")
      
      MIT Licensed
      Copyright © 2018-present Sanic Community Organization

      ~ Made with ❤️ and ☕️ ~