is there a clever way to inject my dependencies wh...
# ktor
b
is there a clever way to inject my dependencies when using extension functions of Route? atm i have a method like this but i always have to pass through my dependencies to other method calls. this feels a bit clumsy
👍 1
e
I'm curious about this as well. I have quite a few routes in my API and some of them have more than 6 dependencies. This is troublesome specially in cases where routes are nested (e.g.
/accounts/123/items/123
) - then you have to nest these functions and it becomes a dependency hell. I'm thinking about refactoring my app, this is the approach I currently have though of:
Copy code
interface AccountService {
    suspend fun getAccounts(): List<Account>
}

abstract class Handler(private val path: String) {
    fun init(root: Route) {
        root.route(path) {
            routes()
        }
    }

    protected abstract fun Route.routes()
}

class AccountHandler(
    private val accountService: AccountService
) : Handler("/accounts") {

    override fun Route.routes() {
        get {
            call.respond(accountService.getAccounts())
        }
        
        // List other API calls here.
    }
}

fun Application.main() {
    val accountService = object : AccountService {
        override suspend fun getAccounts() = emptyList<Account>()
    }

    // List all handlers here (order is important).
    val handlers = listOf<Handler>(
        AccountHandler(accountService)
    )

    routing {
        route("/api") {
            handlers.forEach { handler ->
                handler.init(this)
            }
        }
    }
}
Haven't yet implemented this in prod, but maybe it will work out for you.
The nice thing about this method, is that you can setup a dependency injection framework for the
Handler
classes if you really want to.
b
what i do now is that i have all my code extracted to handlers and in the route itself i only extract the json from the request and the handler returns the response json which i return in the route function with call.respond(someJsonObject). i am not sure if i want to pass the call object to the handler .. for now i decided against it. problem with that approach is the i have some validation in route and some of it in the handler.
e
You are right, the
ApplicationCall
object should not be passed to your
*Handler
classes. The API/Json stuff should remain on extension functions and business logic should stay on services (or handlers as in your case).
b
i know that ktor tries to be unbiased .. but i wonder if there is something like a best practice approach to that problem.
e
Both the Kodein and Koin dependency injection frameworks have Ktor features that you can install to retrieve services on your routes
d
If you think you have too many dependencies, the answer is not to introduce a magical DI system, but to refactor and extract a higher level abstraction that may be missing.
👍 2
b
DI framework would be the last step for me .. i have no need for that atm. the question was more about the extension functions in combination with dependencies. i can pass them to the Route.someFunc(dep1, dep2) but i can't store them in a field so they are reachable from other functions. (btw using the locations API)