Implementing 404 with Akka HTTP

Hi folks,

I’m trying to improve the error handling on my REST API and figure out how best to implement a 404 response when a resource can’t be found.

My routes call Akka actors which use a DAO (implemented with Slick) to query the database.

In the case that a record can’t be found, should the Actor respond to the sender (the route) with the error and then it would be up to the route to understand if it had received an error or something that could be serializable to JSON?

For example:

// PostActor.scala
// Inside the PostActor

case GetAllPosts =>
      println(s"Searching for posts")
      val postsFuture = PostsDao.findAll
      val originalSender = sender
      postsFuture.onComplete {
        case Success(posts) => originalSender ! posts.toList
        case Failure(e) =>
          println("Posts not found")
          originalSender ! e
      }

    case GetPostByID(id) =>
      println(s"Finding post with id: $id")
      val postFuture = PostsDao.findById(id)
      val originalSender = sender
      postFuture.onComplete {
        case Success(post) => originalSender ! post
        case Failure(e) =>
          println(s"Post $id not found")
          originalSender ! e
      }

I’m not sure how to implement this in the routes. Most of the documentation on exception handling with Akka HTTP seems to only cover exceptions that are thrown in the routes, rather than passing an exception to a route.

// PostRoutes.scala
// inside post routes
get {
        path(Segment) { id =>
          val uuid = UUID.fromString(id)
          complete((postActor ? GetPostByID(uuid)).mapTo[Post])
        } ~
          pathEndOrSingleSlash {
            complete((postActor ? GetAllPosts).mapTo[List[Post]])
          }
      }

The full source code is in this GitHub repo.

I’d be very grateful if anyone could suggest how to communicate exceptions between the actors and the routes and then handle them with the appropriate HTTP response.

Yes! Akka-Http is a toolkit that means you need to do things by hand. You can write a function/directive which can work as a complete and checks if the returned thing is a None or a Some, and map them to responses with status codes. Or you can write the same to Either, Try, Future[Either[A,B]], EitherT, etc. The cool thing that it is up to you how to handle these cases, and you can generalize them. The sad thing, that even the smallest application you need to do these by hand…

Also, this is a really useful page from the docs, about the global exceptions and FutureFails, and how you can handle them.

For a slightly better but still really flexible solution, I would consider reading about tapir. You still need to understand some things from akka-http, but it can do some things so you don’t have to. (For example it generates open-api docs, makes easier to understand and manage endpoints, and also explicitly forces you to transform your return value to Future[Either[ErrorResponse,OkResponse]])

1 Like