This post is part of a series about http4k.
Part 1 - Http4k - A Sneak Peak
A TL;DR can be found at the bottom of the article.
HttpHandlers are one of http4k's core features: Handling a request and returning a response.
Part 1 - Http4k - A Sneak Peak
A TL;DR can be found at the bottom of the article.
HttpHandlers are one of http4k's core features: Handling a request and returning a response.
Before we dive in, let me define a scenario that we will be used as an example for all the mechanics we will implement.
Scenario
Our company needs a new vacation planning tool. Over the next few articles, we will add more and more features, but today we're starting with two simple things:
- We want to get information about how many of our colleagues are on vacation today.
- We need an option to check the absence of a specific employee for the current day.
Our First HttpHandler
The "Hello World!" of http4k looks like that:
If we now look into what an HttpHandler is, we'll find out that it's just a lambda:
That's refreshing. Just a lambda with an object of type Request as input and a Response as output. Having that in mind, we can finally start playing around with it.
Feature 1: Overall Absence of Our Department
- We want to get information about how many of our colleagues are on vacation
Let's write a test for our feature:
To start of easy, we always expect 15%. Here, I'm using Kotest assertions and its http4k extensions to validate the response. Since HttpHandler is only a lambda of type (Request) -> Response, we can simply pass in our request, and we'll get a response.
Why am I repeating that again?
Well, look closer. We have a test with no setup, just functions calling functions, but we're getting a valid Http response. All of it is running in-memory, blazing fast and easy to understand. Teaser: http4k offers a lot more when it comes to testing. We'll dive into it in another article.
Looking back at our handler, let's change some small things to fit our test:
We rename it and omit the request, since we're not using it anyways. Furthermore, we add the mentioned 15% to our body to make the test pass.
Oh, we're simply omitting the request. Overall, we have nothing that checks the http method.
Maybe we should test our handler with another Http method? Why not try them all? Let's go! Http4k provides all the necessary methods in an enum:
Junit5's Parameterized Tests come in handy here: We can just use the Method enum to iterate over its values
Running our test again, we find out that it indeed works with every method. Why shouldn't it, we didn't specify a http method type (we'll look into that later, don't worry).
Important: When performing it as an integration test (so to speak: crossing boundaries) with an http client, you will find out that the test will fail with an http method of type HEAD. You can read here why. An example for such a test can be found in the git repository.
We learned that, at least right now, the type of http message doesn't really matter. Let's inspect our request:
Hmmm, we also don't really have any kind of URI matcher in our handler.
Shall we test it? I know you would say "For sure.", great.
I told you about what to do, I told you about parameterized tests... Go on and try for yourself! You'll find a solution in the git repository.
Fast-forward: We can throw in any kind of string since the HttpHandler has yet no mechanism for it.
It might be a good idea to introduce one.
Shall we test it? I know you would say "For sure.", great.
I told you about what to do, I told you about parameterized tests... Go on and try for yourself! You'll find a solution in the git repository.
Fast-forward: We can throw in any kind of string since the HttpHandler has yet no mechanism for it.
It might be a good idea to introduce one.
So far so good. But I bet there will not always be 15% of your colleagues on vacation. Let's move it to the parameter:
This snippet will not work, since HttpHandler expects (Request) -> Response.
Thus, we will convert it to a function (let IntelliJ IDEA do the work!).
Thus, we will convert it to a function (let IntelliJ IDEA do the work!).
I chose to move it to an interface that I can easily fake in a test, so we don't have to take care of the underlying implementation.
But there are also other possibilities. I strongly recommend reading Uberto Barbini's article about the invoke function. Summed up, it's possible to have a class inheriting from a function in Kotlin! Here's an example of Uberto's version:
Feature 2: Absence Of An Employee
To get the absence of a specific employee, we have to extract some kind of information from the request.
No worries, it's simple. Inside an HttpHandler, we can query the needed parameter from the request as follows:
No worries, it's simple. Inside an HttpHandler, we can query the needed parameter from the request as follows:
That will do. But it looks kinda odd, no? A little hint: We will clean this up in the next article. To verify the provided id, we need some kind of repository.
Usually, you would want to define your own type UserId instead of using Int. Keep that in mind, we will come back to that.
Our final implementation of the handlers looks like that:
You can see that we declared employeeId as nullable. That might work, but we will learn about a better solution in the next article.
Having all handlers in a single file would quickly become unorganized. Having one point where all of them are grouped can be quite beneficial. Let's look into that.
Having all handlers in a single file would quickly become unorganized. Having one point where all of them are grouped can be quite beneficial. Let's look into that.
Routing HttpHandlers
As already mentioned, I want to group all of our requests together at one place. Http4k provides use with the routes() function that takes HttpHandlers. Perfect match for our objective.
Nesting is of course also possible:
What's next?
And that's basically it! We've made quite some progression on our way to understanding http4k concepts. Next time, we will look into Lenses. A powerful feature that I wasn't aware of at all beforehand.
If you have any kind of feedback, I would love to hear it! Was it to detailed? Should I explain things more granular? Any feedback is welcome. You can find me on Twitter or message me via email.
Stay tuned and have a good day, Philipp
TL;DR
A simple HttpHandler:
A HttpHandler for a specific http method:
Extracting a parameter from the URI:
Routing: