Skip to main content
Version: 4.2

Isolated Context in Ktor

This page covers the KoinIsolated plugin, which starts Koin for one Ktor application without registering it in the global context. Each application then has its own container. What an isolated context is, and when to use one, is explained in Context Isolation. The regular Koin plugin is described in Koin for Ktor.

Installing KoinIsolated​

Install KoinIsolated instead of Koin. The configuration block is the same:

import org.koin.ktor.plugin.KoinIsolated

fun Application.module() {
install(KoinIsolated) {
slf4jLogger()
modules(appModule)
}
}

What changes compared to Koin​

install(Koin)install(KoinIsolated)
Registered in the global contextYesNo
inject() / get() on Application, Route, Routing, ApplicationCallYesYes, from the application's instance
Request scope and call.scopeYesYes
Lifecycle events, close on ApplicationStoppingYesYes
bridge { } to Ktor DIYesNot applied in Koin 4.2
KoinComponent classes, KoinPlatform.getKoin()See this instanceDon't see this instance

The Ktor extensions read the Koin instance stored in the application attributes, so injection in routes works the same:

fun Application.module() {
install(KoinIsolated) {
modules(appModule)
}

val userService by inject<UserService>()
val koin = getKoin() // this application's Koin instance

routing {
get("/users/{id}") {
val logger = call.scope.get<RequestLogger>()
call.respond(userService.getUser(call.parameters["id"]!!))
}
}
}
warning

Code outside Ktor's receivers can't reach the isolated instance. A class implementing KoinComponent, or a call to KoinPlatform.getKoin(), uses the global context and fails with "KoinApplication has not been started". Give your classes their dependencies through constructor parameters, declared in the isolated modules. For an interop entry point that must retrieve dependencies itself, override getKoin() as shown in Context Isolation.

Several servers in one JVM​

Each embedded server gets its own container:

fun main() {
val userServer = embeddedServer(Netty, port = 8080) {
install(KoinIsolated) { modules(userServiceModule) }
userRouting()
}

val orderServer = embeddedServer(Netty, port = 8081) {
install(KoinIsolated) { modules(orderServiceModule) }
orderRouting()
}

userServer.start(wait = false)
orderServer.start(wait = true)
}

With install(Koin), the second server would stop the global Koin of the first one, since the plugin stops a running global instance before it starts.

Tests​

Each testApplication that installs KoinIsolated has its own container, so tests don't share state through the global context and can run in parallel. Test setup is covered in Ktor Testing.

When to keep the global context​

Stay with install(Koin) when one Ktor application runs in the process and other entry points of that process (a scheduled job started from main(), for example) must use the same container, or when you need the Ktor DI bridge.