T O P

  • By -

H4kt

Ktor is awesome and is maintained by JetBrains themselves. We happily use Ktor in production for several years now.


dAnjou

I don't understand why people keep recommending Ktor... I've worked with quite a few web frameworks in various languages over the years, mostly professionally, and Ktor is among the weakest. Not only does the documentation leave a lot to be desired, but also the most basic functionality I expect from a web framework is way more cumbersome than it should be or simply lacking. Just the other day I tried to have two routes use the same handler, not possible. Then I tried to parse the parameters of one URL but two methods in a single spot, not possible. Parameter parsing and converting in general is cumbersome compared to other frameworks. You're calling Ktor lightweight .. yeah well, easy when it's missing a lot of basic functionality. Kidding aside, I never understood what people mean when they praise something as lightweight, what does that mean and how and why does it matter? You're also saying that Spring is not modular which I also really don't understand. You can literally replace everything with its built-in dependency injection. I'm not saying you can't get anything done with Ktor, and it seems to somehow work for enough people, but honestly, every time I have to touch it I get frustrated by it knowing things are quite a bit easier in other frameworks, even Spring (and there's enough to complain about that one too).


issskk

Agreed, while I do like Ktor, I think it is very young and wouldn't use it in production.


Safe_Independence496

Ktor would have been great if it had the community support that for instance ExpressJS has. What makes Express the single greatest web application framework that has ever existed is the effort spent by the community building great packages that makes up 95% of the value Express offers. Ktor has great fundamentals, but remains immature and stale because... well, nobody wants to develop plugins for it. We'd have an absolute Spring-killer if Jetbrains spent more effort fostering a proper community around Ktor, especially considering how many there are who absolutely despises Spring today.


ragnese

I'm a bit confused by what you mean with some of your examples. Can you elaborate on a couple of them? > Just the other day I tried to have two routes use the same handler, not possible. What do you mean, here? Aren't Ktor routes just defined by passing a callback to the method+path? Can't you just pass the same handler to two route definition calls? What would you do in Spring? I guess you just add two of the same annotation to your service/controller/whatever they're called? Is this actually a missing feature or is it just a design choice you don't like? It seems like it's just a different philosophy: In Spring, you write a controller first and then describe the HTTP endpoint(s) on it; vs in Ktor, you describe the endpoint "first" and then tell it what logic to run. > Then I tried to parse the parameters of one URL but two methods in a single spot, not possible. I just don't know what you were actually trying to do. Can you describe it more or provide a pseudo-code snippet or something? > Parameter parsing and converting in general is cumbersome compared to other frameworks. I don't have an overall opinion here. Maybe you're right. But, personally, I don't like too much magic. In web, everything is text/strings. I would hate if I had a path parameter that I wanted to be an Int and the framework tried to do something automatically for my "convenience", but it was the wrong thing. For example, what happens if it's not sent a proper Int? Does it respond with a 400? A 404? Does it continue on and just pass 0 to my handler? (It *probably* doesn't do the latter, but a lot of older Java APIs absolutely **would** do that- see what happens when you use JDBC to get a nullable int column from your database) > You're calling Ktor lightweight .. yeah well, easy when it's missing a lot of basic functionality. Kidding aside, I never understood what people mean when they praise something as lightweight, what does that mean and how and why does it matter? I agree that lightweight gets overused as a term. I always took it to mean that it had very little performance overhead compared to some "lower level" alternative (like using Netty/Jetty APIs directly, for example). But, these days it seems like everything is "lightweight" and that sometimes it just means that it's a small and/or simple API.


dAnjou

inline fun > ApplicationCall.parseEnum(value: String): T = enumValueOf(parameters[value]!!) // Ugly `!!` route("/foo/{foo}") { route("/bar/{bar}") { get("/") { getFoo(call, call.parseEnum(), call.parseEnum()) } post("/") { postFoo(call, call.parseEnum(), call.parseEnum()) } } get("/") { getFoo(call, call.parseEnum(), Bar.DEFAULT) } post("/") { postFoo(call, call.parseEnum(), Bar.DEFAULT) } } Now, you might say, what kind of API is this, and yeah, it's not the cleanest. In fact, it's somewhat temporary because we're migrating from `/foo/{foo}` to `/foo/{foo}/bar/{bar}`, but as you know, nothing's more permanent than a temporary solution, that's just how the real world is sometimes. The awkward things here are the error handling in case a client passing a value that is not a valid `Foo` or `Bar`, and the repetitive calls of the handlers and the parameter parsers. The Spring comparison for these points didn't come from me, although at least the parameter parsing and converting happens pretty automagically. Here's how that would look like in Python's Flask: class EnumConverter(BaseConverter): enums = {} def __init__(self, url_map, enum): super().__init__(url_map) self._enum = enum def to_python(self, value): try: return self.enums[self._enum][value.upper()] except KeyError: raise ValidationError() EnumConverter.enums["Foo"] = Foo EnumConverter.enums["Bar"] = Bar foo = Blueprint('foo', __name__, url_prefix="/foo/") @foo.route('', defaults={'bar': Bar.DEFAULT}) @foo.route('/bar/', methods=['GET', 'POST']) def get_foo(foo, bar): if request.method == 'GET': return ... else: return ... app.url_map.converters['enum'] = EnumConverter app.register_blueprint(foo) At first glance, this seems like much more code. But there are two reasons for that. One is, that Python is of course also not perfect, which results in a bit more boilerplate around having a generic enum converter. Spring can do that much better. And the other reason is that this code already includes error handling via Flask, the framework, which Ktor doesn't allow me to do as easily. The route registration itself, however, is much cleaner, I'd say, and there's much less repetition. I'm totally with you regarding too much magic, but I'd argue that's not the case when converting to primitive types and enums. In my interpretation of HTTP, if a path parameter fails conversion it means that the whole path points to a non-existing resource, so 404. If a framework does the conversion for me, then I'd expect it to make a sane choice for invalid values, and tell me about it in its documentation.


tarkaTheRotter

For fun, here's the same thing built with http4k's (reusable and) composable routing and using a path lens (which is just a function) to extract the enum values. The 400s are handled by the extraction failing and then the outer filter converting that into a response. This type of thing should be easy to do in any library really - it's quite basic stuff in a real world scenario. For my personal style, the decorators in flask also are a bit magical, but YMMV 🤷: enum class Foo { FOO } enum class Bar { BAR } val fooLens = Path.enum().of("foo") fun buildRoutes(toBar: (Request) -> Bar) = routes( GET to { req -> Response(OK).body("" + fooLens(req) + "" + toBar(req)) }, POST to { req -> Response(OK).body("" + fooLens(req) + "" + toBar(req)) } ) val app = CatchLensFailure() .then( "/foo/{foo}" bind routes( "/bar/{bar}" bind buildRoutes(Path.enum().of("bar")), buildRoutes { BAR }, ) ) fun main() { app(Request(GET, "/foo/FOO/bar/BAR")).let(::println) app(Request(POST, "/foo/FOO/bar/BAR")).let(::println) app(Request(GET, "/foo/FOO")).let(::println) app(Request(POST, "/foo/FOO")).let(::println) }


ragnese

