Lifecycle handlers
Roadster provides the
AppLifecycleHandler
trait to allow
apps to hook into various stages of their lifecycle to perform any custom setup or teardown logic. Roadster also
implements some of its built-in lifecycle logic using this trait; for example, DB migrations are performed by the
DbMigrationLifecycleHandler
.
Similar to the HTTP middleware and initializers, lifecycle handlers are run in priority order, and the priority of each
handler can be customized via the app's config files.
Lifecycle hooks
AppLifecycleHandler
s have various hooks to allow managing resources at a particular point in the process of
the app's lifecycle. They are listed below in the order in which they are run:
before_health_checks
: Runs right before the app's health checks are run during startup.before_services
: Runs right before the app's services are started.on_shutdown
: Runs when the app is shutting down after all the app's services have been stopped.
Default lifecycle handlers
For the most up to date list of lifecycle handlers provided by Roadster, see the module's docs.rs page
All of the default lifecycle handlers can be configured via the app's config files. All lifecycle handlers have at least the following config fields:
enable
: Whether the lifecycle handlers is enabled. If not provided, the lifecycle handlers enablement falls back to the value of thelifecycle-handler.default-enable
field.priority
: The priority in which the lifecycle handlers will run. Lower values (including negative numbers) run before higher values. The lifecycle handlers provided by Roadster have priorities between-10,000
(runs first) and10,000
(runs later) by default, though these values can be overridden via configs. If the order your lifecycle handlers runs in doesn't matter, simply set to0
.
Custom middleware
Implement AppLifecycleHandler
Custom middleware can be provided by implementing the
AppLifecycleHandler
trait.
pub struct ExampleLifecycleHandler;
#[async_trait]
impl AppLifecycleHandler<RoadsterApp<AppContext>, AppContext> for ExampleLifecycleHandler {
fn name(&self) -> String {
"example".to_owned()
}
fn enabled(&self, state: &AppContext) -> bool {
// Custom lifecycle handlers can be enabled/disabled via the app config
// just like built-in handlers
state
.config()
.lifecycle_handler
.custom
.get(&self.name())
.map(|config| config.common.enabled(&state))
.unwrap_or_else(|| state.config().health_check.default_enable)
}
fn priority(&self, state: &AppContext) -> i32 {
state
.config()
.lifecycle_handler
.custom
.get(&self.name())
.map(|config| config.common.priority)
.unwrap_or_default()
}
async fn before_health_checks(
&self,
_prepared_app: &PreparedAppWithoutCli<RoadsterApp<AppContext>, AppContext>,
) -> RoadsterResult<()> {
todo!("Implement in order to initialize some state before health checks run")
}
async fn before_services(
&self,
_prepared_app: &PreparedAppWithoutCli<RoadsterApp<AppContext>, AppContext>,
) -> RoadsterResult<()> {
todo!("Implement in order to initialize some state before the app's services are started")
}
async fn on_shutdown(&self, _state: &AppContext) -> RoadsterResult<()> {
todo!("Implement in order to perform any necessary clean up on app shutdown")
}
}
Register the handler
In order to run for the handler to run, it needs to be registered with the app.
fn build_app() -> RoadsterApp<AppContext> {
RoadsterApp::builder()
// Use the default `AppContext` for this example
.state_provider(|context| Ok(context))
// Register custom lifecycle handler(s)
.add_lifecycle_handler_provider(|registry, _state| {
registry.register(ExampleLifecycleHandler)?;
Ok(())
})
.add_service_provider(move |_registry, _state| {
Box::pin(async move { todo!("Add services here.") })
})
.build()
}