Background Jobs
Background jobs in SolidX enable asynchronous task processing, making it easy to offload work that doesn’t need to run immediately. Common use cases include:
- Sending emails or notifications
- Deferring non-urgent tasks
- Handling heavy computations in the background
SolidX implements this using a message queue system based on the Work Queue / Competing Consumers pattern:
- Publishers push jobs into a queue
- Subscribers pick up and process jobs asynchronously
Job execution is fully tracked with support for status updates, retries, and failures:
- ss_mq_message → stores individual queue messages
- ss_mq_message_queue → stores job queue definitions
SolidX supports both:
- Database-backed queues – simple, lightweight, no external dependencies
- RabbitMQ – robust, production-ready, and recommended for high-throughput systems
Setting Up a Background Job
1. Define Queue Options
Specify the queue name and broker type in an options object. Below is an example configuration for a database-backed queue for sending emails.
email-queue-options-database.ts
import { BrokerType } from "src/interfaces";
const MAIL_QUEUE_NAME = "solidx.email.db";
//const MAIL_QUEUE_NAME = "solidx.email.rabbitmq"; //For RabbitMQ
export default {
name: "solidEmailInstance",
type: BrokerType.Database,
//type: BrokerType.Rabbitmq //For RabbitMQ
queueName: MAIL_QUEUE_NAME,
};
2. Configure a Publisher
We need to create a publisher class which extends the appropriate base publisher class based on the broker type and specify the queue options.
email-queue-publisher-database.ts
import { Injectable } from "@nestjs/common";
import mailQueueOptions from "./email-queue-options-database";
import { MqMessageQueueService } from "src/services/mq-message-queue.service";
import { MqMessageService } from "src/services/mq-message.service";
import { QueuesModuleOptions } from "src/interfaces";
import { DatabasePublisher } from "src/services/queues/database-publisher.service";
@Injectable()
export class EmailQueuePublisherDatabase extends DatabasePublisher<any> {
constructor(
protected readonly mqMessageService: MqMessageService,
protected readonly mqMessageQueueService: MqMessageQueueService
) {
super(mqMessageService, mqMessageQueueService);
}
options(): QueuesModuleOptions {
return {
...mailQueueOptions,
};
}
}
Info
In the near future, you need not create a publisher. Only subscriber needs to be created.
3. Configure a Subscriber
Subscribers process messages from the queue. They house the actual job processing logic. Below is an example subscriber that sends emails using the SMTP service.
email-queue-subscriber-database.ts
import { Injectable } from "@nestjs/common";
import mailQueueOptions from "./email-queue-options-database";
import { QueueMessage } from "src/interfaces/mq";
import { MqMessageService } from "src/services/mq-message.service";
import { MqMessageQueueService } from "src/services/mq-message-queue.service";
import { DatabaseSubscriber } from "src/services/queues/database-subscriber.service";
import { SMTPEMailService } from "src/services/mail/SMTPEmailService";
import { QueuesModuleOptions } from "src/interfaces";
@Injectable()
export class EmailQueueSubscriberDatabase extends DatabaseSubscriber<any> {
constructor(
private readonly mailFactory: MailServiceFactory,
readonly mqMessageService: MqMessageService,
readonly mqMessageQueueService: MqMessageQueueService
) {
super(mqMessageService, mqMessageQueueService);
}
options(): QueuesModuleOptions {
return {
...mailQueueOptions,
};
}
subscribe(message: QueueMessage<any>) {
const mailService = this.mailFactory.getMailService();
return mailService.sendEmailSynchronously(message);
}
}
Tip
Keep your subscribe method clean and simple. Keep the actual logic in a separate service and call it from the subscribe method.
Info
The above examples use a database broker. For RabbitMQ, simply switch the base classes to RabbitmqPublisher and RabbitmqSubscriber, and update the queue options accordingly.
Naming Convention
The publisher and subscriber names should follow a convention based on the broker type:
NameDatabasefor database brokerNameRabbitmqfor RabbitMQ broker
They are standard NestJS providers and must be registered in their respective modules.
Database Tables
ss_mq_message_queue: Queue names registryss_mq_message: Stores message details including status, retries, payload
Environment Variable
Broker
-
QUEUES_DEFAULT_BROKER
Choose the broker for background jobs:"database"→ Database broker (default)"rabbitmq"→ RabbitMQ broker
-
QUEUES_RABBIT_MQ_URL(RabbitMQ only)
RabbitMQ connection string, e.g.:
amqp://guest:guest@127.0.0.1:5672
Service Role
QUEUES_SERVICE_ROLE
Defines how this service instance participates:"subscriber"→ Only processes jobs"both"→ Publishes and processes jobs
(useful for distributed job handling)
Info
In a distributed setup, you can have some instances only processing jobs while others handle both publishing and processing. This is useful for load balancing and scaling for e.g (you can set the QUEUES_SERVICE_ROLE to subscriber on multiple instances to only process jobs, while having one instance set to both to handle publishing/subscribing).
Email Jobs
COMMON_EMAIL_SHOULD_QUEUEtrue→ Send emails via background jobsfalse→ Send emails synchronously (default)
SMS Jobs
COMMON_SMS_SHOULD_QUEUEtrue→ Send SMS via background jobsfalse→ Send SMS synchronously (default)
Supported Brokers
1 Database Broker
- Jobs are stored in
ss_mq_message - Uses polling to fetch and process jobs every second
- Best for lightweight or dependency-free setups
2 RabbitMQ Broker
- Jobs processed via RabbitMQ queues
- Uses
amqplibfor message handling - Best for larger-scale systems requiring reliability and routing
- Management UI: http://localhost:15672
- Default login:
guest / guest - Set
QUEUES_RABBIT_MQ_URLto connect to your RabbitMQ instance