Cron, Jobs, and Axum
A Rust web framework that handles the boring-but-critical infrastructure so you can focus on your app.
#[derive(Clone)]
struct App { db: PgPool, cookie_key: CookieKey }
impl AppState for App {
fn db(&self) -> &PgPool { &self.db }
fn cookie_key(&self) -> &CookieKey { &self.cookie_key }
}
async fn run() -> Result<()> {
let app = App::from_env().await?;
let tasks = vec![
NamedTask::spawn("server", run_server(routes(app.clone()))),
NamedTask::spawn("jobs", job_worker(app, Jobs)),
];
wait_for_first_error(tasks).await
}Database-backed persistence with retry and exponential backoff. Dead letter queue for failed jobs. Cancellation tokens for graceful shutdown.
#[derive(Debug, Serialize, Deserialize, Clone)]
struct SendEmail { to: String }
#[async_trait]
impl Job<AppState> for SendEmail {
const NAME: &'static str = "SendEmail";
async fn run(&self, state: AppState) -> Result<()> {
// your logic here
Ok(())
}
}Interval or cron expression scheduling with timezone awareness. Distributed locking ensures single execution across instances.
let mut registry = CronRegistry::new();
registry.register_job(
CleanupJob,
Some("Remove expired sessions"),
Duration::from_secs(3600),
);
registry.register_with_cron(
"daily-report", None,
"0 0 9 * * *", // 9am daily
|state, _| Box::pin(async move {
generate_report(state).await
}),
)?;Axum-based HTTP server with cookie-based DB-backed sessions, structured tracing with OpenTelemetry and Sentry, and zero-downtime reloading.
fn routes(state: AppState) -> Router {
Router::new()
.route("/", get(home))
.route("/api/items", post(create_item))
.with_state(state)
}
// Sessions work as extractors:
async fn home(
Session(session): Session<MySession>,
) -> impl IntoResponse { /* ... */ }use cja::app_state::AppState;
use cja::server::cookies::CookieKey;
use cja::jobs::Job;
#[derive(Clone)]
struct App { db: PgPool, cookie_key: CookieKey }
impl AppState for App {
fn version(&self) -> &str { env!("CARGO_PKG_VERSION") }
fn db(&self) -> &PgPool { &self.db }
fn cookie_key(&self) -> &CookieKey { &self.cookie_key }
}
#[derive(Debug, Serialize, Deserialize, Clone)]
struct Digest;
#[async_trait]
impl Job<App> for Digest {
const NAME: &'static str = "Digest";
async fn run(&self, app: App) -> Result<()> {
// send the daily digest
Ok(())
}
}
fn routes(app: App) -> Router {
Router::new()
.route("/", get(|| async { "Hello from CJA" }))
.with_state(app)
}cargo add cjaexport DATABASE_URL=postgres://localhost/myappcargo runRequires PostgreSQL. See the full documentation for a complete guide.