Skip to content

darkryh/katalyst

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

167 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Katalyst – Ktor Bootstrap Starter Kit

Maven Central

Katalyst gives you a ready-to-use Ktor backend stack: automatic DI, YAML configuration, Exposed/Hikari/JDBC persistence, migrations, scheduler, events, websockets, and first-class testing helpers—so you can focus on business logic instead of wiring.

  • What you get: DI (Koin), persistence with Exposed + HikariCP, YAML config, scheduler + events, websockets, migrations, and testing utilities—all packaged for Ktor services.
  • Docs: see documentation/README.md for the full guide index.
  • Latest version: see maven-metadata.xml for the current release.

What's New (Latest Alpha)

  • Deferred DI injection primitives in katalyst-di:
    • Provider<T> for runtime resolution
    • Lazy<T> constructor injection
    • () -> T function-provider injection
    • Optional @InjectNamed("...") for qualifier disambiguation
  • ApplicationInitializer supports multi-binding with deterministic execution order.
  • Managed low-level SQL API in katalyst-persistence via SqlExecutor (executeUpdate, query, queryOne, executeBatch), reusing active transaction connections when present.

Add Katalyst to your project (Gradle)

Use the published artifacts from Maven Central (see badge above or maven-metadata.xml for the latest version).

plugins {
    kotlin("jvm") version "2.2.21"
    id("io.ktor.plugin") version "3.3.3"
    id("org.jetbrains.kotlin.plugin.serialization") version "2.2.21"
}

repositories {
    mavenCentral()
}

dependencies {
    // Replace with the latest Katalyst version from Maven Central
    val katalystVersion = "<katalyst-version>"
    // Pin your app stack as needed
    val ktorVersion = "3.3.3"
    val exposedVersion = "1.1.1"
    val hikariVersion = "5.1.0"
    val postgresVersion = "42.7.8"

    // Core Katalyst modules
    implementation("io.github.darkryh.katalyst:katalyst-core:$katalystVersion")
    implementation("io.github.darkryh.katalyst:katalyst-transactions:$katalystVersion")
    implementation("io.github.darkryh.katalyst:katalyst-persistence:$katalystVersion")
    implementation("io.github.darkryh.katalyst:katalyst-ktor:$katalystVersion")
    implementation("io.github.darkryh.katalyst:katalyst-scanner:$katalystVersion")
    implementation("io.github.darkryh.katalyst:katalyst-di:$katalystVersion")
    implementation("io.github.darkryh.katalyst:katalyst-migrations:$katalystVersion")
    implementation("io.github.darkryh.katalyst:katalyst-scheduler:$katalystVersion")
    implementation("io.github.darkryh.katalyst:katalyst-websockets:$katalystVersion")
    implementation("io.github.darkryh.katalyst:katalyst-ktor-engine-netty:$katalystVersion")

    // Config
    implementation("io.github.darkryh.katalyst:katalyst-config-provider:$katalystVersion")
    implementation("io.github.darkryh.katalyst:katalyst-config-yaml:$katalystVersion")

    // Events
    implementation("io.github.darkryh.katalyst:katalyst-events:$katalystVersion")
    implementation("io.github.darkryh.katalyst:katalyst-events-bus:$katalystVersion")

    // Testing helpers
    testImplementation("io.github.darkryh.katalyst:katalyst-testing-core:$katalystVersion")
    testImplementation("io.github.darkryh.katalyst:katalyst-testing-ktor:$katalystVersion")

    // Ktor surface + persistence (pin to your needs; shown for reference)
    implementation("io.ktor:ktor-server-core:$ktorVersion")
    implementation("io.ktor:ktor-server-netty:$ktorVersion")
    implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
    implementation("com.zaxxer:HikariCP:$hikariVersion")
    implementation("org.postgresql:postgresql:$postgresVersion")
}

To iterate on Katalyst locally without publishing, you can use a composite build override in your consumer’s settings.gradle.kts:

includeBuild("../katalyst") // path to local checkout, removes the need to publish during local dev

Minimal Boot Sequence

