Let's look at ktor, a new web app framework being developed to take advantage of all the awesome that Kotlin has to offer.
In the spirit of the Christmas season I figured we could explore this framework by building a Christmas wishlist web service.
The framework is still under heavy development as it heads toward a 1.0 release but it already looks very promising and things are stable enough for us to do something simple in.
Ktor as a framework operates on the idea that your web application can be described as a pipeline made up of a series of "features" that you can create and install in the pipeline. Each feature has access to the
ApplicationCall object which is your execution context. The
ApplicationCall has access to attributes that you can manipulate to pass data from one feature to another. It also has access to the
ApplicationResponse objects. Despite the framework not yet being 1.0 it already includes some very handy features that we will make use of in our service.
First, let's look at the gradle file that we'll need to create for our application.
At the top of the file we simply declare our maven group id and version number. Next we define our buildscript block. Here we define some version string variables to make keeping our dependencies up to date easier. We also add the kotlin gradle plugin, the shadow plugin, and the junit platform plugin to our classpath.
The kotlin gradle plugin will help teach gradle how to build our kotlin project.
The shadows plugin will make sure we can create an executable jar file with all of our dependencies baked into it and finally the junit platform gradle plugin will be used by our test framework
After that we apply the plugins that we need for our project to build sucessfully. A few things worth noting here is that we let the junit platform plugin know that we are gonna use the
Spek engine. We also configure the shadowJar plugin with a few things and then make sure that coroutines are enabled.
The last part of our build script is where we will declare our project's dependencies. No big surprise that we'll need the kotlin standard library. We are also adding some ktor libraries that give us a few additional features to use in our app as well as gson for json serialization/deserialization, logback for logging, and spek for testing.
Now that we have our build file setup we can setup our application and install some default features that you'll probably want in any real world service you end up writing.
We declare a main function so that we have an entry point into our application and then we call the
embeddedServer function. We tell that function we would like to use Netty as our embedded server, that we wish to run on port 8080.
Then we pass a lambda configuration block that will install the features we want to use as well as configure our routing. We'll talk more about routing here in a moment but first lets look at the features we install in our pipeline first.
The coolest thing about these is that we didn't need to create them ourselves. These are either already part of the ktor framework or available via maven as artifacts. The first feature is the
DefaultHeaders feature. It makes sure that each response has a few built in default headers added to it, as well as allowing us to pass a lambda that adds a few of our own customized headers. For our project we added a
Seasonal-Greetings header and give it the value of
Merry Christmas. Now each response our service returns will have that header automatically.
After that we install the
ContentNegiation feature and configure it with gson and pretty printing. This means that gson will automatically handle our serialization/deserialization of json and the responses that we spit out we will be easy to read since we have set pretty printing.
If you've ever gone through the hassle of setting an api up to handle CORS you'll be pleased to note that the next feature we install is the
CORS feature. It will automatically handle making sure that our web service can handle
CORS requests, no configuration from our side needed.
Compression features give us easy to follow traces and compressed responses respectively.
The last two features that we install
Routing work together to define our application's routes. Notice that in our gist we haven't done that yet.
If we weren't using the
Locations feature we could just start defining routes in that block right now, but for even the small number of routes our christmas list service will have that becomes tedious and messy, for a real application it would be a deal breaker in using ktor.
Luckily we have the
Locations feature to help us out. This ktor extension will allow us to declare our routes at the top of our Application file and then implement them in separate files.
From the gist above you can see that we just need to use the
@location annotation on top of a class declaration. We put the resource path in our annotation and the class declaration is used below in our
install(Routing) config lambda. We can declare route parameters like our
userId in the class declaration underneath the
location annotation. We will need to write an extension method on the
Route object before we can use it in our
install(Routing) block. Here is what that looks like for our
We implement our
getUser call logic by first getting the userId our of the call parameters property. If it is missing then we will simply respond to our client with a bad request, however, if it is present then we can use it to retrieve our user object from the UserDao and then simply call
call.respond(user) to send it back in json format thanks to our installation of gson for content negotiation.
Thanks to gson again, we are able to get a user object effortlessly out of our call in the PUT request handler.
call.receive<User> will give us a user instance that we can then use to update our UserDao with.
That covers our introduction to ktor. Hopefully this post has piqued your interest in ktor and will make you consider it for your next web service.
ktor is also great for creating full MVC style web apps too, it even includes a feature that gives you FreeMarker template support out of the box.
I've included a lot of links below for further reading on everything ktor is already capable of. Merry Christmas!