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
requestand 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:
strReturns a string and is the default value.intReturns a Python integer.floatReturns a Python floating-point number.uuidreturns an instance of Pythonuuid.UUID.pathreturns 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) -> URLroutes = [ 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
websocketparameter. - A class that implements the ASGI interface, such as Starlette's WebSocketEndpoint.