Core

Application lifecycle, type-safe routing, CLI framework, fixtures, configuration, and repair system.

App lifecycle

Funktor provides hooks for every stage of the application lifecycle. Implement the hook interface, register it in Kontainer, and it runs automatically.

class MyStartupHook : AppLifeCycleHooks.OnAppStarted {
    override val executionOrder = ExecutionOrder.Normal

    override suspend fun onAppStarted(application: Application) {
        // Run after the app has started
    }
}

Available hooks: OnAppStarting, OnAppStarted, OnAppStopPreparing, OnAppStopping, OnAppStopped. Each supports ExecutionOrder for priority control (ExtremelyEarly through ExtremelyLate).

Type-safe routing

Routes carry their parameter types at compile time. No stringly-typed path extraction.

data class UserParams(val id: String)

val getUserRoute = route<UserParams>("/users/{id}")

// In routing setup:
get(getUserRoute) { params ->
    val user = userService.get(params.id)
    call.respond(user)
}

CLI framework

Register Clikt commands in Kontainer. Run them with --cli. Built-in commands include app:info, app:config, fixtures:install, and fixtures:list.

class MyCommand : CliktCommand(name = "my:command") {
    val count by option().int().default(10)

    override fun run() {
        echo("Running with count=$count")
    }
}

// Register in module:
val MyModule = module {
    singleton(MyCommand::class)
}

Fixtures

Load test data with dependency ordering. Fixtures declare what they depend on, and the installer resolves the correct execution order.

class UserFixtures(
    repo: UserRepo,
    private val authRecords: AuthRecordStorage,
) : RepoFixtureLoader<User>(repo = repo) {

    val admin = singleFix {
        repo.insert("admin", User(name = "Admin", email = "admin@example.com"))
            .also { user -> authRecords.createPassword(user, "password123") }
    }

    override val dependsOn = listOf<FixtureLoader>()
}

Configuration

Type-safe configuration via Typesafe Config. Environment-specific files (application.dev.conf, application.prod.conf) are merged automatically.

Repair system

Register RepairMan.Repair implementations to run data migrations or fixes on startup. Failures are logged but don't prevent the app from starting.

Fixtures are for dev/test. Repairs are for production. Both run automatically — fixtures via CLI, repairs on every startup.