While there are a lot of great talks on Youtube about http4k, only very little has been written yet. Wanting to explore the area of technical writing, I decided to start a series of articles about the library, hoping that others will find it useful and start playing around with http4k.
Today we will start by giving a little glimpse on what http4k is and progress later on with more specific topics.
Today we will start by giving a little glimpse on what http4k is and progress later on with more specific topics.
But let's get into it. Citing their website, "http4k is a lightweight but fully-featured HTTP toolkit written in pure Kotlin[...]".
So far so good, nothing special. Why should one care about another HTTP library?
Before I show you, we first have to understand the main paradigm:
Application As A Function
The main difference to other libraries and frameworks in the Kotlin ecosystem is that http4k follows the principle of " Application as a Function", which is based on a paper from 2013 by Marius Eriksen at Twitter. Summing up the paper, fully fledged HTTP services can be written by composing just two types of functions: A Service and a Filter.
- Service: A Service is a system boundary which takes a Request and returns a Response. In http4k, these are called HttpHandlers.
- Filter: Filters, on the other hand, are used for pre- or post-processing, e.g. call-logging or authentication. One can refer to them as middleware.
Having these two concepts in mind, we can finally start diving into http4k.
Http4k's Principles
While the documentation is a good place to learn about all aspects, I really wanted to limit it a little. This turned out to be harder than expected, but I decided to choose the following three aspects that stand out for me:
Simplicity, Explorability & Adaptionability and Testability.
Simplicity
Http4k does one thing and it does it in the simplest way possible. It does not care how you provide application properties or how you structure your code. As long as you pass in the needed parameters to a function, everything is fine.
Though what I really like is that it still offers other modules like serverless, graphql or AWS connectors to have in place if one wants to use them.
Though what I really like is that it still offers other modules like serverless, graphql or AWS connectors to have in place if one wants to use them.
Talking about that one thing, an http service: Server as a Function might sound simple, but it's incredibly powerful. You take some functions, plumb them together and voilà, you have a running server. For example, http4k's "Hello World!" looks like that:
val myFirstFunction: HttpHandler = { _: Request -> Response(Status.OK).body("Hello World!") }
Sounds easy. That's all? How does it work? If we look at what an HttpHandler is, we will find out that it is nothing more than a type alias for (Request) -> Response. Digging deeper you will see that there is no magic, no reflection, no annotations, just immutable structures that make excellent use of Kotlin's language design.
To run it as a server, we simply call our app as follows:
val server = myFirstFunction.asServer(SunHttp(8080)).start()
Place it in your main function and you're good to go. SunHttp(8080) states that we will use Sun's HTTP server, but http4k also offers a lot of different server backends which are easily swappable. You're thinking about using GraalVM? Sure, just choose the Apache module.
What about calling another service? Well, we already talked about HttpHandlers and that they take a Request and turn it into a Response. That's exactly what we want, so let's use it.
val client: HttpHandler = ApacheClient() val request = Request(GET, "http://otherservice.com/get").query("message", "Hello!") val response = client(request)
Keeping concepts to the bare minimum helps a lot with testing, as we will learn later on. But first let's talk about exploring code.
Explorability & Adaptionability
Since the concepts are simple, learning it is rather fast, which is great, especially for new colleagues.
But the point that I enjoy the most is that it's a joy to wander around the source code and learn. All the decisions are reasonable and the code is easy to understand. I learned a lot about how I can get the best out of Kotlin by digging into the source code of the library.
Furthermore, since the concepts are simple and the library is easily accessible, writing your own adapters is not that far off. If you want to look at some examples, visit the http4k-connect Github repository. If you want to understand how it works and how you can use it on your own, I really recommend the article by one of the libraries creators, David Denton.
But the point that I enjoy the most is that it's a joy to wander around the source code and learn. All the decisions are reasonable and the code is easy to understand. I learned a lot about how I can get the best out of Kotlin by digging into the source code of the library.
Furthermore, since the concepts are simple and the library is easily accessible, writing your own adapters is not that far off. If you want to look at some examples, visit the http4k-connect Github repository. If you want to understand how it works and how you can use it on your own, I really recommend the article by one of the libraries creators, David Denton.
Testability
This was actually the thing that stood out the most for me. Testing in http4k is a breeze. You can clearly see that the library is being built by TDD practitioners.
Testing is an aspect that I want to cover in an article itself, but to give you an idea of how easy it is, a simple example: We have an endpoint that takes a body and returns it as uppercase.
val upper: HttpHandler = { request: Request -> Response(Status.OK).body( request.body .toString() .toUpperCase() ) }
Usually, we would set up frameworks like Rest Assured or make use of Springs MockMvc.
With http4k things are a little different:
class Test { @Test fun `should capitalize a word`() { val input = Request(Method.POST, "/capitalize").body("hello") val expectedResponse = Response(Status.OK).body("HELLO") upper(input) shouldBe expectedResponse } }
We define a request, set up the expected response and execute the test by calling our handler. As you can see, we don't have to set up any kind of test infrastructure, and the test is really fast. Want to test your HttpClient? It's also of type HttpHandler, so your workflow is the same. There are a lot of other goodies, like approval testing, but I will cover that in a dedicated article about testing.
Overall, this is great when using Test-Driven Development: You start by writing a test, you extract the handler to use it in multiple tests and then just extract it from src/test/MyTest.kt to src/main/MyHandler.kt. All while having a quick feedback loop. Kent Beck would be proud.
Where to start?
As mentioned a few times, the actual documentation is great. Go take a look at it! It's pragmatic, well written, and an easy read.
Besides that I also mentioned that there is video material out there. My favorites are:
- KotlinConf 2018 - Server as a Function in Kotlin by Ivan Sanchez & David Denton
- David Denton, Ivan Sanchez — Writing test driven apps with http4k
- Introduction to http4k - Dimitry Kandalov
If you want to play around with the library, you can simply add the (small) dependency to a project of yours and start right of. If you want to have some things pre-configured, you can visit http4k toolbox and have a start.spring.io-like experience.
What's next?
I tried to show you some small bits of http4k, but I also really want to give you a more in-depth view on it. That's why there will be follow-up articles about different concepts and workflows.
In the next one we will look into HttpHandlers, Filters and Routers.
Stay tuned and have a good day,
Philipp