How is Ktor different? Hi all! I'm joining a team ...
# ktor
# How is Ktor different? Hi all! I'm joining a team that uses Ktor and I'm trying to get a grip on what distinguishes it from other frameworks. ---> continued in thread
🧵 2
(NOTE: I'm using the term "framework" here, some people think since Ktor isn't a Rails or Spring-style framework, it's not the correct term, but for the purposes of this post, please look past that, call it a library for connected code, whatever) Surface level tutorials, including the Ktor ones, that show how to implement a simple rest API or client in a given framework are great. I've had a look at a bunch of them and they do a good job of explaining basic Ktor concepts like plugins etc. But they don't really showcase or go into detail about the thought process behind, or how, the framework was built and how it works under the hood. The main differences I can see so far seems to be that Ktor (unsurprisingly) makes heavy use of Kotlin features such as coroutines, DSL, extension functions etc. ## Coroutines In a traditional synchronous framework, there's a thread pool of N threads, and each request will occupy (block) and live in a thread until the service responds, and if you mark an endpoint or service method called as part of that endpoint as async, what that means is the framework will spin up a piece of work in a different thread to do that work, but respond before that work has generated a result. Obviously, this isn't what is being referred to with async in Ktor, because then Ktor would always just respond immediately before any work has been done. My understanding is, rather, using Ktor with a simple scheduler, there will only be one thread per core in your machine, but those threads be able to spin up lots of coroutines which do things like accept a request, perform computations, io like storing to db or calling other services etc, and those coroutines while eg waiting for a response from the db or other service will be paused until they have a result. So eg a request to an endpoint that does three things before responding, the request will result in a parent coroutine, and each of those three things that get done (if they're suspend funs) would be child coroutines, and the three things will execute asynchronously and in parallel, and then when all of them have results there will be a response. Because coroutines are lighter weight than threads, you spin up many times more coroutines than threads, increasing performance, which ultimately means you can serve more traffic with less cloud resources. Is my understanding roughly correct? ## Dsl and extension functions Can someone explain the thoughts behind Ktors use of DSL and extension functions? I'm not looking for answers like "Ktor uses it because it's written in Kotlin" or "Ktor uses it because it's nicer than annotations"... A framework could be written from the ground up in Kotlin that is designed completely differently from Ktor - eg, it could eschew DSL completely and rely on just instantiating endpoints like eg val getUser : Get = Get(Url(/users)) as objects that get attached to resource objects that get attached to an app objects etc etc. Why is Kotlin designed the way it is, what was the thought process behind it? Or is its design kind of an accident? The history of the framework seems semi accidental to me - it seems like it was originally mostly an internal experimental framework that happened to catch on, which took the development team kind of by surprise a bit. ## Misc By all means any other ways you can think of that Ktor is different and explain in more detail how it's built and how it works under the hood vs other frameworks would be super useful.
The thing that distinguishes Ktor from other frameworks is precisely that it’s written in Kotlin instead of another language. There are many JVM server frameworks that provide support for Kotlin, but because they were not written in Kotlin, their integration with Kotlin and its unique features like Coroutines will be limited. Kotlin itself is the main selling point of Ktor, because fundamentally, Ktor is just an API sitting on top of other HTTP engines, and that API uses the best of Kotlin’s language features. Being able to use coroutines and a declarative code-driven configuration rather than config files, annotation processors, or “event bus”-type hooks as used by other frameworks, is precisely why one would choose Ktor vs another framework. To your point about why Ktor chose a DSL over annotation processors or those other mechanisms, that also goes along with the preference and features of the Kotlin language itself. Java frameworks heavily use Annotations because the language is not expressive enough to make configuration easy and pleasant (at least, not when those frameworks were created. Modern java is significantly more expressive than older-style Java). Likewise, PHP, JS, and Ruby frameworks tend to use event bus-style hooks because they’re dynamically typed and hooks are easy to implement with them (along with being popularized by Wordpress). But Kotlin was strongly inspired by Groovy, being able to use code as configuration, which makes it possible to use the language itself for things that would otherwise have to be built into the framework. It allows them to delegate a lot of the work in building a server to the language, rather than having to handle it within the framework. Using a DSL for Ktor makes it easy to implement because you don’t have to learn a bunch of annotations or hooks to use the framework; you can just use auto-complete to see which Features can be installed into the DSL. It’s also easier to embed into other applications, because the entirety of the server is encapsulated into the DSL and doesn’t rely on any global variables or anything like that. But it is worth noting that it’s also designed to be a “build your own server” kind of framework, rather than one with all the bell-and-whistles built in. This is purely a stylistic choice, and is similar to the difference between choosing Express.JS vs Next.JS. In Express, you can customize the server to exactly what you need, but it takes some work to get familiar with what’s available and how to set everything up. But you can be sure that it doesn’t have anything you didn’t choose to include in the server. In contrast, Next.JS is very opinionated, right down to folder structure, and you’re at the mercy of the decisions of the framework developers. In most cases, you can just trust that they know what they’re doing, and you simply learn the conventions, and then you’re good to go, but it does make it more difficult to handle some specific edge-cases. A similar comparison can be made in the Ruby world with Sinatra vs Rails, or in PHP with Laravel vs Wordpress.
I think you might have missed a very key feature of Ktor - in combination with Kotlin Multiplatform it’s one of the few HTTP frameworks (from a client perspective) that provides a consistent fully multiplatform API. I am able to write cross-platform Kotlin code that is able to run on Android, iOS, desktop, JS (and anywhere else KMP is supported) with the same sourcecode.
One of the practical aspects that I have noticed is how easy it is to refactor, due to the combination of Kotlin and plugin oriented architecture.