kotlincode-reusejavalin

reuse the same set of routes in multiple paths with Javalin


so I am trying to refactor my route definitions in Javalin to be a little more DRY.

So I have the same set of actions performed at different hierarchies. I have a company, an application, and a environment and for each of these, I've split into separate routes that use the same controller action in Javalin.

Currently, it looks a little something like this:

app.routes {
    path("api/") {
        path("company/{companyId}") {
            path("application/{applicationId}") {
                path("project/{environmentId}") {
                    path("doThingA") {
                        post(controller::doThingA)
                    }
                    path("doThingB") {
                        post(controller::doThingB)
                    }
                }

                path("doThingA") {
                    post(controller::doThingA)
                }
                path("doThingB") {
                    post(controller::doThingB)
                }
            }

            path("doThingA") {
                post(controller::doThingA)
            }
            path("doThingB") {
                post(controller::doThingB)
            }
        }
    }
}

Now, this seems a little messy. Anytime I want to update the action for a given route, I have to do it three times. Ideally, I'd be looking for something like:

sharedRoutes = routes {
  path("doThingA") {
    post(controller::doThingA)
  }
  path("doThingB") {
    post(controller::doThingB)
  }
}

app.routes {
    path("api/") {
        path("company/{companyId}") {
            path("application/{applicationId}") {
                path("project/{environmentId}") { sharedRoutes }
                sharedRoutes
            }
            sharedRoutes
        }
    }
}

I know this above code is wrong but I hope it illustrates what I am trying to achieve.


Solution

  • I don't know Javalin and it's hard to find the proper KDoc on the web, but looking at the Javadoc, it seems that these route handlers are declared on an EndpointGroup type. So you could define your reusable routes as an extension on EndpointGroup like this:

    fun EndpointGroup.sharedRoutes() {
        path("doThingA") {
            post(controller::doThingA)
        }
        path("doThingB") {
            post(controller::doThingB)
        }
    }
    
    

    Then when you need it:

    app.routes {
        path("api/") {
            path("company/{companyId}") {
                path("application/{applicationId}") {
                    path("project/{environmentId}") { sharedRoutes() }
                    sharedRoutes()
                }
                sharedRoutes()
            }
        }
    }
    

    Note that the simplest way to discover this kind of trick (if you're using IntelliJ IDEA) is to select the part you want to extract and reuse, and use the "Extract method" refactoring on it. It will detect duplicates and do everything for you.