Combining endpoints under same path prefix

Hi.
My team is migrating from Akka-HTTP to Tapir. One of the challenges we face is migrating from composing Akka’s routes to composing Tapir’s endpoints.
With Akka we are used to the approach, where we define some group of endpoints (including their specific path segments, parameters and server logic) in isolation, and then we combine these groups under some common path prefix.
Here is a toy example:

val catsRoute = path("cats") { get {...logic...} ~ post {...logic...} }
val dogsRoute = path("dogs") { get {...logic...} ~ post {...logic...} }
val mainRoute = path("animals") { catsRoute ~ dogsRoute }

The main point here is that before catsRoute or dogsRoute are passed to mainRoute, they are already configured with server logic. And mainRoute then composes them using some common path prefixes.

With Tapir you are expected to configure endpoint’s server logic only after you have completely defined the path of the endpoint.
It is not a problem to add server logic for all endpoints at once, when you have fairly small amount of endpoints, like it is done in Books example. But it is not clear how to conveniently manage endpoints and their logic when there are a lot of them and they are spread across different files/classes, yet we expect endpoints to have some hierarchy. The only way I can see so far is to pass common parts via parameters:

class CatsRoutes(baseEndpoint) {
    val endpointGet = baseEndpoint.in("cats").get.serverLogic(...)
    val endpointPost = baseEndpoint.in("cats").post.serverLogic(...)
}

But it means that we will have to duplicate path definition when migrating some part of Akka HTTP routes to Tapir.

class MainRoute {
    val route = path("animals") {
        new DogsRoute().route ~ new CatsRoute(endpoint.in("animals")).route
    }
}

Note that we specify “animals” segment both in Akka mainRoute and in Tapir’s endpoint passed to CatsRoute.
It might look not a big issue in this toy exampe, but with bigger hierarchy of endpoints it might cause issues. Especially, when a single endpoint can be accessed from multiple path prefixes.
Is there more convenient way to integrate these concepts within Tapir?

From what I understand, you’d like to add a context path to a server endpoint, once it is fully defined? If so, this is possible using the ServerEndpoint.prependSecurityIn method.

Also, if the .route method from your example runs the tapir->akka interpreter and returns an akka Route, the following snippet won’t work correctly:

val route = path("animals") {
   new DogsRoute().route ~ new CatsRoute(endpoint.in("animals")).route
}

The documentation will be correct, however as for the server-side behavior, the endpoint will match the /animals/animals/... route. Any directives with which the tapir-returned route is applied to, will have their normal effect. So I think the correct form would be:

val route = path("animals") {
   new DogsRoute().route // legacy "vanilla" endpoint
} ~ new CatsRoute(endpoint.in("animals")).route // migrated endpoint