Modules
Ktor allows you to use modules to structure your application by defining a specific set of routes inside a specific module. A module is an extension function on Application that sets up routes, installs plugins, and configures services. Using modules helps you:
Group related routes and logic together.
Keep features or domains isolated.
Enable easier testing and modular deployment.
Defining a module
A module is an extension function of the Application class. In the example below, the module1 extension function defines a module that accepts GET requests made to the /module1 URL path.
Loading modules in your application depends on the way used to create a server: in code using the embeddedServer function or by using the application.conf configuration file.
Loading modules
Embedded server
Typically, the embeddedServer function accepts a module implicitly as a lambda argument. You can see the example in the Configuration in code section. You can also extract application logic into a separate module and pass a reference to this module as the module parameter:
You can find the full example here: embedded-server-modules.
Configuration file
If you use the application.conf or application.yaml file to configure a server, you need to specify modules to load using the ktor.application.modules property.
Suppose you have three modules defined in two packages: two modules in the com.example package and one in the org.sample package.
To reference these modules in a configuration file, you need to provide their fully qualified names. A fully qualified module name includes a fully qualified name of the class and an extension function name.
You can find the full example here: engine-main-modules.
Module dependencies
Modules often need to share common services, repositories, or configuration. Injecting dependencies rather than creating them inside a module improves testability and flexibility. Ktor offers several approaches depending on the complexity of your project.
Passing dependencies through parameters
The simplest way to pass dependencies is to declare them as parameters of module functions:
This works well for small or medium applications and keeps dependencies clear. However, modules become tightly coupled at compile time and cannot be easily swapped at runtime.
Using application attributes
You can use Application.attributes - a type-safe map available to all modules:
This creates loose coupling by avoiding direct references between modules.
Using dependency injection
Ktor includes a dependency injection (DI) plugin, which allows you to declare and resolve dependencies directly inside your Ktor application using a lightweight container.
Concurrent modules
You can use suspendable functions when creating application modules. They allow events to run asynchronously when starting the application. To do that, add the suspend keyword:
You can also launch all application modules independently, so when one is suspended, the others are not blocked. This allows for non-sequential loading for dependency injection and, in some cases, faster loading.
Configuration options
The following configuration properties are available:
Property | Type | Description | Default |
|---|---|---|---|
|
| Defines how application modules are loaded |
|
|
| Timeout for application module loading (in milliseconds) |
|
Enable concurrent module loading
To opt into concurrent module loading, add the following to your server configuration file:
For dependency injection, you can load the following modules in order of appearance without issues: