KtKit


A comprehensive Kotlin multiplatform toolkit for building server applications with Ktor.

📖 Documentation
🏠 Homepage (under construction)
Features
Overview
KtKit is a Kotlin multiplatform toolkit designed to speed up server-side application development with Ktor. It brings
together several libraries into a cohesive set of tools that handle the repetitive aspects of backend development.
[!NOTE]
Early Stage Project: KtKit is actively evolving. APIs may change between versions as we refine the abstractions
based on real-world usage. Production use is possible but expect some breaking changes. Feedback and contributions are
highly appreciated!
Usage
implementation("io.github.smyrgeorge:ktkit:x.y.z")
Ergonomics
The example module shows how Arrow's Raise and Kotlin context parameters keep handler and service code compact while
preserving explicitness around errors and execution context.
A REST handler extends one of the handler base classes (here XRealNameRestHandler), declares its base path in uri()
and its routes in routes(). Typed helpers (pathVariable, queryParam, ) parse the inputs, the return
value is serialized as the response, and raised errors map to RFC 9457 responses. Handlers are
auto-registered by the application
(from ):
class TestRestHandler(
private val testService: TestService
) : XRealNameRestHandler() {
override fun String.uri(): String = "/api/v1/test$this"
override Route. {
GET() {
log.info { }
testService.withTransaction {
testService.test().map { it.toDto() }
}
}
}
}
The service the handler calls uses context parameters for the error channel (Raise<ErrorSpec>), the execution context,
and the database scope (QueryExecutor/Transaction)
(from TestService.kt):
class TestService(
override db: Driver,
repo: TestRepository,
) : AuditableDatabaseService<Test> {
log = Logger.of(::)
context(_: Raise<ErrorSpec>, _: QueryExecutor)
: List<Test> = db { repo.findAll() }
context(_: ExecContext, _: Raise<ErrorSpec>, _: Transaction)
: List<Test> {
log.info { }
findAll().also {
log.info { }
}
}
}
The execution context is a coroutine context element that also carries log4k's tracing context:
class ExecContext(
val reqId: String,
val reqTs: Instant,
val principal: Principal,
val tracing: TracingContext,
) : TracingContext by tracing, CoroutineContext.Element
This lets handlers and services carry request metadata and tracing without threading parameters manually, while domain
errors are raised through the Raise<ErrorSpec> context parameter. The context is propagated in two ways at once: via
CoroutineContext and via context parameters in function signatures.
Modules and features
Gradle plugin (ktkit-gradle-plugin)
The Gradle plugin is the single entry point of a ktkit service build. A typical service build script (see
the example module):
plugins {
kotlin("multiplatform")
id("io.github.smyrgeorge.ktkit") version "x.y.z"
}
kotlin {
jvm()
macosArm64 { binaries { executable() } }
}
ktkit {
sqlx4k {
driver = PostgreSQL
generatedCodePackage =
extensions(Pgmq)
args = mapOf( to )
}
jar {
mainClass =
}
}
The full list of options of each ktkit { } block is documented in its options file:
Application bootstrap and configuration
The Application wrapper is the entry point of a ktkit service: it manages the Ktor server lifecycle
(startup/shutdown), sets up JSON and the Koin DI container, and auto-registers every REST handler bound as
AbstractRestHandler. Tracing, logging, and DI are configured from one place:
Application(
name = "ExampleApplication",
conf = Application.Conf(host = "localhost", port = 8080),
configure = {
logging {
level = Level.INFO
}
tracing {
}
json {
}
ktor {
}
di {
single { db }.bind<Driver>()
singleOf(::TestRestHandler) { bind<AbstractRestHandler>() }
singleOf(::TestService)
}
},
postConfigure = {
}
).start()
A complete bootstrap lives in the example module:
ExampleApplication.kt.
Kotlinx serialization
Everything that crosses the wire is a @Serializable class: REST request/response bodies, the RFC 9457 error responses,
the TOML configuration, and the JSON database columns. There is no reflection involved, so serialization works
identically on JVM and Native targets. The Gradle plugin applies the kotlinx.serialization compiler plugin
automatically.
API errors (RFC 9457)
Domain errors are typed values (e.g. , , , ,
, ), raised through the context parameter instead of thrown.
maps each onto an :
sets the status, the class name the , the , the extension, the
span id the . Any other throwable is an .
Security & permissions
Authentication and authorization are built into the request pipeline of AbstractRestHandler.
Authentication — AbstractRestHandler takes a PrincipalExtractor, which resolves the authenticated Principal
from the incoming request. When the extractor yields no principal, the handler falls back to its defaultUser (that is
how works — it defaults to the anonymous principal); otherwise the request is rejected with a 401
. Ready-made handler base classes wire the extractor for you (e.g. uses
).
The PrincipalExtractor implementations:
[!WARNING]
The x-real-name mechanism is not safe to expose directly to the internet. It assumes a trusted reverse proxy or
API gateway in front of the application that authenticates the user, strips any incoming x-real-name header, and
sets it with the authenticated user's information before forwarding. Without such a proxy, any client could forge the
header and impersonate any user.
Permissions — authorization runs after authentication in two layers, and both must pass (a failure responds with a
403 Forbidden API error):
- Role-based — a
Principal carries a set of roles; the handler constructor accepts hasRole, hasAnyRole and
hasAllRoles constraints, enforced on every route of the handler.
- Custom predicates — both the handler constructor and every route call accept a
permissions: HttpContext.() -> Boolean function (handler-level and route-level predicates are combined with AND).
class AdminRestHandler : XRealNameRestHandler(
hasRole = "admin",
) {
override fun String.uri(): String = "/api/v1/admin$this"
{
GET(, permissions = { user.hasRole() }) {
}
}
}
Logging, tracing, and metrics (log4k)
Observability is built on log4k — a multiplatform logging library with tracing
and metrics. The ExecContext carries the request's tracing context end to end, so log lines and spans are correlated
automatically across REST handlers, database transactions and queries, and PGMQ messages. The log4k annotations
(@Traced, @Timed, @Logged) instrument service methods declaratively.
Health & metrics endpoints
Every application serves two unauthenticated status endpoints out of the box:
GET /api/status/health — application name, status, start time, and uptime
GET /api/status/metrics — the log4k metrics registry in OpenMetrics line format (VM memory and processor gauges are
collected by default; register your own via log4k's Meter)
TOML Configuration Loading
ConfigPropertiesToml loads TOML files into @Serializable data classes, with environment-variable interpolation
(${VAR_NAME}) and layered overrides: load() reads application.toml from resources as the base, then merges the
first override found among , , and
(override values win).
[database]
url = "postgresql://${DB_HOST}/mydb"
maxConnections = 10
@Serializable
data class AppConfig(val database: DatabaseConfig)
val config: AppConfig = ConfigPropertiesToml.load()
For the full API (loading from a specific file, explicit base/override merging), see
ConfigPropertiesToml.kt.
Database support (sqlx4k)
Database access is built on sqlx4k — a coroutine-first SQL toolkit for Kotlin
Multiplatform with compile-time query validation. PostgreSQL, MySQL/MariaDB, and SQLite are supported, on JVM and Native
targets alike.
The ktkit-sqlx4k module glues sqlx4k into the toolkit: @Table entities with auto-managed audit columns (createdAt/
createdBy//), interfaces implemented at compile time by the sqlx4k code generator,
traced transactions and queries, error mapping into the ktkit error model, and JSON-column encoders for
classes. PGMQ (a message queue on Postgres) is available through .
- How the integration works: ktkit-sqlx4k
- The full sqlx4k feature list: smyrgeorge/sqlx4k
Queue support (PGMQ)
Message-queue support is built on PGMQ — a lightweight message queue on Postgres, like
AWS SQS and RSMQ — via sqlx4k's PGMQ client (PostgreSQL only).
The ktkit-sqlx4k-pgmq module glues it into the toolkit: the Pgmq wrapper and AbstractPgmqEventHandler, which
propagates tracing and the authenticated user through message headers (in both directions — send attaches them,
consuming restores them into a fresh ExecContext), and manages the consumer lifecycle with retries and graceful
shutdown. Enabled via sqlx4k { extensions(Pgmq) } in the Gradle plugin.
- How the integration works: ktkit-sqlx4k-pgmq
- The PGMQ project: pgmq/pgmq
OpenAPI generation (ktkit-compiler-openapi)
A Kotlin compiler plugin that generates the OpenAPI 3.1 specification of your REST handlers at compile time — no
reflection, works on every KMP target (JVM and Native). It is attached automatically by
the Gradle plugin (turn it off with ktkit { openApi { enabled = false } }).
At runtime the framework merges the generated fragments of all registered handlers and serves an interactive
documentation UI — Swagger UI (default) or Scalar, selected via Application.Conf.OpenApi.ui — at GET /api/docs, and
the merged OpenAPI 3.1 document at GET /api/docs/openapi.json.
- What the plugin provides (analysis rules,
@OpenApi metadata, configuration, limitations):
ktkit-compiler-openapi
Example
Check the example application here.
Building & Development
Build
On a clean checkout (and after every version bump), bootstrap the build first — it publishes the ktkit Gradle plugin and
the OpenAPI compiler plugin to mavenLocal, which the example module needs before the main build can even configure
(see bootstrap.sh):
./scripts/bootstrap.sh
Then build as usual:
./gradlew build
Docker Setup
The project includes a docker-compose.yml for PostgreSQL:
docker-compose up -d
Contributing
This is an open-source project. Contributions are welcome!
License
Check the repository for license information.
Related Projects
- log4k – Multiplatform logging with tracing
- sqlx4k – Multiplatform database access
Author
Yorgos S. (@smyrgeorge)