We will be looking into how Http4s supports creating servers as well as client. The library's support for circe to serialise and deserialise request and response.
Lack of etiquette and manners is a huge turn off.
KnolX Etiquettes
Punctuality
Join the session 5 minutes prior to
the session start time. We start on
time and conclude on time!
Feedback
Make sure to submit a constructive
feedback for all sessions as it is
very helpful for the presenter.
Silent Mode
Keep your mobile devices in silent
mode, feel free to move out of
session in case you need to attend
an urgent call.
Avoid Disturbance
Avoid unwanted chit chat during
the session.
Our Agenda
Recap of http4s
Introducing Middleware in http4s
Demo with Basic http4s Routes
Demo with http4s Routes + middleware
Basics of http4s
type HttpRoutes = Kleisli [ OptionT [ F, * ], Request, Response ]
http4s - Recap
● Lightweight Library
● Typeful
● Purely Functional
● Based on Cats and fs2-streaming
● Streaming
● Performant
Heart of http4s
http4s DSL
object LocalDateVar {
def unapply(str: String): Option[LocalDate] = {
if (!str.isEmpty)
Try(LocalDate.parse(str)).toOption
else
None
}
}
case GET -> Root / "weather" / "temperature" / LocalDateVar(localDate) =>
Ok(getTemperatureForecast(localDate)
.map(s"The temperature on $localDate will be: " + _))
http4s - Recap
val route: HttpRoutes[IO] = HttpRoutes.of[IO] {
case GET -> Root / "length" / str => Ok(str.length.toString)
case GET -> Root / "hello" / name => Ok(s"""Hello, ${name}!""")
}
Handling Path Parameters
QueryParamDecoderMatcher
object OptionalYearQueryParamMatcher
extends OptionalQueryParamDecoderMatcher[Year]("year")
val routes = HttpRoutes.of[IO] {
case GET -> Root / "temperature" :?
OptionalYearQueryParamMatcher(maybeYear) =>
maybeYear match {
case None => Ok(getAverageTemperatureForCurrentYear)
case Some(year) => Ok(getAverageTemperatureForYear(year))
}
}
Ex: /temperature or /temperature?year=2005
http4s - Recap
implicit val yearQueryParamDecoder: QueryParamDecoder[Year] =
QueryParamDecoder[Int].map(Year.of)
object YearQueryParamMatcher
extends QueryParamDecoderMatcher[Year]("year")
case GET -> Root / "weather" / "temperature"
:? YearQueryParamMatcher(year) =>
Ok(getAverageTemperatureForYear(year)
.map(s"Average temperature for $country in $year was: " + _))
Ex: /weather/temperature?year=2005
OptionalQueryParamDecoderMatcher
ValidatingQueryParamDecoderMatcher
object LongParamMatcher
extends OptionalValidatingQueryParamDecoderMatcher[Long]("long")
case GET -> Root / "number" :? LongParamMatcher(maybeNumber) =>
maybeNumber match {
case Some(n) =>
n.fold(
parseFailures => BadRequest("unable to parse argument 'long'"),
year => Ok(n.toString)
)
case None => BadRequest("missing number")
}
Ex: /number?long=13
http4s - Recap
implicit val yearQueryParamDecoder = QueryParamDecoder[Int]
.emap(i => Try(Year.of(i))
.toEither
.leftMap(t => ParseFailure(t.getMessage, t.getMessage)))
object YearQueryParamMatcher
extends ValidatingQueryParamDecoderMatcher[Year]("year")
case GET -> Root / "temperature" :?
YearQueryParamMatcher(yearValidated) =>
yearValidated.fold(
parseFailures => BadRequest("unable to parse argument year"),
year => Ok(getAverageTemperatureForYear(year))
)
Ex: /temperature?year=2005
OptionalValidatingQueryParamDecoderMatcher
● Abstraction around a service
● Manipulate the request sent to a service and/or response
returned by the service
● Function that takes one service and returns another service
● Ex: def addHeaders(
service: HttpRoutes[IO],
header: Header.ToRaw
): HttpRoutes[IO]
addHeaders(
userServiceAPI,
"extra-added-header" -> "extra-header-value"
)
http4s - Middleware
http4s includes some middleware OOB in the codebase:
● Authentication
● CORS
● Response Compression
● Metrics
● Loggers