| Crates.io | axum-tasks-derive |
| lib.rs | axum-tasks-derive |
| version | 0.1.11 |
| created_at | 2025-06-11 11:45:06.720496+00 |
| updated_at | 2025-08-17 23:22:11.611769+00 |
| description | Derive macros for axum-tasks - a lightweight background task queue |
| homepage | https://github.com/alexlatif/axum-tasks |
| repository | https://github.com/alexlatif/axum-tasks |
| max_upload_size | |
| id | 1708482 |
| size | 14,400 |
A lightweight background task queue for Axum applications with automatic persistence and monitoring.
tokio and flume[dependencies]
axum-tasks = "0.1"
axum = "0.7"
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
Define a task:
use axum_tasks::{Task, TaskResult};
use serde::{Deserialize, Serialize};
#[derive(Task, Debug, Clone, Serialize, Deserialize)]
pub struct SendEmail {
pub to: String,
pub subject: String,
}
impl SendEmail {
async fn execute(&self) -> TaskResult {
// Your logic here
send_email(&self.to, &self.subject).await?;
TaskResult::Success
}
}
Set up your application:
use axum_tasks::{AppTasks, HasTasks, admin_routes, spawn_task_workers};
#[derive(Clone, HasTasks)]
pub struct AppState {
pub tasks: AppTasks,
}
#[tokio::main]
async fn main() {
let app_state = AppState {
tasks: AppTasks::new().with_auto_persist(|states| {
tokio::spawn(async move {
let json = serde_json::to_string_pretty(states).unwrap();
tokio::fs::write("tasks.json", json).await.ok();
});
}),
};
// Start workers
let shutdown = tokio_util::sync::CancellationToken::new();
spawn_task_workers(app_state.tasks.clone(), shutdown.clone(), None).await;
// Build router
let app = axum::Router::new()
.route("/send", axum::routing::post(queue_email))
.nest("/admin", admin_routes::<AppState>())
.with_state(app_state);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn queue_email(
axum::extract::State(state): axum::extract::State<AppState>,
axum::Json(email): axum::Json<SendEmail>,
) -> Result<String, String> {
state.tasks.queue(email).await.map_err(|e| e.to_string())
}
The library automatically provides:
GET /admin/health - System health and queue statusGET /admin/tasks - List all tasks with optional filteringGET /admin/metrics - Performance metrics and throughput dataPOST /admin/cleanup - Remove completed tasks older than specified timeimpl MyTask {
async fn execute(&self) -> TaskResult {
match perform_work().await {
Ok(_) => TaskResult::Success,
Err(e) if e.is_retryable() => TaskResult::RetryableError(e.to_string()),
Err(e) => TaskResult::PermanentError(e.to_string()),
}
}
}
Retryable errors use exponential backoff. Permanent errors are marked as failed immediately.
Worker count defaults to max(4, num_cpus / 2). Override with:
spawn_task_workers(tasks, shutdown, Some(8)).await;
Persistence is user-controlled. The library provides task state, you choose how to save it:
AppTasks::new().with_auto_persist(|task_states| {
// Save to PostgreSQL, Redis, file system, etc.
save_to_your_backend(task_states);
});
tasks: AppTasks needed in your stateLicensed under the MIT License.