koin
koin copied to clipboard
Ktor DI service per Request
Is your feature request related to a problem? Please describe. We would like to inject a service instance for every request. So it will be different for every request, but the same everywhere within this request.
Describe the solution you'd like It would be great to have a request-scope that integrates with Ktor
Describe alternatives you've considered Currently, I am considering rolling my own solution for this. But, I know some DI frameworks for C# for example have this type of scope.
I ended up using the Scoped feature and attached that scope at the beginning of the pipeline.. It works well, but adding this functionality to koin-ktor would be awesome
I ended up using the Scoped feature and attached that scope at the beginning of the pipeline.. It works well, but adding this functionality to koin-ktor would be awesome
Could you explain it with some details, please ? (for beginners) .. or maybe you have a link to some repository Thank you.
@SinyakovYuri you will need middleware, that creates scope and closes it after request is sent to user.
We are extending KoinScopeComponent
for attaching resources to it and close them (but I think generic scope will do):
/**
* Koin scope, that has attached resources.
*
* Contains list of [Closeable], which should be released upon [closeScope] call.
*/
class ScopeWithResources : KoinScopeComponent {
override val scope: Scope by lazy { createScope(this) }
private val resources = mutableListOf<Closeable>()
/**
* Attach [resource] to scope.
*/
fun <T : Closeable> with(resource: T): T {
resources.add(resource)
return resource
}
/**
* Release resources and close scope.
*/
override fun closeScope() {
resources.forEach { it.close() }
super.closeScope()
}
}
Then you need actual middleware, that creates this scope and attaches it to attributes:
private val requestScopeKey = AttributeKey<Scope>("requestScopeKey")
// Helper extension to get scope in ApplicationRequest
val ApplicationRequest.scope
get() = this.call.attributes[requestScopeKey]
val RequestScope = createApplicationPlugin(name = "RequestScope") {
on(CallSetup) { call ->
val scopeComponent = ScopeWithResources()
call.attributes.put(requestScopeKey, scopeComponent.scope)
}
on(ResponseSent) {
call.attributes[requestScopeKey]?.closeScope()
}
}
And you are ready to go, just install(RequestScope)
in application and get scope
from extension function to create scoped instances, that will be released on request end.
install(RequestScope)
routing {
get("/") {
call.request.scope.get<MyService>()
}
}
Upd. fixed https://github.com/InsertKoinIO/koin/issues/1374#issuecomment-1701823968
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
still on the roadmap 👍
The solution by @floatdrop is broken if multiple requests are active, you can't save a reference to the scope as a local variable in the plugin instance.
this is the correct approach:
class CallScope : KoinScopeComponent {
override val scope = createScope()
}
private val callScopeKey = AttributeKey<Scope>("callScopeKey")
val ApplicationCall.scope
get() = this.attributes[callScopeKey]
val CallScopePlugin = createApplicationPlugin("CallScopePlugin") {
on(CallSetup) { call ->
val callScope = CallScope()
call.attributes.put(callScopeKey, callScope.scope)
// Optional, you can inject the call itself in the container so it's available to the scoped beans
callScope.scope.declare(call)
}
on(ResponseSent) { call ->
call.attributes[callScopeKey].close()
}
}
Added in 3.5.0, part of the main Koin plugin 👍 2e491fb0977be9bcaec2ec95be90751d3ae9456a
@arnaudgiuliani the request headers are not available to scoped beans out of the box. is there any intention to make this available in the future.
@sharifahmad2061 you mean having access to the request object from Koin scope?
@arnaudgiuliani Are there any examples for RequestScope usage? Does RequestScope allow to get the applicationCall or any of the request details, so they can be passed to the any of scoped instances constructors?
take a look at https://insert-koin.io/docs/reference/koin-ktor/ktor#resolve-from-ktor-request-scope-since-350 it shoud help you understand details of RequestScope