Dependency Injection
The Dependency Injection (DI) plugin allows you to register services and configuration objects once and inject them into your application modules, plugins, routes, and other components throughout your project. Ktor's DI is designed to integrate naturally with its existing application lifecycle, supporting scoping and structured configuration out of the box.
Add dependencies
To use DI, include the ktor-server-di
artifact in your build script:
Basic dependency registration
You can register dependencies using lambdas, function references, or constructor references:
Configuration-based dependency registration
You can configure dependencies declaratively using classpath references in your configuration file. This supports both function and class references:
Ktor resolves constructor and function parameters automatically using the DI container. You can use annotations like @Property
or @Named
to override or explicitly bind parameters in special cases, such as when the type alone is not enough to distinguish a value. If omitted, Ktor will attempt to resolve parameters by type using the DI container.
Dependency resolution and injection
Resolving dependencies
To resolve dependencies, you can use property delegation or direct resolution:
Asynchronous dependency resolution
To support asynchronous loading, you can use suspending functions:
The DI plugin will automatically suspend resolve()
calls until all dependencies are ready.
Injecting into application modules
You can inject dependencies directly into application modules by specifying parameters in the module function. Ktor will resolve these dependencies from the DI container based on type matching.
First, register your dependency providers in the dependencies
section of the config:
Here’s what the dependency provider and module function look like:
Use @Named
for injecting specifically keyed dependencies:
Property and configuration injection
Use @Property
to inject configuration values directly:
This simplifies working with structured configuration and supports automatic parsing of primitive types.
Advanced dependency features
Optional and nullable dependencies
Use nullable types to handle optional dependencies gracefully:
Covariant generics
Ktor's DI system supports type covariance, which allows injecting a value as one of its supertypes when the type parameter is covariant. This is especially useful for collections and interfaces that work with subtypes.
Covariance also works with non-generic supertypes:
Limitations
While the DI system supports covariance for generic types, it currently does not support resolving parameterized types across type argument subtypes. That means you cannot retrieve a dependency using a type that is more specific or more general than what was registered.
For example, the following code will not resolve:
Resource lifecycle management
The DI plugin handles lifecycle and cleanup automatically when the application shuts down.
AutoCloseable support
By default, any dependency that implements AutoCloseable
is automatically closed when your application stops:
Custom cleanup logic
You can define custom cleanup logic by specifying a cleanup
function:
Scoped cleanup with key
Use key
to manage named resources and their cleanup:
Dependencies are cleaned up in reverse order of declaration to ensure proper teardown.
Testing with dependency injection
The DI plugin provides tooling to simplify testing. You can override dependencies before loading your application modules:
Loading configuration in tests
Use configure()
to load configuration files easily in your tests:
Conflicting declarations are ignored by the test engine to let you override freely.