scalascala-catshttp4stagless-final

Converting an `Option[A]` to an Ok() or NotFound() inside an Http4s API


I've got an API that looks like this:

object Comics {
  ...

  def impl[F[_]: Applicative]: Comics[F] = new Comics[F] {
    def getAuthor(slug: Authors.Slug): F[Option[Authors.Author]] =
      ...

and a routing that looks like this:

object Routes {
  def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
    val dsl = new Http4sDsl[F] {}
    import dsl._
    HttpRoutes.of[F] {
      case GET -> Root / "comics" / authorSlug =>
        comics
          .getAuthor(Authors.Slug(authorSlug))
          .flatMap {
            case Some(author) => Ok(author)
            case None         => NotFound()
          }

So when there is a None, it gets converted to a 404. Since there are several routes, the .flatMap { ... } gets duplicated.

Question: How do I move this into a separate .orNotFound helper function specific to my project?


My attempt:

To make things simple for me (and avoid parameterisation over F initially), I've tried to define this inside comicsRoutes:

  def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
    val dsl = new Http4sDsl[F] {}
    import dsl._

    def orNotFound[A](opt: Option[A]): ???[A] =
      opt match {
        case Some(value) => Ok(value)
        case None        => NotFound()
      }

    HttpRoutes.of[F] {
      case GET -> Root / "comics" / authorSlug =>
        comics
          .getAuthor(Authors.Slug(authorSlug))
          .flatMap(orNotFound)

But what's ??? here? It doesn't seem to be Response or Status. Also, the .flatMap { ... } was made under import dsl._, but I'd like to move this further out. What would a good place be? Does it go into the routes file, or do I put it in a separate ExtendedSomething extension file? (I expect that ??? and Something might be related, but I'm a little confused as to what the missing types are.)

(Equally importantly, how do I find out what ??? is here? I was hoping ??? at the type level might give me a "typed hole", and VSCode's hover function provides very sporadic documentation value.)


Solution

  • The type returned by Http4sDsl[F] for your actions is F[Response[F]].

    It has to be wrapped with F because your are using .flatMap on F. Response is parametrized with F because it will produce the result returned to caller using F.

    To find that out you can use IntelliJ and then generate the annotation by IDE (Alt+Enter and then "Add type annotation to value definition"). You can also:

    So if you want to move things around/extract them, pay attention to what implicits are required to instantiate the DSL and extension methods.

    (Shortcuts differ between Mac OS - which sometime use Cmd instead of Ctrl - and non-Mac OS systems so just check them in documentation if you have an issue).