Evojam

View Original

Kotlin on the Backend Without Spring?

Bored with the constant use of Spring in commercial projects, I decided to investigate the use of an alternative tech stack.

One of the prevalent alternatives is Micronaut. We have already tried it out at Evojam and even successfully implemented it in production in a few microservices.

That made me think —  what if we use something even smaller? As we are starting to use Kotlin instead of Java boldly, I couldn't help but consider using Ktor.

The minimal requirements that I set for this experiment are as follows:

  • easy to implement endpoints and test HTTP layer;

  • a way to inject dependencies into classes;

  • an alternative for Spring repositories with easy saving and fetching;

  • easy to implement JWT authorisation;

  • handling environment variables; and

  • a possibility to run a container.

Alternative stack

  • Ktor for handling HTTP

  • Koin for handling DI

  • KMongo for working with a database

Stack share comparison

Image source: https://stackshare.io/stackups/ktor-vs-spring-vs-micronaut

As you can see, Ktor is way less popular than Spring. Micronaut, on the other hand, has similar popularity.

Booting up

In Spring, there is an easy way to start a new project: https://start.spring.io.

Instruction on how to start with Ktor quickly: https://ktor.io/docs/intellij-idea.html#install_plugin

1) Install the Ktor plugin.

2) Create a new project in IntelliJ.

You can also manipulate an existing Gradle or Maven configuration:

https://ktor.io/docs/gradle.html#create-new-gradle-project

HTTP

“Routing in Ktor is organized in a tree with a recursive matching system. The Tree is built with nodes that contain selector and optional handler. Selectors are used to pick a route based on request.”

(Source: https://ktor.io/docs/resolution-algorithms.html)

See this content in the original post

Similar syntax is also possible in Spring 5 if you want to write in a more functional or reactive way.

See this content in the original post

But it’s quickly starting to be ugly when you want to add more routing.

See this content in the original post

Dependency injection

Dependency injection is handled differently in functional languages.

To avoid feeling shocked due to Spring habits, we will use the Koin library to build something "Spring-ish."

Creating singletons:

See this content in the original post

Injecting one singleton into another:

See this content in the original post

Using it in routing:

See this content in the original post

E2E Testing

Ktor provides the withApplication method for testing purposes where you can use TestApplicationEngine to "hook directly into internal mechanisms and processes an application call."

See this content in the original post

I pulled out HTTP calls and JSON converting methods into another object to have the E2E test quite readable.

See this content in the original post

Security

Working with Ktor is based on extension functions, which may initially shock someone unfamiliar with Kotlin, but the concept is straightforward and explained here:

https://kotlinlang.org/docs/extensions.html

Add routing to application:

See this content in the original post

So you can later invoke the function while initializing the application:

See this content in the original post

Similar to the above, we can add a security configuration:

See this content in the original post

That is presented, for example, like that:

See this content in the original post

By the auth-jwt name, you can then use auth configurations in routing:

See this content in the original post

Properties

If you are using the HOCON file (more about that here: https://ktor.io/docs/configurations.html#hocon-file), you can easily add new environment variables:

See this content in the original post

This JWT secret is fetched from properties in Application.module() like this:

See this content in the original post

Database

As mentioned before, I used KMongo to work with persistence.

It may be a bit of discomfort for those working only with Spring Data Repositories But if you’ve used MongoTemplate or MongoOperations, you’re good as it looks quite similar.

See this content in the original post

Of course, this is just an example, and I propose to pull the configuration into a separate module.

Testing persistence layer

I like working with flapdoodle. It works fast and simulates a real base quite well.

Unfortunately, unlike Spring, you have to configure it ourselves.

Fortunately, it is not very complicated, and you can do it, for example, in this way:

See this content in the original post

Containerizing

Nothing crazy here. Here’s the way I containerised the application:

1) I added a plugin application in gradle.build.kts:

See this content in the original post

2) Then, I used the command for package build:

See this content in the original post

3) In docker, I added instructions to pull the package and boot up the application:

See this content in the original post

There is a few other ways of doing it, and they’re described right here: https://ktor.io/docs/docker.html

Pros & Cons

In conclusion, I believe that the application has been created quite “Spring-ishly.”

Thanks to this, developers accustomed to Spring won’t die of shock! :)

Pros

  • The app starts in 0.4s compared to ~ 4s in Spring.

  • The syntax is quite similar to functional writing in Spring but more readable.

  • You can compose an application from libraries, e.g. using Koin for DI and KMongo. If there is a better alternative, it should not be impossible to replace it.

  • Pleasant documentation that allowed me to configure and familiarise myself with the topics mentioned above in about eight hours.

Cons

  • Embedded MongoDB firing is not automatic on context triggering like in Spring — you need to configure it yourself.

  • Ktor 2.0 will probably come out soon*, which may introduce breaking changes — until then, I would rather refrain from implementing it in production.

Things that I didn’t check out, but you should:

You can find the codebase right here: https://github.com/milunas/kotlin-without-spring.

I encourage you to download the API from GitHub and play with it!

* Actually, it’s already here! :)

https://blog.jetbrains.com/ktor/2022/04/11/ktor-2-0-released/

See this gallery in the original post