#
Adding Jobs
QuickDapp includes a simple worker system for background job processing. The system handles three main job types for maintenance and blockchain operations.
#
Existing Job Types
The worker system includes these built-in job types:
#
removeOldWorkerJobs
Cleans up old completed worker jobs from the database:
// Job runs automatically to remove old jobs
// No custom configuration needed
#
watchChain
Monitors blockchain events and transactions:
// Watches for specific contract events
// Handles transaction confirmations
// Updates application state based on blockchain changes
#
deployMulticall3
Deploys Multicall3 contract for batch operations:
// One-time deployment job
// Enables efficient batch contract calls
// Configures contract addresses in application
#
Job Structure
All jobs follow this basic pattern:
// Job handler interface
export type JobHandler<T = any> = (
job: Job & { data: T },
context: { serverApp: ServerApp }
) => Promise<any>
#
Job Data Format
Jobs are stored in the database with this structure:
interface Job {
id: number
type: 'removeOldWorkerJobs' | 'watchChain' | 'deployMulticall3'
userId: number
data: any
status: 'pending' | 'running' | 'completed' | 'failed'
due: Date
attempts: number
createdAt: Date
updatedAt: Date
}
#
Adding Custom Jobs
To create a new job type, follow these steps:
#
1. Create Job Handler
Create a new job handler file:
// src/server/workers/jobs/myJob.ts
import type { JobHandler } from './types'
import { createLogger } from '../../lib/logger'
const logger = createLogger('job-my-job')
export interface MyJobData {
userId: number
parameters: Record<string, any>
}
export const myJobHandler: JobHandler<MyJobData> = async (job, { serverApp }) => {
const { userId, parameters } = job.data
logger.info('Starting custom job', { jobId: job.id, userId })
try {
// Your job logic here
const result = await processJob(parameters, serverApp)
logger.info('Job completed', { jobId: job.id, result })
return result
} catch (error) {
logger.error('Job failed', { jobId: job.id, error: error.message })
throw error
}
}
async function processJob(params: any, serverApp: any) {
// Implement your job logic
return { success: true }
}
#
2. Register Job Handler
Add your job to the registry:
// src/server/workers/jobs/registry.ts
import { myJobHandler } from './myJob'
export const jobHandlers = {
removeOldWorkerJobs: removeOldWorkerJobsHandler,
watchChain: watchChainHandler,
deployMulticall3: deployMulticall3Handler,
myJob: myJobHandler, // Add your job here
}
#
3. Update Types
Add your job type to the union:
// src/server/workers/jobs/types.ts
export type JobType =
| 'removeOldWorkerJobs'
| 'watchChain'
| 'deployMulticall3'
| 'myJob' // Add your job type
#
4. Submit Jobs
Submit jobs from your application code:
// Example: Submit a custom job
await serverApp.workerManager.submitJob('myJob', {
userId: user.id,
parameters: {
action: 'processData',
data: someData
}
})
#
Best Practices
#
Job Design
- Keep jobs simple and focused on a single task
- Make jobs idempotent (safe to run multiple times)
- Handle errors gracefully with proper logging
- Use appropriate data types for job parameters
#
Error Handling
- Always log job start and completion
- Include relevant context in error messages
- Let errors bubble up for retry handling
- Use structured logging for debugging
#
Performance
- Keep job execution time reasonable (< 5 minutes)
- Use database transactions when needed
- Clean up resources after job completion
- Monitor job queue length and processing times
The worker system provides a simple foundation for background processing while keeping the complexity minimal for most use cases.