For a REST API, your service will want to support different verbs/methods. Http4s has a list of all the methods you’re familiar with, and a few more.
import cats.effect._
import io.circe.generic.auto._
import io.circe.syntax._
import org.http4s._, org.http4s.dsl.io._
import org.http4s.circe._
case class TweetWithId(id: Int, message: String)
case class Tweet(message: String)
def getTweet(tweetId: Int): IO[Option[TweetWithId]] = ???
def addTweet(tweet: Tweet): IO[TweetWithId] = ???
def updateTweet(id: Int, tweet: Tweet): IO[Option[TweetWithId]] = ???
def deleteTweet(id: Int): IO[Unit] = ???
implicit val tweetWithIdEncoder = jsonEncoderOf[IO, TweetWithId]
// tweetWithIdEncoder: EntityEncoder[IO, TweetWithId] = org.http4s.EntityEncoder$$anon$2@1ebcd92a
implicit val tweetDecoder = jsonOf[IO, Tweet]
// tweetDecoder: EntityDecoder[IO, Tweet] = org.http4s.EntityDecoder$$anon$2@5bad99c5
val tweetService = HttpService[IO] {
case GET -> Root / "tweets" / IntVar(tweetId) =>
getTweet(tweetId)
.flatMap(_.fold(NotFound())(Ok(_)))
case req @ POST -> Root / "tweets" =>
req.as[Tweet].flatMap(addTweet).flatMap(Ok(_))
case req @ PUT -> Root / "tweets" / IntVar(tweetId) =>
req.as[Tweet]
.flatMap(updateTweet(tweetId, _))
.flatMap(_.fold(NotFound())(Ok(_)))
case HEAD -> Root / "tweets" / IntVar(tweetId) =>
getTweet(tweetId)
.flatMap(_.fold(NotFound())(_ => Ok()))
case DELETE -> Root / "tweets" / IntVar(tweetId) =>
deleteTweet(tweetId)
.flatMap(_ => Ok())
}
// tweetService: HttpService[IO] = Kleisli(
// org.http4s.HttpService$$$Lambda$12903/708292025@64ebf670
// )
There’s also DefaultHead
which replicates the functionality of the native
implementation of the HEAD
route.