How to build REST API with Arrow, Ktor and requery in Kotlin
Introduction
In one of our projects, we have decided to give a try to Kotlin programming language for our backend service. Kotlin is well known among Android Developers as an alternative to Java. It is also the language that is supported by Spring, which is a well known Java Framework. Ktor, on the other hand, is Kotlin library which can be used to create asynchronous HTTP servers and clients. We have also found Arrow— Kotlin library that implements common concepts from functional programming like typeclasses and effects. Unfortunately, Ktor is not functional and is based on suspend
functions. In this tutorial, we will show how to integrate these two libraries and make a sample REST API in a functional way.
We will use the latest version of Arrow (0.10.0), which contains IO.suspend()
function used to integrate with Kotlin suspend
functions. The final code can be found at GitHub: https://github.com/siilisolutions-pl/arrow-ktor-blog-entry. In this repository, you can also find an earlier solution with Arrow 0.9.0, which is a little bit more complicated because of the lack of IO.suspend()
function. Let’s go into the code now.
Integrate Ktor with Arrow
Let’s define a sample entity:
Next, there will be Ktor server returning static JSON content:
There is a configuration for JSON serialization (using Jackson library) and one endpoint (”/employees”
) returning a list of employees. Let’s extract data access to a separate class and use Arrow’s effect:
Kotlin does not have higher kinded types, they are emulated in Arrow with Kind<F, A>
(for specific F
we need to use For*
classes like ForIO
for IO
to be able to compile the code, e.g. Kind<ForIO, List<Employee>>
). There exists conversion functions, so IO<List<Employee>>
for the compiler is the same as Kind<ForIO, List<Employee>>
.
If we now use the above repository directly:
it won’t work since we pass to Ktor list of employees wrapped in IO
. Obviously, we can invoke it:
but this is not very elegant and if we would like to extract routing to a separate class that will be stuck to the IO
from Arrow. What we want is to transform the IO
into suspend
function that will be invoked by Ktor in an appropriate time. To make it in a generic way we will define Suspendable
typeclass and provide an implementation of it for IO
:
Suspendable<F>
is a typeclass that tells us we can transform effect F
into the suspended function which returns value from F
. We define an instance of Suspendable
for IO
as function in the companion object. This is a standard way how typeclasses are defined in Arrow (in Arrow it is implemented with @extension
annotation and annotation processor which generates code; However, we do not want to unnecessarily complicate this sample code). Implementation is trivial, since we invoke ready to use function of IO
monad (introduced in Arrow 0.10.0).
Now let’s extract our routing, make it generic and use Suspendable
typeclass:
We can now use it in our application:
Ktor is now integrated with functional IO
monad from Arrow library.
Routing class needs only the implementation of repository and implementation of Suspendable
for F
, which means that we can make unit tests quite easily using below code:
We can also make some more “layers” between repository and routing, which all will be based on F
(it can be a service layer, and/or a validation layer), and test them in isolation using any implementation of F
that will work best in unit tests.
Accessing database
Since we already have a web layer, let’s implement repository that will access a real SQL database. Time to use requery library:
We have used MonadDefer
from Arrow to delay synchronous operation and KotlinEntityDataStore
from requery that will be configured below. First, some annotation and mark interface is needed in the entity class:
All we need to change in the application is adding already mentioned configuration for requery and change the usage of IOEmployeeRepository
to DeferEmployeeRepository
. Since IO
implements MonadDefer
there is nothing we need to do here (besides passing proper typeclass instance to repository constructor).
Let’s make a configuration of requery with H2 in-memory database with some sample data:
Below you can find the updated server using the new repository.
Since requery supports Java Rx2 we can also make different implementation of the repository with Single
as our F
type:
Here, we do not need to use MonadDefer
, since Single
is already asynchronous, but we need to implement Suspendable
for SingleK
(Arrow’s wrapper for Single
from Java Rx 2.0):
Usage of suspendCoroutine
function is a way to transform the callback API method to suspend
method. Here we invoke subscribe
function from Java Rx2 Single
class and use the result callback for transformation into suspend
function.
Finally, we can use the same routing implementation with two different repository implementations:
Summary
We have created sample REST API with Ktor, Arrow and requery in a functional way. Suspendable
typeclass integrated nicely functional code with asynchronous HTTP server. Requery helped us to make non-blocking access to the SQL database. Full source code can be found at https://github.com/siilisolutions-pl/arrow-ktor-blog-entry.
Thanks to Simon Vergauwen, Bob Glamm, Anton Spaans and Paco Estevez from #arrow
on https://kotlinlang.slack.com/