Routing

Starlette has a simple but capable request routing system. A routing table is defined as a list of routes, and passed when instantiating the application.

from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route


async def homepage(request):
    return PlainTextResponse("Homepage")

async def about(request):
   return PlainTextResponse("About")


routes = [
    Route("/", endpoint=homepage),
    Route("/about", endpoint=about),
]

app = Starlette(routes=routes)

The endpoint argument can be one of:

  • A regular function or async function, which accepts a single request argument and which should return a response.
  • A class that implements the ASGI interface, such as Starlette's class based views.

Path Parameters

Paths can use URI templating style to capture path components.

Route('/users/{username}', user)

Convertors for int, float, and path are also available:

Route('/users/{user_id:int}', user)

Path parameters are made available in the request, as the request.path_params dictionary.

async def user(request):
    user_id = request.path_params['user_id']
    ...

Handling HTTP methods

Routes can also specify which HTTP methods are handled by an endpoint:

Route('/users/{user_id:int}', user, methods=["GET", "POST"])

By default function endpoints will only accept GET requests, unless specified.

Submounting routes

In large applications you might find that you want to break out parts of the routing table, based on a common path prefix.

routes = [
    Route('/', homepage),
    Mount('/users', routes=[
        Route('/', users, methods=['GET', 'POST']),
        Route('/{username}', user),
    ])
]

This style allows you to define different subsets of the routing table in different parts of your project.

from myproject import users, auth

routes = [
    Route('/', homepage),
    Mount('/users', routes=users.routes),
    Mount('/auth', routes=auth.routes),
]

You can also use mounting to include sub-applications within your Starlette application. For example...

# This is a standalone static files server:
app = StaticFiles(directory="static")

# This is a static files server mounted within a Starlette application,
# underneath the "/static" path.
routes = [
    ...
    Mount("/static", StaticFiles(directory="static"), name="static")
]

app = Starlette(routes=routes)

Reverse URL lookups

You'll often want to be able to generate the URL for a particular route, such as in cases where you need to return a redirect response.

routes = [
    Route("/", homepage, name="homepage")
]

# We can use the following to return a URL...
url = request.url_for("homepage")

URL lookups can include path parameters...

routes = [
    Route("/users/{username}", user, name="user_detail")
]

# We can use the following to return a URL...
url = request.url_for("user_detail", username=...)

If a Mount includes a name, then submounts should use a {prefix}:{name} style for reverse URL lookups.

routes = [
    Mount("/users", name="users", routes=[
        Route("/", user, name="user_list"),
        Route("/{username}", user, name="user_detail")
    ])
]

# We can use the following to return URLs...
url = request.url_for("users:user_list")
url = request.url_for("users:user_detail", username=...)

Mounted applications may include a path=... parameter.

routes = [
    ...
    Mount("/static", StaticFiles(directory="static"), name="static")
]

# We can use the following to return URLs...
url = request.url_for("static", path="/css/base.css")

For cases where there is no request instance, you can make reverse lookups against the application, although these will only return the URL path.

url = app.url_path_for("user_detail", username=...)

Route priority

Incoming paths are matched against each Route in order.

In cases where more that one route could match an incoming path, you should take care to ensure that more specific routes are listed before general cases.

For example:

# Don't do this: `/users/me` will never match incoming requests.
routes = [
    Route('/users/{username}', user),
    Route('/users/me', current_user),
]

# Do this: `/users/me` is tested first.
routes = [
    Route('/users/me', current_user),
    Route('/users/{username}', user),
]

Working with Router instances

If you're working at a low-level you might want to use a plain Router instance, rather that creating a Starlette application. This gives you a lightweight ASGI application that just provides the application routing, without wrapping it up in any middleware.

app = Router(routes=[
    Route('/', homepage),
    Mount('/users', routes=[
        Route('/', users, methods=['GET', 'POST']),
        Route('/{username}', user),
    ])
])