A Brief Introduction to Using Kotlin for Back-end Development

I’m not required to introduce Kotlin to experienced Android developers because, in May 2017, Google declared it the official language for Android development. Since then, it’s become incredibly popular and is often the top choice for building new, cutting-edge Android apps. Kotlin addresses many of Java’s shortcomings, leading to its adoption in new app development and the rewriting of existing apps.

Kotlin’s excellence in front-end development is undeniable, and many associate it primarily with Android. However, this article focuses on Kotlin’s capabilities as a back-end language. I’ll share my experience building a fast, dependable, and asynchronous Kotlin back end for a personal Android project. While the project itself is beyond this article’s scope, I’ll explain why I selected Kotlin and believe it excels in server-side applications and REST API development.

Why Kotlin?

Let’s revisit the beginning of my journey. I’ve always had entrepreneurial aspirations, believing that creating something independently was the first step. It didn’t need to be groundbreaking or massive – just something small for personal use, perhaps shared with family and friends. With a decent idea, I dove into implementation, starting with tool selection. The right tools, after all, can save significant time and resources.

As a primarily Java developer, I’ve built numerous back-end systems and REST APIs using Java and Spring, considering them excellent for such tasks. Java, comprehensive on its own, becomes even more potent when paired with Spring.

However, there’s one minor drawback: verbosity. While Spring and recent Java versions help, boilerplate code remains an issue. As someone once told me, the most secure, reliable, and bug-free code is often the code left unwritten. Take this basic Java class as an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Person {
	private final String name;
	private final int age;
	public Person(String name, int age) {
    	this.name = name;
    	this.age = age;
	}

	public String getName() {
    	return name;
	}
	public int getAge() {
    	return age;
	}
}

This seems excessive for simply defining a class with two read-only fields. It’s worth noting that the constructor and methods were auto-generated. Yet, during code reviews, one still scrutinizes them, just in case. Sure, libraries like Lombok can shorten this, but wouldn’t a native solution be preferable? Let’s examine the same class in Kotlin:

1
2
3
4
class Person(
	val name: String,
	val age: Int
)

Significantly shorter and cleaner. The val keyword makes the variables final, while the compiler generates the constructor and getters. For a mutable person object, simply replace val with var – one letter change achieves mutability.

Another aspect I dislike in Java POJO classes is overriding equals() and hashCode(). Usually auto-generated, they still demand review for peace of mind. Thankfully, Kotlin handles this too. Changing class to data class automatically provides equals() and hashCode().

1
2
3
4
data class Person(
	val name: String,
	val age: Int
)

While I appreciate Java, I wanted to rapidly create a minimum viable product. In software development, writing less code is often the quickest route. Thus, my search for a superior back-end language continued, leading me to Node.js. It boasts significant advantages—a few lines set up an Express server listening on port 8080, responding with Hello World! to GET requests.

1
2
3
4
let express = require('express')
let app = express();
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(8080);

Easy, straightforward, and fast—exactly what one expects from a popular language. Although I enjoy JavaScript, its dynamic typing gave me pause. While dynamic typing suits the front end, in my experience, static typing on the back end provides additional confidence, reducing the risk of runtime crashes due to type mismatches. And honestly, when your back end serves hundreds of thousands, such crashes are highly undesirable. However, Node.js offered one feature I wanted to retain: the ease of writing asynchronous code and services.

Considering these requirements, I opted for Kotlin for my Android back end as well.

Kotlin: A Quick Overview

For the uninitiated, Kotlin is an open-source, statically typed programming language supporting both object-oriented and functional paradigms. With syntax and concepts similar to C#, Java, and Scala, it primarily targets the JVM but also offers variants for JavaScript and native code. Kotlin/JVM’s compilation to Java bytecode makes it readily accessible to back-end engineers with JVM experience.

As its official page states, Kotlin prioritizes practicality and incorporates decades of language development best practices, rather than striving for novelty. While usable with any Java IDE or from the command line, I personally advocate for IntelliJ, which is actively maintained by the JetBrains team. For those starting with Kotlin, IntelliJ’s community version suffices. Kotlin’s three most compelling aspects, in my view, are its conciseness (significantly reducing boilerplate code), safety (including built-in null safety), and interoperability (allowing seamless use of existing JVM, Android, and browser libraries).