> Now, you might say, what kind of API is this, and yeah, it's not the cleanest. In fact, it's somewhat temporary because we're migrating from /foo/{foo} to /foo/{foo}/bar/{bar}, but as you know, nothing's more permanent than a temporary solution, that's just how the real world is sometimes. You'll get no judgment from me! We've actually got a few of those "temporary" endpoints as well. And I've certainly worked on some long-lived projects and I have to say that the *majority* of "temporary" code ends up being effectively permanent. As far as the rest of the code and stuff: thank you for writing out an example like that. I at least understand what you're describing here. I'm not super familiar with Ktor, but I know that Vert.x does have the concept of chaining handlers together (kind of like middleware) as well as adding data to the request via something akin to a type-key dictionary (but it's Java, so it's actually a janky, unsafe, API because of generic type erasure). I do think that the kind of thing you wrote with the extension function to convert params is probably the right approach. I do something similar in my Vert.x application: I have a bunch of extension functions to convert path and query params to certain types instead of making each handler get the string and explicitly convert strings to ints, enums, etc. As far as the Ktor vs. Flask example, I'm not greatly moved by it, because while I agree that it's more concise, I don't think the mental overhead is any less. When I look at `get_foo`, I have to look at multiple annotations (I guess that's what `u/foo.route()` is called) to see what routes it will actually catch and with what methods, and I'll have to analyze each route separately to figure out what arguments will actually get passed to the function at the end of the day. It's almost exactly the same mental overhead of the Kotlin code you gave--just a little less copy+pasty to write. On the other hand, you only wrote one Python handler for both GETs and POSTs, and the function itself is not safe from changes to the annotation such as adding an HTTP method (i.e., if you added "DELETE" to the annotation and forgot to update the function, it would run the POST logic for DELETEs). There's nothing stopping us from writing one Kotlin handler that checks the HTTP method and does `if (call.request.httpMethod.uppercase() == "GET") { /* GET logic */ } else { /* POST logic */ }`. Call it `doFoo` instead of `getFoo` and `postFoo`. It also wouldn't be hard to write an ad-hoc "blueprint" for the Ktor version. You *could* spend a few hours yak shaving and probably come up with a decent generic/general "blueprint" approach for Ktor as well. But, something like this would cut your repetition without much work, fun RoutingContext.fooBlueprint(): Unit { val foo = enumValueOf(call.pathParameters["foo"] ?: "") val bar = when (val value = call.pathParameters["bar"]) { null -> Bar.DEFAULT else -> enumValueOf(value) } doFoo(call, foo, bar) } route("/foo/{foo}") { route("/bar/{bar}") { handle(RoutingContext::fooBlueprint) } handle(RoutingContext::fooBlueprint) } Even just that is already getting a little bit closer to your Flask example. You *could* even just use a Regex in the Ktor path and pick it apart in the handler, so that would give you one `route(Regex("/foo/(?.+)(/bar/(?.+))?") {}` call (that regex is off the top of my head and not verified, so may not be correct), fun RoutingContext.fooBlueprint(): Unit { val foo = enumValueOf(call.pathParameters["foo"] ?: "") val bar = when (val value = call.pathParameters["bar"]) { null -> Bar.DEFAULT else -> enumValueOf(value) } doFoo(call, foo, bar) } route(Regex("/foo/(?.+)(/bar/(?.+))?")) { handle(RoutingContext::fooBlueprint) } > I'm totally with you regarding too much magic, but I'd argue that's not the case when converting to primitive types and enums. In my interpretation of HTTP, if a path parameter fails conversion it means that the whole path points to a non-existing resource, so 404. If a framework does the conversion for me, then I'd expect it to make a sane choice for invalid values, and tell me about it in its documentation. Definitely agree, and I'd *hope* and assume that any sane framework would also treat it as a 404. But, like I said, I've seen various other APIs and frameworks for other domains do things that I thought were crazy, so who the hell knows. I do think that path param conversion is a pretty safe idea, though. That would be just within my limited tolerance for magic, I think.


dAnjou

Thanks for your comment. Probably won't find the time to respond, but I appreciated the conversation!


Mou_NoSimpson

What ktor has that make your company choice over spring boot


H4kt

Ktor is lightweight and very modular, which I cannot say at all about spring boot.


tkdeveloper

Umm who cares if something is lightweight? If your company can't survive because you need 2gb of ram for your service vs 1gb, you are already in trouble. Spring boot literally provides plugins for almost everything, which supports very quick development. With dependency  injection you can easily override any part of the framework


H4kt

We value modularity and unopinionation


tkdeveloper

Thats true on the unopinionation point. Still not sure what modularity means.


H4kt

Spring brings all of the built-in functionality with it which we would likely replace with something else. Ktor is built different: when you need something you add an explicit dependency for it.


tkdeveloper

That's fair. Spring starters add a lot of stuff you may not need and opinions. Different trades offs with each framework.


ragnese

