Job Class
A job must extend the Job
class from Sidequest.js. The job class defines the work to be performed and provides the structure for job execution. By extending this class and implementing the run
method, you define the logic that will be executed when the job is processed and Sidequest will take care of the lifecycle management, including retries, error handling, and state transitions.
Basic Job Structure
Every job class must:
- Extend the
Job
base class - Implement a
run
method that contains the job logic - Be exported from a module so it can be dynamically imported
import { Job } from "@sidequest/engine";
export class MyJob extends Job {
async run(arg1: string, arg2: number) {
// Your job logic here
console.log(`Processing ${arg1} with value ${arg2}`);
return { processed: true, result: arg1.toUpperCase() };
}
}
Constructor Parameters
Jobs can accept constructor parameters for configuration or dependencies:
import { Job } from "@sidequest/engine";
export class EmailJob extends Job {
constructor(
private emailService: EmailService,
private config: EmailConfig,
) {
super();
}
async run(to: string, subject: string, body: string) {
await this.emailService.send({
to,
subject,
body,
from: this.config.defaultSender,
});
return { emailSent: true, timestamp: new Date() };
}
}
Job Script Detection
Sidequest automatically detects the file path of your job class using stack trace analysis. This means:
- Jobs must be defined in their own files or imported modules
- The class must be exported (named or default export)
- Script files must be compiled to JavaScript if using TypeScript
TIP
If you are using Node.js >= 24, you can run TypeScript jobs directly without pre-compiling them to JavaScript.
- The file must be accessible at runtime by the Sidequest engine
WARNING
If you are distributing Sidequest in multiples machines, the script file path must be consistent across all machines, otherwise the engine won't be able to find the job script.
- Dependencies must be accessible in the job's context
By using this strategy, you do not need to manually specify the job script path when enqueuing jobs. Sidequest will automatically resolve it based on the class definition. If you are using TypeScript, this also allows Sidequest.js to provide type safety for the jobs when enqueueing them.
Job Data Properties
During the job execution, jobs can access various properties that provide context about the job execution. These properties come from the database and follow the JobData interface, as explained in the Job Metadata Section. For convenience, here is the interface definition:
/**
* Represents the data structure for a job in the queue system.
*
* Contains metadata, execution details, state tracking, error reporting,
* uniqueness configuration, and timestamps for lifecycle events.
*
* @remarks
* This interface is used to define the schema for jobs managed by the queue,
* including information about execution attempts, results, errors, and
* worker claims.
*/
export interface JobData {
/**
* Unique identifier for the job.
*/
id: number;
/**
* Name of the queue this job belongs to.
*/
queue: string;
/**
* Current state of the job.
*/
state: JobState;
/**
* The script or module associated with the job.
*/
script: string;
/**
* The class name responsible for executing the job.
*/
class: string;
/**
* Arguments to be passed to the job's execution method.
*/
args: unknown[];
/**
* Arguments to be passed to the job's constructor.
*/
constructor_args: unknown[];
/**
* Number of times this job has been attempted.
*/
attempt: number;
/**
* Maximum number of attempts allowed for this job.
*/
max_attempts: number;
/**
* Timestamp when the job was inserted into the queue.
*/
inserted_at: Date;
/**
* Timestamp when the job becomes available for processing.
*/
available_at: Date;
/**
* Maximum allowed execution time for the job, in milliseconds. Null if no timeout is set.
*/
timeout: number | null;
/**
* Result of the job execution, if available.
*/
result: Omit<unknown, "undefined"> | null;
/**
* List of errors encountered during job execution, if any.
*/
errors: ErrorData[] | null;
/**
* Timestamp when the job was last attempted, or null if never attempted.
*/
attempted_at: Date | null;
/**
* Timestamp when the job was completed, or null if not completed.
*/
completed_at: Date | null;
/**
* Timestamp when the job failed, or null if not failed.
*/
failed_at: Date | null;
/**
* Timestamp when the job was canceled, or null if not canceled.
*/
canceled_at: Date | null;
/**
* Timestamp when the job was claimed by a worker, or null if not claimed.
*/
claimed_at: Date | null;
/**
* Identifier of the worker that claimed the job, or null if not claimed.
*/
claimed_by: string | null;
/**
* Unique digest string used for job uniqueness, or null if not set.
*/
unique_digest: string | null;
/**
* Configuration object for job uniqueness, or null if not set.
*/
uniqueness_config: UniquenessConfig | null;
}
You can access these properties directly in your job run method using this
:
import { Job } from "@sidequest/engine";
export class MyJob extends Job {
async run() {
console.log(`Job ID: ${this.id}`);
console.log(`Job State: ${this.state}`);
console.log(`Job Inserted At: ${this.inserted_at}`);
}
}
WARNING
These properties are read-only and should not be modified directly. They are provided for informational purposes and to help with job logic. If you modify them, it will not affect the job's state in the database.
WARNING
Sidequest injects these properties in runtime and only after creating the job object. They will only be available after the job starts executing, so you cannot access them in the constructor or before the run
method is called.
Class Naming Conventions
While not required, following these conventions helps with organization:
- Use descriptive names ending in "Job":
SendEmailJob
,ProcessPaymentJob
- Use PascalCase for class names
- Group related jobs in the same directory or module
Best Practices
- Keep jobs focused: Each job should have a single responsibility
- Make jobs idempotent: Jobs should be safe to retry
- Use meaningful return values: Return data that might be useful for debugging or downstream processing
- Handle dependencies carefully: Consider how to provide services and configurations to your jobs
- Add proper logging: Use the built-in logger for debugging and monitoring
- Validate inputs: Check that required arguments are present and valid