Simple zero-deps non-blocking web apps in Kotlin
Tallinn, Estonia
About me - Anton Keks
24 years working in software
Co-founded Codeborne 12 years ago
Agile geek, software craftsman, conference speaker
Did custom projects for many diverse customers� fintech, energy, telecom, other cool stuff
Formerly in Java (even taught Java in TalTech)
Now a Kotlin fan (seen my Kotlin Puzzlers?)
Frontends in Svelte + TypeScript
Kotlin web frameworks
Ktor - too big, hard to unit-test routes
Http4k - no async/coroutine support
Jooby/Kooby - too javaesque, problems with coroutines
SparkJava - only routes, depends on Jetty, no async
Javalin - no coroutines, 7k loc vs 1k loc
Spring - bloated, annotation-driven development is not right� (debugging and testing nightmare)
Any other - big, hard to debug, blocking, no first-class Kotlin
Klite goals
Kotlin should enable a simple and thin framework
Productivity, no boilerplate
Memory usage & decent performance
Small runtime, few dependencies (no log4j2)
“Native” for Kotlin & coroutines
Running in a (Docker) container, 12-factor compatible
App code should be mostly framework-agnostic
Easy to write pure unit tests
JDK HttpServer
Official part of JDK since 1.6 (2006)
Since Java 9 - module jdk.httpserver
JavaHttpServerDemo.kt
Can serve 100 concurrent requests in ~1ms each
Your app spends much more time on DB or other I/O
Klite (Kotlin-lite)
Klite is a thin wrapper around jdk.httpserver
Runs http request handlers in coroutines
Started in Dec 2021 as a POC
Now powers 3 production projects
The main server module is ~800 loc
184kb jar, no other dependencies
Kotlin features used most
Expressive nullability (great for zero-code validation)
Constructors with default values
Extensibility with extension functions
Reified generics (with typeOf<>)
Type-safe reflection
Delegation, including lazy
Value classes (but still no good support in Jackson)
And single-expression functions :-)
Main parts
Config
Server
Context (Router) -> handlers
HttpExchange
request & response
Body & content-type handling
BodyParser
Looked up according to Content-Type request header
BodyRenderer
Looked up according to Accept request header
plain/text & x-www-form-urlencoded by default
Or use<JsonBody>()
UTF-8 by default
Return Unit for 204 No Content response�
Error handling
Exceptions are mapped by ErrorHandler to ErrorReponse �Also rendered by BodyRenderer
�Some common Kotlin exceptions are mapped to nice responses,�e.g. NoSuchElementException to 404 NotFound
Annotated routes
Instead of
get {}
post {}
annotated<MyRoutes>()
@Path, @GET, @POST, etc
@PathParam, @QueryParam, @BodyParam, etc
Converter is used to convert strings to param types, e.g. UUID, LocalDate, etc
Allows most of the code to be framework-agnostic and easily testable
Annotations are for annotating, not changing of behaviour!
Registry
For internal and application components
Constructor-based dependency injection
Annotated routes get their parameters injected
Dependencies auto-created when first required
Default values used if no implementation registered
Decorators
They decorate route handlers, i.e. run around them
For logging, transaction handling, etc
Before & After also supported, both converted to decorators
Route handlers’ decorators are defined before them
Logging
Java System.Logger (since Java 9)
val logger = logger()
RequestIdGenerator - sets Thread name (instead of MDC)
RequestLogger
Can be redirected to slf4j
Implements simple logging to stdout as text or json
Session
Disabled by default
SessionStore
implementation can be provided to Server
CookieSessionStore
Encrypted cookies
No in-memory, as you want many nodes and restart them
Assets
AssetsHandler
Serves static files with caching headers
(can use pre-gzipped files)
SPA support: useIndexForUnknownPaths=true
Running behind a https proxy
As most applications do�
XRequestIdGenerator
To handle X-Request-Id
XForwardedHttpExchange
X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto
Server-side HTML
Koltin Template strings (triple-quotes)
Use + for HTML escaping
”””Hello <b>${+world}</b>”””
Use IDEA’s @Language(“html”)
Or implement BodyRenderer using any template engine
Optional Modules…
jackson
vs kotlinx-serialization
jdbc
liquibase (app user)
jobs
i18n
slf4j
Best Practices
12-factor apps
Assets for SPA (e.g. using Svelte + Vite)
Routes, dependency injection
TestData
Conclusion
Started as a POC, is now quite enjoyable and productive
Hopefully inspires you to strive for simplicity
JVM + Kotlin = powerful mix
Steal ideas!
Fork it if you want! Send PRs
Or just use it :-)