Building Backend in Ktor

Choosing Ktor as backend framework of choice

Photo by Taylor Vick on Unsplash

My Story With Kotlin

When Google announced Kotlin to become the first-class citizen for Android application development, my opinions were mixed. Sure, it promised a lot, but Java seemed to get the job done, there was absolutely no need to migrate, and even after doing so, the language did not completely persuade me into being an adequate alternative.

This all changed when Google began pushing forward Kotlin-first Jetpack libraries which not only accelerated development, they began exposing some of the Kotlin’s greatest strengths.

Sure, if you look at Java today, it looks nothing like Java 6 which was around when I began working on Android. Imagine my surprise when lambdas came in version 8, or when functional-style operators for collections came with the same release, or when collection construction became easy with new helper methods in Java 9 or how interfaces suddenly got private members, or the mind-boggling change when in Java 10 the var keyword dropped in from the left-field, then nothing too significant until version 13 when switch became suspiciously similar to Kotlin’s “when” statement with the introduction of arrow syntax (and that finally got standardized in version 14).

But on Android, the latest version remains version 8. We are missing so many modern features!

Kotlin’s unique philosophy inspired a radical change in the way ex-Java developers write code: suddenly, your utility functions could be declared as top-level, solving the issue of encapsulating everything into a class, even though you’d only use a single static method from there, not to mention extension functions which came in handy when writing repetitive blocks of code for your Activity or Fragment classes… Of course, I’m only scratching the surface here.

The biggest change was the introduction of coroutines, lightweight threads which factored a great improvement to AsyncTask . Combined with LiveData , they represent one of the fundamental building blocks of a modern, asynchronous, style of programming for Android. As a side note, Java is, however, pushing forward Loom, which might represent an answer to Kotlin’s coroutines.

Kotlin promotes itself as being a general-purpose programming language targeting a variety of platforms, just check its official website! And this is where the story with Ktor begins…

Before Ktor Was Node

One of the first experiences with backend development for me was Node. Granted, the use of JavaScript, a relatively easy language to pick up, seemed like a great way of quickly writing some server-side code. And this is definitely true, with frameworks such as Express, one can quickly bootstrap a simple server. In the past 10 years, the entire Node ecosystem arose to become one of the de facto backend stacks.

Surely, Microsoft’s TypeScript greatly improved developer experience, at least initially, allowing developers like myself coming from strongly-opinionated object-oriented Java background, to migrate with ease. Suddenly, you could translate a hefty number of known OOP principles to Node.js world, and the TypeScript compiler would make sure it’d produce a highly-performant JavaScript code.

Here’s an example of how TypeScript would compile into ES5.

The next step was to find a framework that would offer a bit more out-of-the-box than its competitors. Meet Nest.

I’ve used Nest in my previous role as a backend developer, and due to the extensibility it offers, you could virtually do anything: you want a simple in-memory database like Redis? You have a module for it. Pick a database of your choice, there’s a module that helps you get set up quickly. Are you jumping on a GraphQL bandwagon? Nest has an entire section of their documentation dedicated to helping you get set up.

For a custom backend, whether simple or enterprise-grade, I’d pick Nest without a question if your team consists of JavaScript/TypeScript developers.

In my first post related to Flutter, I mentioned architecture. My personal belief, whether you agree with me or not, is that good software requires exquisite architecture. Loosely coupled and easily testable pieces of software will consequently be maintainable and scalable in the future.

Nest comes in with the clear separation of business logic (providers), handling requests/responses on particular routes (controllers), and binding these two into modules that enable Nest to organize the application structure.

Once familiar with the concepts, you get thrown into the endless documentation, from fundamentals to recipes… Despite the superb docs and active community, there was still something missing.

The developer experience is ultimately what drew me away from the Nest, and all Node-based technologies in the first place. After the initial hype of writing typed code for an inherently dynamic language, you become aware of limitations and philosophies that ultimately repudiate their promises.

Quoting Ryan Carniato:

You can spend hours perfecting an elegant solution to your types without shipping any new workable code. It makes you feel really good and clever when you get it right. It is metaprogramming to the highest degree.

So, What Happened? TypeScript’s type system is a compile-time feature.

