Building a light-weight REST application with HTTP4S


Estafet consultants occasionally produce short, practical tech notes designed to help the broader software development community and colleagues.
 

If you would like to have a more detailed discussion about any of these areas and/or how Estafet can help your organisation and teams with best practices in improving your SDLC, you are very welcome to contact us at enquiries@estafet.com

Introduction

http4s is a typed and functional streaming HTTP library for Scala. Servers and clients share an immutable model of requests and responses. Standard headers are modelled as semantic types, and entity codecs are done by typeclass. The latest stable version(0.23.26) is compatible with Scala 2 and 3, ScalaJS(1.8). It runs on JDK 1.8+. In this tech. note I aim to show you how easy it is to create a lightweight REST application using this library.  

Why choose http4s?

  • Pure FP and sensible DSL
  • Full compliance with the HTTP protocol
  • It’s modular – you can combine and change different clients, servers, backends
  • Seamless integration with the whole Typelevel ecosystem
  • It’s pretty mature – it can be traced back to 2017

Foundations

  • Cats – this is a project which provides core building blocks(abstractions) and efficient functional types for the Scala language – data types, type classes and some syntactic enrichments.
  • cats-effect  – is used for pure asynchronous runtime and effectful code. The library provides additional types allowing complete separation of the effect type used to handle the I/O operations
  • fs2 – provides support for concurrent streams.

Dependencies

For generic http4s interfaces you most probably will use 

In the normal case, routing DSL and Ember client/server (default) will be quite enough.

In addition to JDK, Ember can also run on Node.js and Scala native

Http4s provides a standard interface for defining services and clients. This allows a wide range of interchangeable client and server implementations to be used. For example, if a socket client or proxy is needed, Netty can be used. Netty(https://netty.io/) is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

For possible integrations, you can check the documentation

The easiest way to get started with http4s is by using their ready giter8 template

This gives a basic working REST application with two endpoints – the popular HelloWorld greeting and a joke service.

Request matching

The core concept behind http4s DSL is that the server contains a predefined set of routes. An HttpRoutes[F] is a wrapper containing a function that transforms the request into (optional) response (Request => F[Response]), where F is an effectful operation.

As the Request is case class, we can pattern match against every property. For example

case req@PUT -> Root / “task” => // function to generate response 

Here we match against the HTTP method(PUT) and path(pathInfo) of the request(Root / “task”)

A request to the root of the service is matched with the Root extractor. Root consumes the leading slash of the path info. Each “/” after the initial slash delimits a path segment. For example, the following service responds with “Happy Birthday, Jamie!” to GET /birthday/Jamie:

Path parameters

Here the last segment is a parameter(with name placeholder). Http4s DSL allows us to use (right-associative)”/:” extractor for segments with arbitrary depth.

Please note that name parameters (and rest) are of String type. You can use predefined numeric extractors IntVar and LongVar, and UUIDVar for java.util.UUID. To extract a variable of type T you have to provide a custom extractor object which implements def unapply(str: String): Option[T]

Query parameters

A query parameter needs to have a QueryParamDecoderMatcher provided to extract it. In order for the QueryParamDecoderMatcher to work there needs to be an implicit QueryParamDecoder[T] in scope. 

To accept an optional query parameter a OptionalQueryParamDecoderMatcher can be used. OptionalMultiQueryParamDecoderMatcher as the name suggests is for multiple optional query parameters. For more information on provided types and implementation guidelines you can check in the documentation

Generating Responses

  • Status codes – http4s-dsl provides a shortcut to create an F[Response] by applying a status code. For example NoContent() generates response with
  • Headers – http4s-dsl includes some common headers in the response for us. You are free to include any specific/custom header/s with the response. It’s considered good practice to provide a Content-Type when responding with a body
  • Cookies – The library allows you to set or invalidate cookies. For example 
  • Body – A response body can be generated for any type with an implicit EntityEncoder in scope. http4s provides several out of the box – for plain text, binary. A good choice for Json conversion is Circe

HttpRoutes is an alias for Kleisli[OptionT[F, *], Request, Response]. As this is just a function, we can test it without a server. Due to an HttpRoutes[F] returning a F[Response[F]], we need to call unsafeRunSync on the result of the function to extract the Response[F]. Please note that unsafeRunSync should be used for test purposes only! 

Http4s includes some server middleware out of the box in the org.http4s.server.middleware package. For more details on the topic you can check the documentation:

Authentication

Cross Origin Resource Sharing (CORS)

Response Compression (GZip)

HSTS

CSRF

Serving Static Files

HTTP server

Our main application could be as simple as

Here we just extend cats.effect.IOApp trait. This trait defines an abstract run method that returns an IO[ExitCode]. An IOApp runs the process and adds a JVM shutdown hook to interrupt the infinite process and gracefully shut down your server when a SIGTERM is received. IOApp takes care of properly setting up (and tearing down) the cats. effect. unsafe. IORuntime needed to run the IO which represents your application. All of the associated thread pools (if relevant) will be configured with the assumption that application is fully contained within the IO produced by the run method. 

EmberServerBuilder is immutable so any chained method returns a new builder.

In my simple application I use the following routes:

The two get matchers could be rewritten as one with the /: extractor

HTTP Client

To show how easy is to build a client, I’ve made this simple one

Or any rest client you prefer. Here are some test curl requests

curl –header “Content-Type: application/json” –request POST \

 –data ‘{“id”:1,”description”:”Tom”}’ http://localhost:8080/task

curl –header “Content-Type: application/json” –request POST \

–data ‘{“id”:2,”description”:”Jerry”}’ http://localhost:8080/task

curl –header “Content-Type: application/json” –request PUT \

–data ‘{“id”:1,”description”:”Tom Tom Tom”}’ http://localhost:8080/task/1

curl –request DELETE http://localhost:8080/task/1

Conclusion

http4s is a great library for HTTP clients or servers. It is functional, safe and very powerful. It is built on the basis of proven libraries such as Cats, fs2, cats-effect. This tech note demonstrated how easy it is to use it to create a REST application. I hope this has stimulated your curiosity to try it out yourself, so give it a try!

By Delcho Delov, Consultant at Estafet

Stay Informed with Our Newsletter!

Get the latest news, exclusive articles, and updates delivered to your inbox.