DataQueueDataQueue
Usage

Job Output

Store and retrieve results from job handlers

Jobs can store an output value when they complete. This is useful when you need to retrieve the result of a background task — for example, a generated report URL, a processed image path, or computation results.

Storing Output

There are two ways to store output from a handler:

1. Return a value from the handler

The simplest approach — return any JSON-serializable value from your handler function:

@lib/job-handlers.ts
import { JobHandlers } from '@nicnocquee/dataqueue';

export const jobHandlers: JobHandlers<JobPayloadMap> = {
  generate_report: async (payload, signal, ctx) => {
    const url = await generateReport(payload.reportId); 
    return { url, generatedAt: new Date().toISOString() }; 
  },
};

2. Use ctx.setOutput(data)

For more control, call ctx.setOutput() explicitly. This is useful when you want to store intermediate results during execution:

@lib/job-handlers.ts
export const jobHandlers: JobHandlers<JobPayloadMap> = {
  process_images: async (payload, signal, ctx) => {
    const results: string[] = [];

    for (const image of payload.images) {
      const url = await processImage(image);
      results.push(url);

      await ctx.setProgress(
        Math.round((results.length / payload.images.length) * 100),
      );
      await ctx.setOutput({ processedUrls: results }); 
    }
  },
};

Precedence

If both ctx.setOutput() is called and the handler returns a value, the ctx.setOutput() value takes precedence. The handler's return value is ignored in that case.

Rules

  • JSON-serializable: The output value must be JSON-serializable (objects, arrays, strings, numbers, booleans, null).
  • Last write wins: Calling ctx.setOutput() multiple times overwrites the previous value.
  • Best-effort persistence: Like setProgress, output writes to the database are best-effort — errors do not kill the handler.

Reading Output

Output is stored in the output field of the JobRecord:

const job = await jobQueue.getJob(jobId);
console.log(job?.output); // null | any JSON value
  • Before the handler stores output, the value is null.
  • After the job completes, the output is preserved and can be read at any time.
  • Handlers that return undefined (or void) do not store output — the field remains null.

Tracking Output in React

If you're using the React SDK, the useJob hook exposes output directly:

import { useJob } from '@nicnocquee/dataqueue-react';

function JobResult({ jobId }: { jobId: number }) {
  const { status, output, progress } = useJob(jobId, {
    fetcher: (id) =>
      fetch(`/api/jobs/${id}`)
        .then((r) => r.json())
        .then((d) => d.job),
  });

  if (status === 'completed' && output) {
    return <a href={(output as any).url}>Download Report</a>;
  }

  return (
    <div>
      <p>Status: {status}</p>
      <progress value={progress ?? 0} max={100} />
    </div>
  );
}

Listening for Output Events

You can subscribe to the job:output event to be notified whenever a handler calls ctx.setOutput():

jobQueue.on('job:output', ({ jobId, output }) => {
  console.log(`Job ${jobId} stored output:`, output);
});

Database Migration

If you're using the PostgreSQL backend, make sure to run the latest migrations to add the output column. See Database Migration.

The Redis backend requires no migration — the output field is stored automatically as part of the job hash.