Hi! Does graphql-kotlin support dependency injecti...
# graphql-kotlin
d
Hi! Does graphql-kotlin support dependency injection? It seems from the docs that you need to keep some global `val`s for all the repositories, making a bit difficult to set up unit test fakes for them...
d
Hello 👋 It is a pretty good question so by any chance could you open up a corresponding discussion on https://github.com/ExpediaGroup/graphql-kotlin/discussions? (which will make it discoverable for future folks)
d
Sure, I'll try as soon as I have a chancee
d
thanks!
@Sergey Shumov That's getting a bit closer, but as you can see there's 0 tests in that project, and it's not really using DI... I guess it shows how it could be used, but I think the lists being injected into GraphQLKotlin's ktor plugin should really be generated with some kind o
@IntoSet
of a DI...
There's a bunch of things still not clear, like accessing repositories from functions in Entities...
s
For support tests function
confugureDI
may be looks like:
Copy code
fun Application.configureDI(testing: Boolean = false) {
    di {
        bindSingleton {
            if (testing) {
                new(::ArticleRepositoryMocked)
            } else {
                new(::ArticleRepository)
            }
        }
        bindSingleton { new(::UserRepository) }
    }
}
And test:
Copy code
fun testModule1() = testApplication {
    application {
        configureDI(testing = true)
        configureGraphQLModule()
    }
}
Or using other ways of replacing dependencies that the DI library provides.
d
Yeah, that's more or less clear, but what do you do for entities that need a repository?
s
Could you provide an example? If you can't pass the repository explicitly through top-level objects, then you can simply pass the DI object through the ContextMap and get the desired dependency in place.
d
That's soooo uggllllyyy! Using a ContextMap would riddle code with tons of retrievals from the map and casting code.... 🙈 🤕 Unless I'm not understanding what you're proposing?
Maybe there should be a way to directly provide entities with such repositories somehow...
s
I don't quite understand in which case you are having problems providing repositories.
For example, you have a query:
Copy code
class ArticleQuery(
    private val repository: ArticleRepository,
) : Query {
    @GraphQLDescription("List of articles")
    fun articles(
        sortOrder: SortOrder? = null,
        limit: Int? = null,
    ): List<ArticleDto> {
        return repository.list(
            sortOrder = sortOrder ?: SortOrder.DESC,
            limit = limit,
        )
    }
}
When you declare a GraphQL schema, you can pass a repository from DI:
Copy code
queries = listOf(ArticleQuery(articleRepository))
Similarly for mutations and dataloaders.
d
https://github.com/darkxanter/graphql-kotlin-ktor/blob/4b2b414af411044701d9a1eb06b[…]/example/src/main/kotlin/example/feature/articles/ArticleDto.kt
Copy code
@GraphQLName("Article")
@GraphQLDescription("Article")
data class ArticleDto(
    val id: Long,
    val created: OffsetDateTime,
    val authorId: Long,
    val title: String,
    val content: String,
) {
    fun author(dfe: DataFetchingEnvironment): CompletableFuture<User> {
        return dfe.getValueFromDataLoader(UserDataLoader::class, authorId)
    }
}
The dfe way is messier than:
Copy code
@GraphQLName("Article")
@GraphQLDescription("Article")
data class ArticleDto(
    val id: Long,
    val created: OffsetDateTime,
    val authorId: Long,
    val title: String,
    val content: String,
) {
    fun author(authorRepo: AuthorRepository): CompletableFuture<User> {
        return authorRepo.getAuthor(id)
    }
}
Also, it requires making a data loader for each thing...
d
Copy code
Core schema generation logic only concern is with GraphQL and DI is outside of its scope of responsibilities. DI is higher level concept that is handled by your specific server implementation.

Our graphql-kotlin-spring-server library is a SpringBoot autoconfiguration library to easily run reactive GraphQL servers. Since it is SpringBoot based, it does support Spring DI mechanism (see <https://opensource.expediagroup.com/graphql-kotlin/docs/server/spring-server/spring-schema/>).

If you are looking at servers outside of Spring, then you can use whatever mechanism they support, e.g. see Ktor + Koin example (<https://insert-koin.io/docs/reference/koin-ktor/ktor>).

Side note: data loaders should always be accessed through DataFetchingEnvironment (as they are created per execution) and generally should not be injected directly to your classes
117 Views