{"id":10868,"library":"faktory-worker","title":"Faktory Worker for Node.js","description":"Faktory-worker is a robust Node.js client and worker library designed to integrate with the Faktory job server, enabling asynchronous background job processing within Node.js applications. It provides both a client for pushing jobs to the Faktory server and a worker for fetching and executing those jobs. The current stable version is 4.7.1, with an active development cadence indicated by recent minor releases introducing features like weighted-random queue fetching (v4.4.0) and CLI improvements (v4.5.0). Its key differentiators include comprehensive handling of Faktory job payloads, support for bulk job pushing, flexible queue configuration (including strictly ordered and weighted random processing), and graceful shutdown mechanisms for workers. The library requires Node.js >=16 and is compatible with Faktory server versions greater than 1.6.1. It also ships with TypeScript types, facilitating its use in modern TypeScript projects.","status":"active","version":"4.7.1","language":"javascript","source_language":"en","source_url":"https://github.com/jbielick/faktory_worker_node","tags":["javascript","faktory","client","node","server","job","background","async","typescript"],"install":[{"cmd":"npm install faktory-worker","lang":"bash","label":"npm"},{"cmd":"yarn add faktory-worker","lang":"bash","label":"yarn"},{"cmd":"pnpm add faktory-worker","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"For ESM, prefer named imports for individual functions. `connect` returns a `Client` instance for pushing jobs. In CommonJS, `require('faktory-worker')` returns an object from which `connect` is accessed.","wrong":"const faktory = require('faktory-worker'); const client = await faktory.connect();","symbol":"connect","correct":"import { connect } from 'faktory-worker';"},{"note":"`register` is used to associate a job type string with a JavaScript function that processes the job payload. This is typically done in the worker process.","wrong":"const faktory = require('faktory-worker'); faktory.register('JobType', ...);","symbol":"register","correct":"import { register } from 'faktory-worker';"},{"note":"`work` starts the Faktory worker process, which fetches and executes registered jobs from the Faktory server. It handles graceful shutdown on `SIGINT`/`SIGTERM`.","wrong":"const faktory = require('faktory-worker'); faktory.work();","symbol":"work","correct":"import { work } from 'faktory-worker';"},{"note":"While `connect()` is the primary way to get a client instance, the `Client` class is exported for type-checking or advanced usage. A `Client` represents a connection handle and is safe for concurrent use.","symbol":"Client","correct":"import { Client } from 'faktory-worker';"}],"quickstart":{"code":"import { connect, register, work, ClientOptions } from 'faktory-worker';\n\n// 1. Define your job processing logic\ninterface ResizeImagePayload {\n  id: number;\n  size: string;\n}\n\nregister('ResizeImage', async (payload: ResizeImagePayload) => {\n  console.log(`[Worker] Processing job ResizeImage for image ${payload.id} with size ${payload.size}`);\n  // Simulate an asynchronous operation, e.g., image resizing or database update\n  await new Promise(resolve => setTimeout(resolve, Math.random() * 1000 + 500));\n  console.log(`[Worker] Finished ResizeImage for image ${payload.id}`);\n});\n\n// 2. Worker startup (typically run in a long-running background process)\nasync function startFaktoryWorker() {\n  console.log('Starting Faktory worker...');\n  try {\n    const workerOptions: ClientOptions = {\n      queues: ['default', 'images'], // Listen to specified queues\n      concurrency: 5,                  // Process up to 5 jobs concurrently\n      // Faktory server URL can be set via FAKTORY_URL environment variable\n      // or explicitly passed: { host: 'localhost', port: 7419 }\n      // factory: { host: 'localhost', port: 7419 },\n    };\n    await work(workerOptions);\n    console.log('Faktory worker started and waiting for jobs...');\n  } catch (error) {\n    console.error(`Faktory worker failed to start: ${error}`);\n    // In Node.js, ensure the process exits on critical errors\n    if (typeof process !== 'undefined' && process.exit) {\n      process.exit(1);\n    }\n  }\n}\n\n// 3. Client job pushing (typically run from an application server or another process)\nasync function pushFaktoryJob(payload: ResizeImagePayload) {\n  let client;\n  try {\n    console.log(`[Client] Connecting to Faktory server to push job for image ${payload.id}...`);\n    client = await connect(); // Connects to Faktory server\n\n    console.log(`[Client] Pushing job 'ResizeImage' with payload:`, payload);\n    await client.job('ResizeImage', payload).push();\n    console.log(`[Client] Job 'ResizeImage' for image ${payload.id} pushed successfully.`);\n\n    // Example of pushing a bulk of jobs\n    // const job1 = client.job('ResizeImage', { id: 102, size: 'medium' });\n    // const job2 = client.job('ResizeImage', { id: 103, size: 'small' });\n    // const rejected = await client.pushBulk([job1, job2]);\n    // if (Object.keys(rejected).length > 0) {\n    //   console.error('[Client] Some bulk jobs were rejected:', rejected);\n    // }\n\n  } catch (error) {\n    console.error(`[Client] Failed to push job: ${error}`);\n  } finally {\n    if (client) {\n      await client.close(); // Important: reuse client or close after use\n      console.log('[Client] Disconnected from Faktory server.');\n    }\n  }\n}\n\n// To run this quickstart:\n// 1. Ensure a Faktory server is running (e.g., docker run --rm -p 7419:7419 -p 7420:7420 contribsys/faktory)\n// 2. Save this file (e.g., `app.ts`).\n// 3. Run the worker in one terminal: `ts-node app.ts worker`\n// 4. Run the client to push a job in another terminal: `ts-node app.ts client`\n\nconst mode = process.argv[2];\n\nif (mode === 'worker') {\n  startFaktoryWorker();\n} else if (mode === 'client') {\n  pushFaktoryJob({ id: 101, size: 'large' });\n} else {\n  console.log('Usage: ts-node app.ts [worker|client]');\n  console.log('Example: ts-node app.ts worker (to start the job processor)');\n  console.log('Example: ts-node app.ts client (to push a job)');\n  // For a truly single-file runnable demo, you'd push then immediately start a worker\n  // but in practice, these are separate long-running processes.\n}","lang":"typescript","description":"This quickstart demonstrates both the worker setup, registering a job ('ResizeImage'), and how a client pushes that job. It includes a basic `process.argv` check to allow running either the worker or client part independently, mirroring real-world deployment where these are separate processes."},"warnings":[{"fix":"Initialize a single client instance for an application lifetime (e.g., on application startup) and reuse it. Always ensure `await client.close()` is called in `finally` blocks or during application shutdown for graceful disconnection.","message":"It is crucial to properly manage Faktory client connections. Clients should be reused across multiple job pushes rather than creating a new client for each job. Remember to call `client.close()` when the client is no longer needed to release resources, especially in short-lived scripts or server shutdowns.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Always `await` all asynchronous operations within your job functions. Ensure the function only returns once all work is truly complete or an error has been properly thrown.","message":"Job functions registered with `faktory.register()` must correctly handle asynchronous operations. If an `async` job function returns before all `await` calls are resolved, the job will be `ACK`ed prematurely by the Faktory server, potentially leading to incomplete work.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Upgrade your Node.js environment to version 16 or newer. Use a Node.js version manager like `nvm` to easily switch and manage Node.js versions.","message":"The library requires Node.js version 16 or higher. Older Node.js versions are not supported and will result in runtime errors or unexpected behavior.","severity":"breaking","affected_versions":"<16.0.0"},{"fix":"Ensure your Faktory server instance is updated to version 1.6.1 or newer. Refer to the Faktory server's official documentation for upgrade instructions.","message":"This `faktory-worker` library is compatible with Faktory server versions `>v1.6.1`. Using it with older Faktory server versions may lead to protocol incompatibilities, unexpected job processing failures, or connection issues.","severity":"breaking","affected_versions":"<1.6.1 (Faktory server)"},{"fix":"Design job functions to be idempotent where possible. Monitor worker health and Faktory busy queues. Configure appropriate job reservation timeouts on the Faktory server to balance responsiveness and retry safety. Implement robust error handling and logging within job functions to identify issues before a full worker crash.","message":"If a Faktory worker process crashes while processing a job, the job will sit in the 'busy' state until its reservation timeout expires on the Faktory server. Only after the timeout will Faktory consider it failed and potentially re-enqueue it for retry, depending on job configuration. This can cause delays.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure the Faktory server is running and accessible from the worker process. Verify `FAKTORY_URL` environment variable, or explicitly pass connection options to `faktory.connect()` or `faktory.work()`.","cause":"The Faktory job server is not running or is not accessible at the default host and port (localhost:7419), or `FAKTORY_URL` environment variable is misconfigured.","error":"faktory worker failed to start: Error: connect ECONNREFUSED 127.0.0.1:7419"},{"fix":"Review Faktory server logs for related errors. Ensure consistent deployment practices to avoid sudden worker restarts on jobs. Consider Faktory server configuration and network stability. This might be a server-side issue or transient network problem.","cause":"This error can occur intermittently, especially with large job backlogs or during worker deployments, indicating the Faktory server cannot locate a job that a worker attempted to process.","error":"Error: Job not found <jid>"},{"fix":"For CommonJS, use `const faktory = require('faktory-worker');` and then access `faktory.connect()`, `faktory.register()`, `faktory.work()`. For ES modules, use `import { connect, register, work } from 'faktory-worker';`.","cause":"Attempting to use ES module named imports syntax with CommonJS `require()` or `faktory-worker` is exporting functions directly and not as properties of a default object when using CommonJS.","error":"TypeError: faktory.connect is not a function (when using require)"}],"ecosystem":"npm"}