akka / akka-http

The Streaming-first HTTP server/module of Akka
https://doc.akka.io/docs/akka-http
Other
1.34k stars 596 forks source link

Type safety for path directives #2956

Open nrktkt opened 4 years ago

nrktkt commented 4 years ago

Today, it's possible to build routing DSLs with the path directives that result in unreachable code. For example

path("users" / Segment) { userId =>
  get {
    complete(userService.getUser(userId)
  } ~
  path("friends") { 
    get {
      complete(userService.listFriends(userId)
    }
  }
}

Noticing this requires an attention to detail that it would be great if the compiler could exert for us. The general idea would be to create a trait which indicates if a route or directive is path-sensitive and if so, don't allow it to be passed to an inner directive of path or pathEnd. I could see the scope of this going a little crazy, so a first step could be to just to try to cover use cases with the routing DSL, and say it's fair to lose type safety when flat mapping and such.

jrudolph commented 4 years ago

I ran into that trap often enough myself, so I can feel the pain and see how that would be useful.

That said, a compile-time solution is very unlikely. The routing DSL is by design very dynamic. This has various drawbacks but the main benefit is that the DSL is consistent. There are basically only two types, Directive and Route that can be used to implement any kind of behavior you need. This is probably not going to change. If you think about it, it would have to be a massively invasive change just to support the type-safety for path. What if we would like to introduce type-safe variants of other directives like get/post etc?

I can see different ways of improvement:

nrktkt commented 4 years ago

it would have to be a massively invasive change just to support the type-safety for path

This is what I meant by "the scope of this going a little crazy". I was hopeful that a minimally invasive solution could be brainstormed. One that doesn't make things completely type-safe, but covers the common gotchas. But,

Being able to inspect the route tree would be almost as good, for me. Especially the ability to write a test to check the structure itself without having to send a request to hit every end route. The ability to inspect the route tree would also unlock some other interesting features like listing endpoints for documentation generation.

This problem does not seem to be a runtime problem but more of a debugging problem

I think this point is fair, but optimistic. Being confident that the code is correct after it compiles is always going to be better than code that requires even one short test, which is always going to be better than code that requires many short tests. I think people are likely to be less rigorous the latter down that ladder they go (self included). I think being able to say "just write a test like

val violations: Seq[RouteViolation] = myRoute.check
violations shouldBe empty

would be preferable to a way to trace a specific execution.