Routing
Routing is the core Ktor plugin (formerly known as feature) for handling incoming requests in a server application. When the client makes a request to a specific URL (for example, /hello
), the routing mechanism allows us to define how we want this request to be served.
Install Routing
The Routing plugin can be installed in the following way:
Given the Routing
plugin is so common in any application, there is a convenient routing
function that makes it simpler to install routing. In the code snippet below, install(Routing)
is replaced with the routing
function:
Define a route handler
After installing the Routing plugin, you can call the route function inside routing
to define a route:
Ktor also provides a series of functions that make defining route handlers much easier and more concise. For example, you can replace the previous code with a get function that now only needs to take the URL and the code to handle the request:
Similarly, Ktor provides functions for all the other verbs, that is put
, post
, head
, and so on.
In summary, you need to specify the following settings to define a route:
HTTP verb
Choose the HTTP verb, such asGET
,POST
,PUT
, and so on. The most convenient way is to use a dedicated verb function, such asget
,post
,put
, and so on.Path pattern
Specify a path pattern used to match a URL path, for example,/hello
,/customer/{id}
. You can pass a path pattern right to theget
/post
/etc. function, or you can use theroute
function to group route handlers and define nested routes.Handler
Specify how to handle requests and responses. Inside the handler, you can get access toApplicationCall
, handle client requests, and send responses.
Specify a path pattern
A path pattern passed to the routing functions (route
, get
, post
, etc.) is used to match a path component of the URL. A path can contain a sequence of path segments separated by a slash /
character.
Below are several path examples:
/hello
A path containing a single path segment./order/shipment
A path containing several path segments. You can pass such a path to the route/get/etc. function as is or organize sub-routes by nesting severalroute
functions./user/{login}
A path with thelogin
route parameter, whose value can be accessed inside the route handler./user/*
A path with a wildcard character that matches any path segment./user/{...}
A path with a tailcard that matches all the rest of the URL path./user/{param...}
A path containing a route parameter with tailcard.
Wildcard
A wildcard (*
) matches any path segment and can't be missing. For example, /user/*
matches /user/john
, but doesn't match /user
.
Tailcard
A tailcard ({...}
) matches all the rest of the URL path, can include several path segments, and can be empty. For example, /user/{...}
matches /user/john/settings
as well as /user
.
Route parameter
A route parameter ({param}
) matches a path segment and captures it as a parameter named param
. This path segment is mandatory, but you can make it optional by adding a question mark: {param?}
. For example:
/user/{login}
matches/user/john
, but doesn't match/user
./user/{login?}
matches/user/john
as well as/user
.
To access a parameter value inside the route handler, use the call.parameters
property. For example, call.parameters["login"]
in the code snippet below will return admin for the /user/admin
path:
Route parameter with tailcard
A route parameter with a tailcard ({param...}
) matches all the rest of the URL path and puts multiple values for each path segment into parameters using param
as key. For example, /user/{param...}
matches /user/john/settings
.
To access path segments' values inside the route handler, use call.parameters.getAll("param")
. For the example above, the getAll
function will return an array containing the john and settings values.
Define multiple route handlers
If you want to define multiple route handlers, which of course is the case for any application, you can just add them to the routing
function:
In this case, each route has its own function and responds to the specific endpoint and HTTP verb.
An alternative way is to group these by paths, whereby you define the path and then place the verbs for that path as nested functions, using the route
function:
Independently of how you do the grouping, Ktor also allows you to have sub-routes as parameters to route
functions. The following example shows us how to respond to incoming requests to /order/shipment
:
Route extension functions
A common pattern is to use extension functions on the Route
type to define the actual routes, allowing us easy access to the verbs and remove clutter of having all routes in a single routing function. You can apply this pattern independently of how you decide to group routes. As such, the first example could be represented in a cleaner way:
For our application to scale when it comes to maintainability, it is recommended to follow certain Structuring patterns.