Using Kotlin JS: Todo Fetcher

February 22, 2025

Building a Frontend App That Paginates in Pure Kotlin

Using Kotlin/JS: Working with Axios in KotlinJS

As an experiment, I decided to set up a repo that uses kotlin/js to fetch a list of todos completely clientside.

Here is a live demo so you can see what it does; a simple fetch of todos and ability to paginate through them. Also I've added a button to filter out all of the unfinished items.

How it Works in Pure Kotlin

First, we make a fetch with axios. Axios integration with Kotlin is daunting at first but they've made it pretty easy.

The only thing I needed to use axios was a helper class and to add the dependency to my gradle file

implementation(npm("axios", "1.7.9"))
@JsModule("axios")
@JsNonModule
@JsName("axios")
external class Axios {
    companion object {
        @JsName("get")
        fun get(endPoint: String): Axios
    }

    @JsName("then")
    fun then(unt:  (Axios) -> Unit)
}

Then to actually use axios it's as you'd expect:

Axios.get(endPoint = urlEndpoint)
    .then {
        val result = it.asDynamic().request.responseText as? String ?: "Get Request String Issue"

        val todoList = Json.decodeFromString<List<TodoResponseItem>>(result)

        todoListManager = TodoListManager(fullTodoList = todoList)

        setResultList()
    }

Serializing the Request Into Data Classes

An interesting line of code here is the call to Json.decodeFromString, what that's actually doing is allowing me to deserialize the request dynamically into a data class.

This means I'm proccessing my code type safely, and don't need to use .asDynamic() past this point.

This repo uses Kotlinx.serialization to deserialize the json dynamically into a data class, it's easy to add to gradle:

plugins {
    ...
    kotlin("plugin.serialization") version "2.1.10"
}

dependencies {
	...
	implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
}

Now my data class for each Json typicode todo object looks as expected, just needs @Serializable annotation.

@Serializable
data class TodoResponseItem (
    val userId: Int,
    val id: Int,
    val title: String,
    val completed: Boolean
)

We can use TodoResponseItems anywhere in code with type-safety!

Kotlinx.html Templating is For Winners

My favorite part of this mini project is using Kotlinx.html Kotlin's html templating. This allows us to write tags enclosed in brackets, giving us a much more code like feel. For instance, our main list looks like this:


document.getElementById("mainBody")
    ?.apply {
        innerHTML = ""
        append {
            ul {
                todoTemplate(todoList = resultsByPage)
            }
        }
    }

Writing html this way is a breeze and way less prone to errors. Since the todo template is effectively a list of li items, we know it will work ahead of time at compile.

fun UL.todoTemplate (todoList: List<TodoResponseItem>)
    = todoList.forEach { todo ->
        li {
            b { + todo.title }
            + if(todo.completed) "✅" else  "❌"
        }
    }

This gives us succinct code that is also flexible and readable. I prefer this to jsx and think it's far superior.

Todo list manager

Writing the todolist manager was also a breeze. The core of my pagination logic lies with adjusting the list with a call to js .slice(). This is a nice idiomatic approach illustrating how we can still use JS api's where we want. This snippet is nearly 1:1 with pure js.

fun resultsByPage(filteredList: List<TodoResponseItem>? = null, adjustedInterval: Int? = null)
    : List<TodoResponseItem> {
    val todoList = filteredList ?: fullTodoList
    val pageNum = if(currentPageNumber <  endPage) currentPageNumber else endPage
    val interval = adjustedInterval ?: currentInterval
    //1: 0..9, 2: 10..19, 3: 20..29 etc
    val rangeStart = (pageNum - 1) * interval
    val rangeEnd = rangeStart + (interval-1)
    return todoList.slice(indices = IntRange(start = rangeStart, endInclusive = rangeEnd))
}

//A second version of this method written in more idiomatic kotlin
//the main differences being `coerceAtMost` and `subList` which I think
//make this code a bit more readable
fun resultsByPage(filteredList: List<TodoResponseItem>? = null, adjustedInterval: Int? = null)
    : List<TodoResponseItem> {
    val todoList = filteredList ?: fullTodoList
    val pageNum = currentPageNumber.coerceAtMost(endPage)
    val interval = adjustedInterval ?: currentInterval
    val rangeStart = (pageNum - 1) * interval
    val rangeEnd = (rangeStart + interval).coerceAtMost(todoList.size)
    return if (rangeStart in todoList.indices)
            todoList.subList(rangeStart, rangeEnd) 
        else emptyList()
    
}

Further down I decided to use kotlin's api's a bit more. Kotlin's api makes doing things like sorting and filtering much easier to understand than with JS clunky closure syntax.

While the difference above is arguable, writing this same snippet in JS would be ugly, Kotlin's {} bracket syntax makes it much easier on the eyes.

fun onlyUnfinishedTasks(): List<TodoResponseItem> {
    currentPageNumber = 1
    onlyUnfinished = !onlyUnfinished
    return if (onlyUnfinished)
        fullTodoList.filterNot{ it.completed }.let {
            resultsByPage(filteredList = it, adjustedInterval = it.size)
        } else resultsByPage()
}

fun sortByName() {
    fullTodoList = fullTodoList.sortedBy { it.title.uppercase() }
}

Thoughts on Kotlin/js and Kotlin as a client side language

I do think Kotlin has a well thought-out approach to client side js. I'd love to see more examples of clientside js but it's hard to find online (hence why I've written this article).

I encourage anyone reading to try it out! It's easy, just checkout the project and use:

./gradlew browserDevelopmentRun

And you should see it live in you browser.

Checkout the code on Github.

© 2025Brooks DuBois™