Kotlin Coroutines

Everyone desires fast and responsive services. Multithreading is one way to maximize server capacity. Java’s approach, however, is rather cumbersome. Beginners often struggle to differentiate between extending the Thread class and implementing the Runnable interface. To add to the confusion, they’re advised to always start threads with the run method, never the start method. Or was it the other way around? Ultimately, they’re told to avoid manual thread creation due to its overhead and to use thread pools instead. Simple, except it isn’t.

Kotlin offers a more elegant solution: coroutines. Put simply, coroutines enable the writing of asynchronous, non-blocking code in a highly readable manner. The key concept is “suspendable functions,” allowing computations to pause at a certain point and resume later. The beauty lies in the minimal impact on the programming model. Writing non-blocking code feels remarkably similar to writing blocking code. Let’s compare two examples:

1
2
3
4
fun sendRequest(): Int {
   /* do some heavy work */
   return 1;
}

This demonstrates a blocking function. The thread executing this code snippet won’t perform any other tasks until the function returns, which, in the case of an API or database call, could take several seconds. Blocking our thread while waiting for another service is undesirable. Let’s transform this function into a non-blocking one.

1
2
3
4
suspend fun sendRequest(): Int {
   /* do some heavy work */
   return 1;
}

This showcases converting our method into a suspendable, non-blocking function. For instance, if the “heavy work” is a simple delay() of 10 seconds, the executing thread continues with other tasks for that duration, resuming the function’s execution once the 10 seconds elapse. Achieving non-blocking code with a single keyword is remarkably efficient.

Asynchronous Service with Ktor

REST API development often involves additional steps like starting an embedded server or parsing requests, tasks nobody enjoys doing manually. Java has Spring Boot to streamline this process, and Kotlin, thankfully, has Ktor. Ktor is a web framework designed for building asynchronous servers. As its website proclaims, Ktor is “easy to use, fun, and asynchronous.” While “fun” is subjective, let’s demonstrate its ease of use and asynchronicity with a code snippet.

1
2
3
4
5
6
7
8
9
fun main() {
	embeddedServer(Tomcat, 8080) {
    	routing {
        	get {
            	call.respond("Hello world!")
        	}
    	}
	}.start(wait = true)
}

This example presents a fully operational Kotlin Ktor server running on an embedded Tomcat server. It listens on port 8080 and asynchronously responds with “Hello world!” to GET requests—all in less than 10 lines of code.

Ktor’s capabilities extend far beyond this basic example. A comprehensive overview would require a dedicated article, but among its many strengths is the simplification of login and authentication. You can delve deeper into Ktor’s server-side features and configuration here.

Other Benefits of Kotlin on the Back End

One notable advantage is the ability to utilize Java libraries within Kotlin. And believe me, a wealth of excellent third-party Java libraries can make your life significantly easier. Why reinvent the wheel when a readily available open-source library perfectly suits your needs? Using these libraries with Kotlin is seamless.

Another significant benefit of Kotlin and Ktor is the extensive ecosystem of testing libraries and frameworks. JUnit integrates seamlessly with Kotlin, and Ktor complements this with its own testing library, facilitating the rapid creation of end-to-end and integration tests. You can leverage a custom test engine to run your entire application, handling requests just like the live environment.

Conclusion

As a seasoned Java back-end developer, I’ve worked on numerous server-side applications and REST APIs. However, despite my fondness for Java, I firmly believe that no single language or framework reigns supreme for every task. My philosophy is to familiarize myself with a wide range of tools and, when faced with a problem, select the one best suited to address it effectively.

As the Kotlin website states, Kotlin doesn’t aspire to be unique but rather draws inspiration and best practices from decades of language development. In the realm of back-end development, I firmly believe that Kotlin, Coroutines, and Ktor form an exceptional trio for accomplishing the task at hand. You can delve further into Kotlin and its merits as a programming language here.

Licensed under CC BY-NC-SA 4.0