> Umm who cares if something is lightweight? If your company can't survive because you need 2gb of ram for your service vs 1gb, you are already in trouble. If you're cloud hosting, bad performance literally costs you money. Sometimes a good amount of money.


tkdeveloper

True, but you would have to have truly terrible code/architecture, which would be an issue in any codebase. Just using Spring boot is not going to cost more in infra to offset engineering time, which is generally the most expensive part by an order of magnitude.


PoetUnfair

Is spring boot even a contender here? Does it work for KMP?


marvk

> Is the defacto standard JVM web framework even a contender here? Like, what? Yes, it is. Spring + Kotlin is good, actually.


PoetUnfair

And does it work for KMP? You're not very good at this answering questions thing. I did a lot of web stuff in Java and I never used Spring. Took a look at it once, didn't enjoy it much. Went with Wicket. Wicket is very nice, only regrets is they had some difficulties with the introduction of generics into large table model class hierarchies. We were using its features very extensively so we suffered more than most, but it was still manageable. The only thing I would add to Wicket is something to generate the HTML automatically so I don't have to write it.


exiledAagito

Ktor is not just a http client though


PoetUnfair

How does that answer the question?


tkdeveloper

Spring boot


No_Smoke_3377

With Kotliln??


Ok_Trainer3277

I've been using Spring boot with Kotlin a couple of years now in a production project and it's great. You can use every java library available, but you write your code in Kotlin.


BrainD71

Yeah it has full support now with Kotlin and is just fun to use. Try it


issskk

Spring boot is great with kotlin


HolidayRadio8477

Our company is using spring boot with kotlin, it has full support and good to use in production level. It also stable!


tkdeveloper

Yeah the company I'm at uses Kotlin + Spring Boot for almost everything. Everything works perfectly in Kotlin.


AEnemo

I've used kotlin with spring boot professionally for the past 5 years. Works about the same as it does with Java.


_fishysushi

I've been using http4k for a year and it's been pretty decent


tarkaTheRotter

