Compared with Play routes

If you have been using Play’s routes file syntaxPlay’s routes file syntax earlier, this page may help you to use the Apache Pekko HTTP routing DSL.

Conceptual differences

The most apparent difference is Play’s use of special purpose syntax implemented as an external DSL, whereas Apache Pekko HTTP routes are described in Scala source codeJava source code with regular methods and values (as “embedded DSL”). Both are crafted to make the reader “grasp the code’s intention”.

The Apache Pekko HTTP DSL uses Directives to describe how incoming requests translate to functionality in the server. Play allows splitting the routes definitions in multiple routes files. The Apache Pekko HTTP DSL is very flexible and allows for composition so that different concerns can be properly split and organized as other source code would be.

Both Play and Apache Pekko HTTP choose the first matching route within the routes file/routes definition. In Play routes are listed with one route per line, in Apache Pekko HTTP multiple routes must be concatenated with the concat method.

Side-by-side

These examples are a non-comprehensive list of how Play routes could be written in Apache Pekko HTTP. They try to mimic the structure which Play uses, to aid understanding, even though it might not be the most Apache Pekko HTTP-idiomatic notation.

Static path

For example, to exactly match incoming GET /clients/all requests, you can define this route in Play.

GET   /clients/all          controllers.Clients.list()

In Apache Pekko HTTP every path segment is specified as a separate String concatenated with the / methodconcatenated by the slash method on segment.

Scala
source(get & path("clients" / "all")) {
  complete(Clients.list())
}
Scala test
sourceGet("/clients/all") ~> clientsAll ~> check {
  responseAs[String] shouldEqual "clientA,clientB,clientC"
}
Java
sourceget(() -> path(segment("clients").slash("all"), () -> complete(Clients.list())));
Java test
sourceTestRoute route = testRoute(new Routes().clientsAll());
route
    .run(HttpRequest.GET("/clients/all"))
    .assertStatusCode(StatusCodes.OK)
    .assertEntity("clientA,clientB,clientC");

Dynamic parts

If you want to define a route that retrieves a client by ID, you’ll need to add a dynamic part.

GET   /clients/:id          controllers.Clients.show(id: Long)

Apache Pekko HTTP uses path matchers which match certain data types and pass their data on.

Scala
source(get & path("client" / LongNumber)) { id =>
  complete(Clients.get(id))
}
Scala test
sourceGet("/client/321433") ~> clientById ~> check {
  responseAs[String] shouldEqual "clientB"
}
Java
sourceget(() -> path(segment("client").slash(longSegment()), id -> complete(Clients.get(id))));
Java test
sourceTestRoute route = testRoute(new Routes().clientById());
route
    .run(HttpRequest.GET("/client/321433"))
    .assertStatusCode(StatusCodes.OK)
    .assertEntity("clientB");

Dynamic parts spanning several /

You may want to capture a dynamic part of more than one URI path segment, separated by forward slashes.

GET   /files/*name          controllers.Application.download(name)

The Apache Pekko HTTP directive Remainingremaining() makes a list of the segments to be passed. (See Path Matchers for other ways to extract the path.)

Scala
source(get & path("files" / Remaining)) { name =>
  complete(download(name))
}
Scala test
sourceGet("/files/images/logo.png") ~> files ~> check {
  responseAs[String] shouldEqual "images/logo.png: file contents"
}
Java
sourceget(() -> path(segment("files").slash(remaining()), names -> complete(download(names))));
Java test
sourceTestRoute route = testRoute(new Routes().files());
route
    .run(HttpRequest.GET("/files/images/logo.png"))
    .assertStatusCode(StatusCodes.OK)
    .assertEntity("images/logo.png: file contents");

Access parameters

The Parameter directives give access to parameters passed on the URL.

Mandatory parameters

By default parameters are expected to be of type String. To make Apache Pekko HTTP convert a parameter to a different type, specify an unmarshaller.

# Extract the page parameter from the query string.
# i.e. http://myserver.com/?page=index
GET   /                     controllers.Application.show(page)
Scala
source(get & path("") & parameter("page")) { page =>
  complete(getPage(page))
}
Scala test
sourceGet("/?page=example.txt") ~> pageParameter ~> check {
  responseAs[String] shouldEqual "The requested [example.txt]."
}
Java
sourceget(() -> parameter("page", page -> complete(getPage(page))));
Java test
sourceTestRoute route = testRoute(new Routes().pageParameter());
route
    .run(HttpRequest.GET("?page=example.txt"))
    .assertStatusCode(StatusCodes.OK)
    .assertEntity("The requested [example.txt].");
route.run(HttpRequest.GET("/")).assertStatusCode(StatusCodes.NOT_FOUND);

Optional parameters

# The version parameter is optional. E.g. /api/list-all?version=3.0
GET   /api/list-all         controllers.Api.list(version: Option[String])

The parameter name may be decorated with .optional to mark it as optional (for other variants see other parameter extractors).

The parameterOptional directive passes the parameter as Optional<String>.

The directive parameterRequiredValue makes the route match only if the parameter contains the specified value.

See parameter extractors.

Scala
source(get & path("api" / "list-all") & parameter("version".optional)) { version =>
  complete(listAll(version))
}
Scala test
sourceGet("/api/list-all?version=3.0") ~> optionalPageParameter ~> check {
  responseAs[String] shouldEqual "aa,bb,cc"
}
Get("/api/list-all") ~> optionalPageParameter ~> check {
  responseAs[String] shouldEqual "ff"
}
Java
sourceget(
    () ->
        path(
            segment("api").slash("list"),
            () -> parameterOptional("version", version -> complete(apiList(version)))));
Java test
sourceTestRoute route = testRoute(new Routes().apiListWithVersion());
route
    .run(HttpRequest.GET("/api/list?version=3.0"))
    .assertStatusCode(StatusCodes.OK)
    .assertEntity("aa,bb,cc");
route.run(HttpRequest.GET("/api/list")).assertStatusCode(StatusCodes.OK).assertEntity("ff");

List parameters

This shows how a repeated URL parameter is captured.

# The item parameter is a list.
# E.g. /api/list-items?item=red&item=new&item=slippers
GET   /api/list-items      controllers.Api.listItems(item: List[String])

Decorating the parameter name with a .repeated makes Apache Pekko HTTP pass all values of that parameter as an Iterable[String]].

The parameterList directive may take a parameter name to specify a single parameter name to pass on as a List<String>.]

Scala
source(get & path("api" / "list-items") & parameters("item".repeated)) { items =>
  complete(listItems(items))
}
Scala test
sourceGet("/api/list-items?item=red&item=new&item=slippers") ~> itemParameterList ~> check {
  responseAs[String] shouldEqual "slippers,new,red"
}
Java
sourceget(
    () ->
        path(
            segment("api").slash("list-items"),
            () -> parameterList("item", items -> complete(apiItems(items)))));
Java test
sourceTestRoute route = testRoute(new Routes().apiListItems());
route
    .run(HttpRequest.GET("/api/list-items?item=red&item=new&item=slippers"))
    .assertStatusCode(StatusCodes.OK)
    .assertEntity("slippers,new,red"); // order is not kept