Skip to content

HTTP routing

Starlette has a simple but powerful request routing system. Routing tables are defined as route lists and passed when the application is instantiated.

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)

endpoint Parameters can be one of the following:

  • A regular or asynchronous function that takes a single argument to request and should return a response.
  • A class that implements the ASGI interface, such as Starlette's HTTPEndpoint.

path parameters

Paths can use URI template styles to capture path components.

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

By default, this captures characters until the end of the path or the next /.

You can use converters to modify captured content. The available converters are:

  • str Returns a string and is the default value.
  • int Returns a Python integer.
  • float Returns a Python floating-point number.
  • uuid returns an instance of Python uuid.UUID.
  • path returns the rest of the path, including any additional / characters.

Converters are used by prepending them with colons, as follows:

Route('/users/{user_id:int}', user)
Route('/floating-point/{number:float}', floating_point)
Route('/uploaded/{rest_of_path:path}', uploaded)

If you need a different converter that is undefined, you can create your own. See below for an example of how to create an datetime converter and how to register it:

from datetime import datetime

from starlette.convertors import Convertor, register_url_convertor


class DateTimeConvertor(Convertor):
    regex = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]+)?"

    def convert(self, value: str) -> datetime:
        return datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")

    def to_string(self, value: datetime) -> str:
        return value.strftime("%Y-%m-%dT%H:%M:%S")

register_url_convertor("datetime", DateTimeConvertor())

Once registered, you will be able to use it as:

Route('/history/{date:datetime}', history)

Path parameters are available in the request as an request.path_params dictionary.

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

Processing HTTP methods

Routes can also specify which HTTP methods endpoints handle:

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

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

submount wiring

In large applications, you might find that you want to split parts of the routing table based on common path prefixes.

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

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

from myproject import users, auth

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

You can also include child applications in your Starlette application using mounts. 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", app=StaticFiles(directory="static"), name="static")
]

app = Starlette(routes=routes)

Reverse URL lookup

You will often want to be able to generate URLs for specific routes, for example, if you need to return a redirect response.

  • Signed by: url_for(name, **path_params) -> URL

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

    We can use the following to return a URL…

    url = request.url_for("Home")

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 an Mount contains an name, then the base should use the {prefix}:{name} style for reverse URL lookup.

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=...)

The installed application may contain an path=... parameter.

routes = [
    ...
    Mount("/static", app=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 do reverse lookups against the application, although these lookups only return URL paths.

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

host-based routing

If you want to use different routes for the same path based on the Host header.

Note that when matching, it will change from Host header. For example, even if Host header contains or does not contain except 3600 ( example.org:5600 , example.org ), Host (host='example.org:3600', ...) will also be processed. So if you're url_for You can specify a port if you want to use it.

There are several ways to connect host-based routing to your application

site = Router()  # Use eg. `@site.route()` to configure this.
api = Router()  # Use eg. `@api.route()` to configure this.
news = Router()  # Use eg. `@news.route()` to configure this.

routes = [
    Host('api.example.org', api, name="site_api")
]

app = Starlette(routes=routes)

app.host('www.example.org', site, name="main_site")

news_host = Host('news.example.org', news)
app.router.routes.append(news_host)

URL queries can include host parameters, just like path parameters

routes = [
    Host("{subdomain}.example.org", name="sub", app=Router(routes=[
        Mount("/users", name="users", routes=[
            Route("/", user, name="user_list"),
            Route("/{username}", user, name="user_detail")
        ])
    ]))
]
...
url = request.url_for("sub:users:user_detail", username=..., subdomain=...)
url = request.url_for("sub:users:user_list", subdomain=...)

routing priority

The incoming path matches each Route sequentially.

In cases where there may be multiple routes matching the incoming path, you should be careful to ensure that more specific routes are listed before the general case.

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 are working at a low level, you may want to use a plain Router instance instead of creating an Starlette application. This gives you a lightweight ASGI application that only provides application routing and does not wrap it in any middleware.

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

WebSocket routing

When using WebSocket endpoints, you should use WebSocketRoute instead of the usual Route.

Path parameters and reverse URL lookups for WebSocketRoute work the same way as HTTP Route, which can be found in the HTTP routing section above.

from starlette.applications import Starlette
from starlette.routing import WebSocketRoute


async def websocket_index(websocket):
    await websocket.accept()
    await websocket.send_text("Hello, websocket!")
    await websocket.close()


async def websocket_user(websocket):
    name = websocket.path_params["name"]
    await websocket.accept()
    await websocket.send_text(f"Hello, {name}")
    await websocket.close()


routes = [
    WebSocketRoute("/", endpoint=websocket_index),
    WebSocketRoute("/{name}", endpoint=websocket_user),
]

app = Starlette(routes=routes)

endpoint Parameters can be one of the following:

  • An asynchronous function that accepts a single websocket parameter.
  • A class that implements the ASGI interface, such as Starlette's WebSocketEndpoint.