Search code examples
scalavert.xvertx-verticlejava-vertx-web

Vertx Web: How to split and organize routes across multiple files?


So far I'm really loving Vertx. The documentation is great, and the cross-language support is amazing.

However, the examples and documentation for my specific problem all seem to be out of date. I guess the API has changed a bit since 3.4.x (I'm currently using 3.9.1 with Scala 2.13.1.

I'd like to be able to split my routes among multiple files for the purpose of keeping things organized. For example, I'd like to have a UserRoutes file, and make a separate file for TodoRoutes, and both of those files can be used in my ServerVerticle.

The only way I've found to do this is basically:

UserRoutes:

object UserRoutes {
    def createUser(context: RoutingContext): Unit = {
        // do work
    }
    def login(context: RoutingContext): Unit = {
        // do work
    }
}

ServerVerticle:

class ServerVerticle(vertx: Vertx) extends AbstractVerticle {
    override def start(): Unit = {
        val router: Router = Router.router(vertx)

        router.post("/user/new").handle(UserRoutes.createUser)
        router.post("/user/login").handle(UserRoutes.login)
        ....
    }
}

What I would really like to do instead:

object UserRoutes {
    // somehow get a reference to `router` or create a new one
    router.post("/user/new", (context: RoutingContext) => {
        // do work
    })
    router.post("/user/login", (context: RoutingContext) => {
        // do work
    })
}

The reason I'd prefer this is because then it is easier to see exactly what is being done in UserRoutes, such as what path is being used, what parameters are required, etc.

I tried taking a similar approach to this example application, but apparently that's not really possible with the Vertx API as it exists in 3.9?

What is the best way to do this? What am I missing something? How do large REST APIs break up their routes?


Solution

  • I suppose one such way to do this would be something like the following:

    UserRoutes

    class UserRoutes(vertx: Vertx) {
      val router: Router = {
        val router = Router.router(vertx)
    
        router.get("/users/login").handler(context => {
          // do work
        })
    
        router
      }
    }
    

    And then in the ServerVerticle:

    ...
    server
        .requestHandler(new UserVerticle(vertx).router)
        .requestHandler(new TodoVerticle(vertx).router)
        .listen(...)
    

    This breaks stuff up nicely, but I still need to figure out a decent way to avoid having to repeats the cors options and stuff in each ___Routes file. There are plenty of way to do this of course, but the question remain: is this the right approach?

    Further, I could really mix in some parts of the approach outlined in the initial question. The UserRoutes class could still have the createUser() method, and I could simply use this in the apply() call or somewhere else.

    Sub-routers is another approach but there are still some issues with this.

    It would also be nice if each ___Routes file could create it's own Verticle. If each set of Routes was running on it's own thread this could speed things up quite nicely.

    Truth be told... as always in Scala there are a plethora of ways to tackle this. But which is the best, when we want:

    • Logical organization
    • Utilize the strengths of Vertx
      • Sub-verticles maybe?
      • Avoid shared state

    I need guidance! There's so many options I don't know where to start.

    EDIT: Maybe it's best to avoid assigning each group of routes to a Verticle, and letting vertx do it's thing?