Last week ScalaTimes presented Creating a TODO application using Akka HTTP and Slick, which encouraged myself to do the same type of starting tutorial in Finch.
Finch is a project written by people working mostly on Twitter and it is a TypeLevel incubator. What I like the most about Finch is its simplicity by using pure functional programming constructs and highly efficient libraries underneath.
Personally, I have used Finch in various small projects and once in a very serious project / environment. I always found it very easy and simplistic even though it is a very capable library.
Finch is fast, just look at How Fast is Finch? You will discover that it is the second faster HTTP library written in Scala.
Let’s focus now how to create a simple Rest API using Finch.
Our build.sbt file looks like follow:
We are only adding the the Finch libraries and Circe for JSON serialization/deserialization.
Now, we need to create our first endpoint to be served.
Let’s start by something very simplistic and then grow from there. First, we can define a hello
endpoint.
In here, Endpoint
is, in my opinion, the most important construct we are going to find in Finch.
The idea behind Endpoint
is that it represent a serie of transformations as follows:
We get a Request
and transform it into some type A
. Then we apply some business logic to the input in an asynchronous matter just to finalize with another transformation into the desired Response
.
In our example, our Endpoint
is a get
which URL is hello
and the business logic to be applied is just returning hello to you!
.
Now, we need to run the server and expose the hello
to be used.
We have only created an application that we can be executed, and we have create a server
to connect to localhost:8080
then it serves the hello
Endpoint
as a Finagle Service.
We can run the application by doing sbt run
or java -jar <the application.jar>
. Once running we can send a request to the endpoint using our terminal: http localhost:8080/hello
and we will get back:
For this example, we are going to use a storage abstraction that can be implemented in multiple ways, in particular we can create one that talks to an in-memory datastore.
Item
is the type of objects we are going to be sending and receiving to our API and Storage is the one in charge of saving / retrieving them.
Our in-memory implementation is as simple as this.
Our first endpoint will be todos
that retrieves all items available in the storage.
We can see how simple that is! It just does a storage.getAll
call. This endpoint will respond to GET /todos
URL in our server.
Now we can define a new endpoint to add todos that will respond at POST /todo
This one is a little more interesting to look at. Because it is a POST
we need to extract the Item
from the body of the message. However, there is a gotcha, the Item
cannot be created without an UUID
and that is what the postTodo
function does. In other words, we extract the taskName
from the POST
message body and create an Item
object with it and a random UUID
.
The idea is that you don’t need to have a different Item
model without the id to be sent on the requests.
Then function storage.add(item)
does not really care about the id, it just add a new Item
to the storage and returns the recently created one (it has its own new id).
Once the Item
has been added, we return the recently created one.
We can test it from our terminal by doing
which returns
Retrieving a particular Item
is an easy task. We only need the id and then we can ask for the storage
to get it for us.
We can test it by doing:
Which returns the same todo we inserted before.
Notice that if the item is not in the storage, .fold
will return NotFound
HTTP message back to the client.
In the same way we can do deleteTodo
We look for the Item
with the given id. If it exists then we delete it. Then we return accordingly using .fold
.
The entire TodoService
code looks like this.
We need to expose all these Endpoint
s from our application. Finch uses Shapeless to compose Endpoint
s and taking a closer look to the TodoService
we can see that this composition is happening in the TodoService.api
.
Let’s look how our application can be rewritten to serve the TodoService
.
Notice that .api
is the combination of every single Endpoint
.
This composition is done using Shapeless and it is basically saying that the incoming request must match one of the endpoints.
Finch is an HTTP library that presents an easy way to define endpoints and to compose them into a more complex API. Finch allows us to write purely functional code for the web that is able to server request at hight rates.