Get started

This guide shows how to add QRush to an Actix Web app: define a job, initialize the queues, mount the metrics at /qrush/metrics, and enqueue work (now or later).

Install

cargo add qrush actix-web anyhow async-trait serde chrono

Define a Job

Implement the Job trait. Keep name() and queue() consistent with how you register the handler.

use qrush::job::Job;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};

#[derive(Clone, Serialize, Deserialize)]
pub struct NotifyUser {
    pub user_id: String,
    pub message: String,
}

#[async_trait]
impl Job for NotifyUser {
    async fn perform(&self) -> anyhow::Result<()> {
        println!("Notify {} -> {}", self.user_id, self.message);
        Ok(())
    }

    fn name(&self) -> &'static str { "NotifyUser" }
    fn queue(&self) -> &'static str { "default" }
}

Initialize QRush (your pattern)

Initialize queues in a background task, wait until ready, register your jobs, and expose the metrics routes under /qrush/metrics.

// jobs/qrush.rs
use actix_web::web;
use std::sync::Arc;
use tokio::sync::Notify;
use qrush::config::{QueueConfig, QUEUE_INITIALIZED, set_basic_auth, QrushBasicAuthConfig};
use qrush::registry::register_job;
use crate::jobs::notify_user::NotifyUser;
use qrush::routes::metrics_route::qrush_metrics_routes;

pub struct QrushInitializer;

impl QrushInitializer {
    pub async fn initialize(basic_auth: Option<QrushBasicAuthConfig>) {
        let notify = Arc::new(Notify::new());

        // Optional: enable Basic Auth via env QRUSH_BASIC_AUTH="user:pass"
        let basic_auth = basic_auth.or_else(|| {
            std::env::var("QRUSH_BASIC_AUTH").ok().and_then(|auth| {
                let parts: Vec<&str> = auth.splitn(2, ':').collect();
                (parts.len() == 2).then(|| QrushBasicAuthConfig {
                    username: parts[0].to_string(),
                    password: parts[1].to_string(),
                })
            })
        });
        let _ = set_basic_auth(basic_auth);
        let _ = QUEUE_INITIALIZED.set(notify.clone());

        // Register job handler
        register_job(NotifyUser::name(), NotifyUser::handler);

        // Init queues
        tokio::spawn({
            let notify = notify.clone();
            async move {
                let redis_url = std::env::var("REDIS_URL")
                    .unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
                let queues = vec![
                    QueueConfig::new("default", 5, 1),
                    QueueConfig::new("critical", 10, 0),
                ];
                if QueueConfig::initialize(redis_url, queues).await.is_ok() {
                    println!("✅ qrush started");
                    notify.notify_waiters();
                } else {
                    eprintln!("❌ qrush init failed");
                }
            }
        });

        // Wait until init completes
        notify.notified().await;
    }

    pub fn routes(cfg: &mut web::ServiceConfig) { qrush_metrics_routes(cfg); }
}

Wire into main.rs

use actix_web::{web, App, HttpServer, HttpResponse, Responder, middleware::Logger};
use dotenv::dotenv;
use std::env;
use crate::jobs::qrush::QrushInitializer;
use qrush::queue::{enqueue, enqueue_in};
use crate::jobs::notify_user::NotifyUser;

async fn health_check() -> impl Responder {
    HttpResponse::Ok().body("OK")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();

    // Start QRush and wait until ready
    QrushInitializer::initialize(None).await;

    // Seed a few jobs
    let _ = enqueue(NotifyUser { user_id: "abc".into(), message: "Hello from QRush!".into() }).await;
    let _ = enqueue_in(NotifyUser { user_id: "abc".into(), message: "After 60s".into() }, 60).await;

    let addr = env::var("SERVER_ADDRESS").unwrap_or_else(|_| "0.0.0.0:8083".to_string());
    HttpServer::new(move || {
        App::new()
            .wrap(Logger::default())
            .service(
                web::scope("/qrush")
                    .configure(QrushInitializer::routes) // -> /qrush/metrics/*
            )
            .route("/", web::get().to(health_check))
            .route("/health", web::get().to(health_check))
    })
    .bind(addr)?
    .run()
    .await
}

Environment

export REDIS_URL="redis://127.0.0.1:6379"
# optional
export QRUSH_BASIC_AUTH="admin:strongpassword"
export SERVER_ADDRESS="0.0.0.0:8083"

Open the metrics

  • Dashboard: http://localhost:8083/qrush/metrics
  • Per-queue: /qrush/metrics/queues/default
  • Health: /qrush/metrics/health

Need help? See Support or open an issue on GitHub.