package io.github.darkryh.katalyst.example // EXAMPLE - BASE PACKAGE
import io.github.darkryh.katalyst.di.katalystApplication
import io.github.darkryh.katalyst.di.feature.enableServerConfiguration
import io.github.darkryh.katalyst.config.yaml.enableConfigProvider
import io.github.darkryh.katalyst.ktor.engine.netty.embeddedServer

fun main(args: Array<String>) = katalystApplication(args) {
    engine(embeddedServer())
    database(DbConfigImpl.loadDatabaseConfig())
    scanPackages("io.github.darkryh.katalyst.example") // REQUIRED
    enableServerConfiguration()
    enableConfigProvider()
    enableEvents()
    enableMigrations()
    enableScheduler()
    enableWebSockets()
}

Once DI is running, Katalyst discovers everything else automatically (see documentation/auto-wiring.md for more examples):

@Suppress("unused") // automatically injected
fun Route.authRoutes() = katalystRouting {
    route("/api/auth") {
        post("/login") {
            val service = call.ktInject<AuthenticationService>()
            call.respond(service.login(call.receive()))
        }
    }
}

@Suppress("unused")
fun Route.notificationWebSocketRoutes() = katalystWebSockets {
    webSocket("/ws/users") {
        send(Frame.Text("""{"type":"welcome"}"""))
        for (frame in incoming) if (frame is Frame.Text && frame.readText() == "ping") {
            send(Frame.Text("""{"type":"pong"}"""))
        }
    }
}

Services and components simply declare their dependencies; implementing Service/Component is what signals auto-discovery:

class AuthenticationService(
    private val repository: AuthAccountRepository,
    private val validator: AuthValidator,
    private val passwordHasher: PasswordHasher,
    private val eventBus: EventBus, // event-bus module needed
    private val jwtSettings: JwtSettingsService
) : Service {
    private val scheduler = requireScheduler() // to use this, needs to have implemented scheduler module

    suspend fun register(request: RegisterRequest): AuthResponse = transactionManager.transaction {
        validator.validate(request)
        val account = repository.save(/**/).toDomain()
        eventBus.publish(UserRegisteredEvent(account.id, account.email, request.displayName))
        AuthResponse(account.id, account.email, jwtSettings.generateToken(account.id, account.email))
    }

    @Suppress("unused") //scheduler functionality based on coroutines
    fun scheduleAuthDigest() = scheduler.scheduleCron(
        config = ScheduleConfig("authentication.broadcast"),
        task = { broadcastAuth() },
        cronExpression = CronExpression("0 0/1 * * * ?")
    )
}

@Suppress("unused")
class UserRegistrationFlowMonitor(
    private val eventBus: EventBus
) : Component { // implementing Component marks this class for auto-discovery
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)

    init {
        scope.launch {
            eventBus.eventsOf<UserRegisteredEvent>().collect { event ->
                println("Flow spotted registration for ${'$'}{event.email}")
            }
        }
    }
}

Testing & Coverage

Prefer the testing modules over ad-hoc bootstrapping (full details in documentation/testing.md):

@Test
fun `register login over HTTP`() = katalystTestApplication(
    configureEnvironment = {
        database(inMemoryDatabaseConfig())
        scan("io.github.darkryh.katalyst.example")
    }
) { env ->
    val register = client.post("/api/auth/register") { /* body */ }
    val profile = client.get("/api/users/me") {
        header(HttpHeaders.Authorization, "Bearer ${register.jwt}")
    }
    assertTrue(env.get<UserProfileService>().listProfiles().any { it.accountId == register.accountId })
}

Commands:

./gradlew build                      # full build + tests
./gradlew :katalyst-example:test     # example module tests (H2 + Postgres container)
./gradlew :katalyst-example:koverHtmlReport  # coverage report (build/reports/kover/html/index.html)

Need deeper walkthroughs? Check the documentation/ folder.

About

bootstrap for kotlin/ktor that has automatic injection, based on DSL, and inheritance. And it has some extra tools like scheduler/cron, event bus, etc

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages