Implementing a todo app with a Finagle HTTP Service
Posted by
Niklas Leopold
on January 03, 2021 ·
19 mins read
About the post
In this post I will try to use vanilla Finagle to implement the classic a Todo rest service.
When I was writing this I discovered that one often use Finch or Airframe (which are addons to Finagle) when createing a Rest api, however I will try to only use Finagle.
I will look at Finch in a future post.
This is not an expert post about Finagle and it’s echo system, it’s my attempt to get an understanding what Finagle is all about.
If you find this interesting I have written another post where I investigate the Finagle client.
Creating a Hello world Finagle HTTP service
Ok, to get started, lets implement a small Hello world service:
This is fairly straightforward, we define that the server should listen on port 8080 and we also create a service that handles the incomming requests.
The most important thing to notice is that the result from the service is a Future[Response]. This is nice since we can write in a non blocking fashion.
After the server is starting and I go to localhost:8080 in my browser I get the pretty unsurprising result:
The todo app
The Todo app should support the following API:
It should be possible to send in a request body for the POST and PUT methods:
The id is optional.
Extracting the request body
To be able to create a Todo I need to extract the information from the request body. You can get request body as a String from the http.request.
I feel that working with a String is pretty low level, I would prefer to get the request body as an object instead:
I could not find any built in support in Finagle for transforming the json payload to Scala object so I decided to use the bijection-json library.
The library give you the ability to define how you transform the payload to an object (Todo in this case) and back again (i.e. transform a Todo to a String).
To be able to handle a Bad request the payload is transformed to a Try[Todo].
I also wrote a small convenience function to make things easier:
We are using the bijection library here: req.contentString.as[Try[A]]. The result from the call will either be a valid object of type A or a failure.
Create and get a Todo
Below is a code snippet from the service that handles the API methods for listing all Todos and to create a new Todo:
First we match on the path and on the method to resolve which AP method that is called.
The state for the Todo app is inside an Actor so to handle the requests we send messages to the Actor. Finally we us asBody another convenience function that get us the response body as a String.
The service
So here is the whole service:
For me it’s kind of annoying that I had to include the error handling for when calling unknown resources in the service (on three different places):
In fact I briefly considered to implement yet another convenience function so that it’s possible to compose the service to hide this, but then I have almost implemented my own small REST wrapper for the Finagle http service and that’s not what I’m after.
Lets investigate what help we can get from Finch in another post instead!
Error handling
The different errors are handled in the toResponse function:
The Actor
For completeness, here is the implementation of the TodoActor:
Since this is not a post about Actor I will not dwell on the code, I will only point out that I choose to always wrap the payloads in the Replies in a Try to get an uniform API. As an example consider ListTodos, this will not fail, however I still wrapp it in a Try. This to simplify the logic in the service.
Conclusion
When implementing the service I could not find that many good examples of how to develop a Finagle http service. I also found that I had to write more boiler plate code that I would have expected. It could be that I have missed something.
I got the impression that most people use Finch or Airframe. In the next post I will investigate what kind of help I get when introducing Finch or Airframe.