Create a Kotlin/Native Web Server With Ktor and SQLDelight PostgresSQL
Kotlin server side — but without a JVM

When reading Kotlin, you probably think of Android development or an alternative programming language to Java running on the JVM (Java Virtual Machine). But actually, the ecosystem is much larger than that. As more and more people love to write business applications in Kotlin, JetBrains (the company that created Kotlin) started to develop other targets besides Android/JVM. This includes Kotlin/JS (JavaScript), Kotlin/Native, and soon-to-be-released Kotlin/Wasm.
Kotlin/Native enables us to write programs directly targeting different operating systems (Linux, Windows, macOS, iOS, Android) without needing a JVM. It also can call C libraries from within the Kotlin code base. But without a JVM, we can also not use any libraries from the Java ecosystem.
Setup
In this article, I want to showcase what is already possible with Kotlin/Native on the server side. For this, we will implement a web server application that can interact with a PostgreSQL database for persistence to serve a REST API returning JSON.
As the web framework, we will use Ktor, which is a modular alternative to other common frameworks like Spring Boot or Quarkus. Ktor is developed directly by JetBrains and written 100% in Kotlin. They just recently added support for the Kotlin/Native target (not yet available for Windows). This makes it a perfect match for our use case.
Most services need some persistence layer. The most common approach is a database. And here, we reach a bit of an early adopter problem. As we are not running on the JVM, we cannot utilize its JDBC driver. Additionally, Kotlin/Native is quite new and was previously without support for a web framework. Because of this, most database driver implementations are targeted for Android usage providing SQLite compatibility.
Luckily, the community has already implemented its own solutions until bigger Kotlin ORM frameworks like Exposed start supporting Kotlin/Native. We are going to use the SQLDelight PostgreSQL Driver from Philip Wedemann. As of writing this article, this currently seems to be the first and only implementation to connect to a server database from Kotlin/Native. As you might have guessed already, this is a custom driver implementation for SQLDelight, which is an ORM framework to run type-safe SQL queries in Kotlin.
Project Configuration
As the build tool for our project, we will use Gradle and the Gradle Kotlin DSL to write our Gradle configuration as Kotlin code (see below).
We need multiple Gradle plugins for our server (ll. 5–9), the first being the Kotlin Multiplatform Plugin to enable us to configure the different target platforms (ll. 27–35). For simplicity reasons, we will put all code into the commonMain
module.
To enable our Ktor server to encode and decode JSON, we need the KotlinX Serialization Plugin, a serialization solution provided by the Kotlin team (an alternative to, e.g., Jackson in Java).
We have to add the SQLDeligth Plugin for database access, which enables us to configure our database dialect and driver (ll. 18–24). It also adds a code generation task to the build pipeline, which creates code for type-safe database access.
As Ktor supports multiple server engines, we must add ktor-cio beside the ktor-core dependency. CIO is currently the only engine supported for Kotlin/Native and is written in 100% Kotlin with a focus on coroutines. To configure our PostgreSQL access, we also need the SQL Delight Postgres Native driver as a dependency.
Because of a bug with KotlinX Serialization CORE and JSON versioning, it is necessary to force a specific library version (otherwise, we would not have to add these dependencies in the config explicitly), ll. 55–62.
To utilize the PostgreSQL driver, one first has to install libpq
on the local machine or docker container (it might become obsolete in the future).
# MacOS
brew install libpq
# Linux
apt-get install libpq-dev
SQLDelight framework requires us to set up special sq-files (e.g. users.sq
not .sql
) which define our table and SQL queries we want to execute in a type-safe way. This file has to be in /src/commonMain/sqldelight/com/nativeserver/
, as defined in our Gradle configuration of the SQLDelight plugin (ll. 21).
SQLDelight files always have to be in
commonMain
module otherwise the plugin will not find them. All other code can be in a module of your liking e.g.nativeMain
.
The first part of the file contains the code for creating our sample table users
. The second part defines a custom function selectById
that will later be an accessible type-safe in Kotlin code.
To generate the SQLDelight Kotlin code, we have to run the following Gradle task:
gradle :generateCommonMainNativePostgresInterface
Server Code
The actual server code located at under /src/commonMain/kotlin/com/nativeserver/main.kt
(see below) looks mostly exactly like any other Ktor server, written in Kotlin/JVM. One difference is the special PostgresNativeDriver
which is used to connect to the database (ll. 16–25).
To keep the example short, it only contains one GET endpoint under /users/:user_id:
which returns a user saved in the database if existent (ll. 33–39).
Since we are not on the JVM, we cannot just use System.getenv("...")
to read environment variables, which might be needed to configure our server. For this use case, Kotlin/Native offers a POSIX API to access system resources. An example of reading an environment variable can be seen below:
Building and Running
To build our Kotlin/Native application, we can utilize the following special Gradle tasks from the Kotlin Multiplatform Plugin:
# Buid
gradle :compileKotlinCommon
# Run Debug
gradle :runDebugExecutableCommon
# Run Release
gradle :runReleaseExecutableCommon
# Run test
gradle :commonTest
# Or always all test
gradle :allTests
The build executable can be found under /build/bin/common/releaseExecutable/<PROJECT_NAME>.kexe
.
In case your code lies in another module e.g.
nativeMain
replace common with native in the commands
GitHub Repo
A more complex example server application, including tests can be found in the following repository:
Conclusion
As shown in the article, creating a simple web server in Kotlin/Native with basic requirements like a database connection is already possible. However, the project is still in a very early stage. This means the performance is much worse than compared to a similar application with Ktor running on the JVM.
The development experience is also terrible, as debugging is not working in IntelliJ, and the compile time is extremely slow, even for small projects. JetBrains is aware of these problems and delivers improvements with every Kotlin release.
Last but not least, one has to consider the currently nonexistent tooling ecosystem around server-side Kotlin/Native. For example, no libraries are available for observability or databases access besides SQLite and PostgreSQL. These are things we easily take for granted in the JVM ecosystem but must be re-implemented for the native target.
I am still looking forward to the future Kotlin/Native, and I am sure the JetBrains team will deliver awesome things in the coming years.
If you enjoyed reading this article, please stay tuned for more about Kotlin, Java, Go, and the cloud.