Despite Nest being an amazing framework to operate with, the drawbacks of an (underhood) dynamically typed language ultimately made me reevaluate my stance on the tech stack I’d be willing to use in the future However, I’m not saying that everything related to Node is bad, the message I’m trying to convey here is that you should pick the right tool for a task, and make sure that you actually know how to use it.

The former point oftentimes goes intentionally unheard in the developer community, we all love to experiment around with our favorite languages and stacks, compose frameworks on top of frameworks, frameworks for frameworks… What we seem to lack is a sense of understanding: clients couldn’t care less about that.

At that point, I had to decide whether or not to continue working in TypeScript, and risk my love/hate relationship influence my productivity, or maybe switch over to “known” waters where I feel like at home.

Well, judging by the title of this article, you can kind of guess which route I took…

From Java To Kotlin

If you have been following articles similar to mine in the past, you understand that idiomatic Kotlin syntax takes time to fully comprehend and implement correctly. It moves away from what “old” Java embodies; an extremely verbose and feature-packed object-oriented language, while in contrast, Kotlin’s fluid paradigm ranging from object-oriented to functional, leads to a cleaner and more legible code. Initially, it causes developers to “write Java code in Kotlin”.

Every article out there (that I have personally read) will eventually begin talking about Kotlin’s outstanding null safety, etc.

But just because you’re beginning to fanboy over a new language that is reducing your typing time by 30%, you should not throw Java in the bin of old languages that ought to get instinct.

In contrast, in 2020, one of my personal goals is to revisit Java, but this time, revisit its newer versions because the language has catered for me when I was spending time learning object-oriented paradigm, spent countless hours studying data structures and algorithms…

Java will always hold a special place.

Kotlin and Ktor

In comparison to bloated frameworks such as Spring, Ktor lacks on the opinionated front, as it gives you a web server to work with, HTTP request dispatching, and then the rest is up to you. The extensibility of Ktor comes in form of modules, which are methods that configure the server and offer installation of features.

That leaves the architecture decisions to the developer, which is a double-edged sword; is he/she capable of making sound architectural decisions? In the majority of cases, no.

The community seems to be fixated on explaining 15 liners instead of giving a bigger picture. The existing starter applications that appear on the front page follow no best practices, separation of concern is a nonexistent idea, inversion of control through dependency injection is a mystery… Of course, I’m not saying that my architecture is perfect, but at least it gives a distinction between routing (controller) logic, services (repositories) which act as wrappers around provides, just like I would otherwise architecture a Nest application. When similar design choices are implemented, working with Ktor becomes working with Nest, but in Kotlin, and with an improved typing system…

Planning architecture bottoms up.

Simple sketch for a given backend architecture

Personally, I look at the backend application architecture similarly as I used to look at building native Android apps before MVVM came and disrupted everything. A lot of people might compare that to MVP/MVC but without the “view” part, as there are no UI elements on the backend side, so we might replace “view” with Application , Ktor class that handles top-level organization (installation of modules, configuring pipelines, …)

At the bottom, we have providers that give raw access to resources (networking, databases, etc) and act as single sources of truth for data origin. They are usually injected into repositories which, if using Koin, might be represented as an interface and its implementation. Repositories are wrappers around providers, so they are responsible for things such as validation. One or multiple repositories are then injected into a service that contains all the business logic. Sometimes, especially in simple applications, we can get away without the need for a service, so a repository is sufficient. Lastly, there is a route controller, which I like to model as extension functions around Route class.

These routes can now be mentioned inside the application configuration, so code becomes extremely readable when you introduce features such as authentication.

I’m not going to lie, using Kotlin outside of Android development for the first time might feel a little bit weird, especially if you’ve never explored the language outside of what’s necessary to get a mobile app up and going.

My personal favorite analogy for comparison of Kotlin inside Android versus inside Ktor is basically buying a Tesla in a country like Slovenia where the charging stations are slowly spawning while taking the same car with you as you move to Silicon Valley, where the same charging stations are a common sight.

Once you realize that backend applications run on servers much more performant than my Xiaomi Redmi Note 8 Pro, you might think that sky is the limit, but there really isn’t that much of a mental leap to be done. If you’re capable of writing performant Android apps, your Ktor apps are going to run smoothly as well.

I hope to see more of you jumping to Ktor as well!

Senior Software Engineer at IPM Smart Community, mobile and web full-stack developer, instructor, consultant, Linux dude.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store