Quickstart
Build your first task queue in 5 minutes.
Build your first task queue in 5 minutes.
Four pieces make up the core loop — a task, a worker, an enqueued job, and its result — plus how to check on the queue afterward. No broker to stand up: everything below talks to one embedded store.
A task is a named function registered on the queue. Producers enqueue by name; a worker executes the registered function and stores its return value as the result.
Create a file called tasks.py:
from flexiq import Queue
# Create a queue backed by SQLite
queue = Queue(db_path="tasks.db")
@queue.task()
def add(a: int, b: int) -> int:
return a + b
@queue.task(max_retries=3, retry_backoff=2.0)
def send_email(to: str, subject: str, body: str) -> str:
# Your email sending logic here
print(f"Sending email to {to}: {subject}")
return f"sent to {to}"import { Queue } from "@byteveda/flexiq";
const queue = new Queue({ dbPath: "flexiq.db" });
// Register a task with optional per-task config.
queue.task("add", (a: number, b: number) => a + b, {
maxRetries: 3,
retryBackoff: { baseMs: 1000, maxMs: 60_000 },
timeoutMs: 30_000,
maxConcurrent: 4,
});import com.fasterxml.jackson.core.type.TypeReference;
import java.time.Duration;
import java.util.Map;
import org.byteveda.flexiq.FlexiQ;
import org.byteveda.flexiq.task.Task;
// A task is a name plus its payload type — the producer never needs the handler body.
Task<Map<String, Integer>> add =
Task.of("add", new TypeReference<Map<String, Integer>>() {})
.retries(3)
.timeout(Duration.ofSeconds(30));
FlexiQ flexiq = FlexiQ.builder().sqlite("flexiq.db").open();A worker polls storage, claims due jobs, and dispatches them to the registered functions above. Start one before you enqueue — or leave it running while you enqueue in the next step:
# Runs in its own terminal and blocks, processing jobs until you stop it (Ctrl+C)
flexiq worker --app tasks:queue# Runs the worker in a background thread inside your own process
import threading
from tasks import queue
t = threading.Thread(target=queue.run_worker, daemon=True)
t.start()import asyncio
from tasks import queue
async def main():
await queue.arun_worker()
asyncio.run(main())# Loads the module that registers the tasks above and runs a worker over them
flexiq run ./app.js --queues default// Same process, or a separate one pointed at the same dbPath / DSN
const worker = queue.runWorker({ queues: ["default"] });import org.byteveda.flexiq.worker.Worker;
Worker worker = flexiq.worker()
.handle(add, p -> p.get("a") + p.get("b"))
.start();
// ... later: worker.close();Nothing executes until a worker is consuming the queue. Enqueued jobs sit in
pending until a worker claims them — so leave this worker running while you
enqueue jobs in the next step.
From another process (or anywhere in your app), enqueue a job and read its result. The worker from step 2 picks it up and runs it:
from tasks import add
# Enqueue — returns a JobResult handle immediately; the function does NOT run here
job = add.delay(2, 3)
print(f"Job ID: {job.id}") # 01936...
print(f"Status: {job.status}") # "pending" — until a worker claims it
# Block until the worker finishes the job (exponential-backoff polling)
result = job.result(timeout=30)
print(result) # 5
# Async variant
result = await job.aresult(timeout=30)// Enqueue a job (producer).
const id = queue.enqueue("add", [2, 3], { priority: 5 });
// Await the result — the worker from step 2 picks it up and runs it.
const result = await queue.result(id); // 5
worker.stop();// Enqueue a job (producer).
String id = flexiq.enqueue(add, Map.of("a", 2, "b", 3));
// Block until the job is terminal, then read its result.
flexiq.awaitJob(id, Duration.ofSeconds(20));
int result = flexiq.getResult(id, Integer.class).orElseThrow(); // 5
worker.close();Check queue health without leaving the terminal:
from tasks import queue
stats = queue.stats()
print(stats)
# {'pending': 0, 'running': 0, 'completed': 5, 'failed': 0, 'dead': 0, 'cancelled': 0}Or the CLI:
flexiq info --app tasks:queue# Live dashboard, refreshes every 2s
flexiq info --app tasks:queue --watchawait queue.stats(); // { pending, running, completed, failed, dead, cancelled }Or the CLI:
flexiq --db flexiq.db statsQueueStats stats = flexiq.stats(); // pending, running, completed, failed, dead, cancelledOr the CLI:
flexiq --url flexiq.db statsFor a full visual interface — job browsing, metrics charts, dead letter management, and queue controls — run the bundled dashboard. It ships in the package itself; no separate service or extra dependencies:
pnpm build:dashboard # builds the SPA into static/dashboard (one-time)
flexiq --db flexiq.db dashboard --port 8787Open http://localhost:8787 in your browser.
flexiq --url flexiq.db dashboard --port 8080 --token "$DASH_TOKEN"Open http://localhost:8080 in your browser.