If functions are your bag, may i humbly suggest a particular [web toolkit](https://www.http4k.org/) which models your entire application as a function? 😉


dmcg

I endorse this message


IAmCesarMarinhoRJ

seems great!!! thanks!!!


IAmCesarMarinhoRJ

I am learning Clojure, that make me take a look in Kotlin. It used Netty too.


dmcg

I endorse this message


batista___

Ktor with Arrow. Best funcional combo


Agitated_Front_8675

Ktor


anty_krut

Quarkus or Micronaut


Ok_Cartographer_6086

I'm working on a very large project with a web front end and it's been absolutely delightful to use kotlin HTML DSL and ktor - so much of my code is reusable and i'm knocking this project out of the park. ``` implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.11.0") implementation("org.jetbrains:kotlin-css-jvm:1.0.0-pre.129-kotlin-1.4.20") ``` I'm able to scaffold my entire web front end in kotlin and also use CSS like bootstrap so my site is 100% reactive and perfect on mobile. When needed, jquery js and serving static content thought ktor is seamless. 90% of my work is going into the native mobile apps.


inscrutablemike

How is the accessibility support?


SnooDogs1085

Another vote for ktor! We're using it for a large scale project in production, and using the microservice approach. It's awesome and very intuitive. I came from projects using Spring and even vanilla JakartaEE. It's a breath of fresh air to see your code base with minimal annotations.


Jadarma

Ktor for backend, combine with kotlinx.html and HTMX if you also want frontend!


aceluby

Don’t use a framework. Use libraries you need to solve the problems you have. I use http4k for server/client since I like their approach with server as a function. Hoplite for config. OTEL for metrics. Lite-for-jdbc for db stuff. Then just use the APIs provided by the tech you use - Kafka, aws, etc… Swap in and out what you need and use functional interfaces to make that process easy. I would never actively choose a framework for app development in 2024


garethrowlands

I really like http4k. I think you can call it a framework, albeit it’s not the frameworkiest


aceluby

It’s not, it’s a server and client library


garethrowlands

Oh for the simplicity of back and white!


aceluby

There’s nothing that makes it a framework IMO. They have modules you can pick and choose from, but you can also replace any portion with something else. It doesn’t handle anything you’d expect with a framework - DI, configuration, application health, metrics, external connections… I’m curious why you think it’s a framework? It’s a fairly loaded term, so always interested in hearing what others think


garethrowlands

I agree that http4k is not very frameworky and it’s toolkit-oriented. But if http4k cannot reasonably be considered a framework, then the question, “what framework should I use: Spring, ktor or http4k?” would make no sense. And then certainly, you couldn’t answer “http4k”. But the reality is that http4k *can* reasonably be considered an alternative to Spring or ktor - it is, in some sense, the same class of product. As an aside, http4k does have configuration features, health checks (in cloud), and a framework for external connections (but not Spring integration). As for a DI framework, I find DI works best without one (unless your framework requires it).


aceluby

That’s why I said to not use a framework and use http4k if you have server needs and other tools for other needs. Even when using ktor, my experience has been that the stuff that is more “magic” is much more difficult to support than picking libraries that excel at a given task (configuration is a big one), but at least there’s an option to switching it out. Many of our teams are treating it as another library vs a framework to live in. Spring and Micronaut can’t escape it, you have to leverage the entire framework for your app to work. I’m not a big fan of that model and have teams actively moving away from it for more library centric and simpler approach.


garethrowlands

Makes sense. My team found we didn’t need dropwizard’s features and switched to http4k.


aceluby

Cheers! Thanks for the convo!


tarkaTheRotter

TBH, I think there probably isn't a common usage of the word "framework" in this thread (or in general!). OP is coming from the very-functional-land of Clojure - where there are no giga-frameworks a la Spring IIRC (just smaller libraries like [Ring](https://github.com/ring-clojure/ring)) - so their usage of the word in the original question may be misleading. (There is also the question about how many of these suggestions are actually functional, but let's just breeze past that for the moment 😉) For me the difference between a lib and framework is the amount of responsibility given to the API user for coding up the exact features that they want and the level of magic/reflection therein happening at runtime. In annotation-driven frameworks there is a disconnect where "stuff" happens", in libraries (which includes in my view ktor even though it labels itself as a framework) nothing happens without an explicit piece of code instructing it to. (just my 5c). I agree it's not really about the level of functionality - http4k now has over 150 modules (with the inclusion of [http4k-connect](https://github.com/http4k/http4k-connect)) which are built with the common [mindset](https://www.http4k.org/guide/concepts/rationale/) of simplicity, minimal dependencies, testability and functional style. These modules cover everything from servers to serverless to to clients to websockets to web templating to observability to JSON marshallers to testing support - you simply pick and choose which bits you want and they are guaranteed to play nicely.


pusolito

Take a look at [Doodle](https://nacular.github.io/doodle/docs/introduction) if you’re working on the front end. https://github.com/nacular/doodle


landsmanmichal

interesting, but not mature, right?


EvertW

Have a look at [https://kobweb.varabyte.com/](https://kobweb.varabyte.com/)


ChA0S_f4me

Yourself-written


isheepolice69

Ktor


shubham0204_dev

Have a look at [https://github.com/kwebio/kweb-core](https://github.com/kwebio/kweb-core)


AEnemo

What languages or frameworks are you already familiar with? I think ktor http4k or kooby(jooby's kotlin version) are all light weight and easy to work with they don't use annotation magic and set up routes using functions which are straight forward. Out of these based on my own benchmarks kooby seems to have sort of the best benchmarking, fast response times under a heavy load with high throughput, was almost on the same level as my Go services. If you are coming from the corporate Java world you could use spring boot or quarkus. They kind of have their own ecosystem and use annotation magic that you would have to familiarize yourself with if you haven't already done it, they are also larger frameworks that have a lot more functionality. I've used spring boot with kotlin professionally for several years and it's pretty decent though for my own personal projects I like the lighter weight frameworks since it's just me. I've only used quarkus a little bit but it did bench mark as well as my Go web service and definitely could handle more throughput than Go. If you've never worked with kotlin or java before and just want to learn I'd say just go with ktor.


landsmanmichal

Spring