QUESTPIE
Operate in Production

Queue

Background job processing with pg-boss.

QUESTPIE uses pg-boss for background job processing. Jobs are stored in PostgreSQL — no external queue service needed.

Configuration

questpie.config.ts
import { pgBossAdapter, runtimeConfig } from "questpie"

export default runtimeConfig({
  queue: {
    adapter: pgBossAdapter({
      connectionString: process.env.DATABASE_URL,
    }),
  },
})

pg-boss creates its own schema in your database (pgboss.* tables) for job storage, scheduling, and dead-letter queues.

Defining Jobs

Jobs are defined in jobs/ using the file convention:

src/questpie/server/jobs/send-email.ts
import { job } from "questpie"
import z from "zod"

export default job({
  name: "send-email",
  schema: z.object({
    to: z.string().email(),
    subject: z.string(),
    body: z.string(),
  }),
  handler: async ({ payload, email, logger }) => {
    await email.send({
      to: payload.to,
      subject: payload.subject,
      html: payload.body,
    })
    logger.info({ to: payload.to }, "Email sent")
  },
  options: {
    retryLimit: 3,
    retryDelay: 5,
    retryBackoff: true,
  },
})

See Jobs for the full job definition guide.

Publishing Jobs

From hooks, routes, or other jobs — use the typed queue context:

handler: async ({ queue }) => {
  await queue.sendEmail.publish({
    to: "user@example.com",
    subject: "Welcome",
    body: "<h1>Hello!</h1>",
  })
}

The queue object is fully typed — autocompletion shows all registered jobs and their payload schemas.

Job Options

OptionTypeDefaultDescription
retryLimitnumber0Max retry attempts on failure
retryDelaynumber0Seconds between retries
retryBackoffbooleanfalseExponential backoff on retries
expireInSecondsnumber900Job expires if not completed
startAfterDate | stringnowDelay job start
singletonKeystringPrevent duplicate jobs with same key
prioritynumber0Higher = processed first

Scheduled Jobs

Use startAfter to schedule future execution:

await queue.sendReminder.publish(
  { appointmentId: "abc" },
  { startAfter: new Date("2026-03-20T09:00:00Z") },
)

Singleton Jobs

Prevent duplicate jobs with singletonKey:

await queue.syncInventory.publish(
  { warehouseId: "wh-1" },
  { singletonKey: "sync-wh-1" },
)

If a job with the same singleton key is already queued/active, the publish is a no-op.

Monitoring

pg-boss stores job status in PostgreSQL. Query the pgboss.job table for monitoring:

SELECT state, COUNT(*) FROM pgboss.job GROUP BY state;

States: createdactivecompleted / failed / expired

  • Jobs — Defining job handlers
  • Email — Sending emails from jobs
  • Config API — Queue adapter configuration

On this page