pg-boss icon indicating copy to clipboard operation
pg-boss copied to clipboard

Get working with pglite (working example)

Open seveibar opened this issue 6 months ago • 1 comments

pglite is a fantastic version of postgres compiled to WASM. It is great for tests where you need to spin up databases. It took a bit of work to get pg-boss working with pglite, so I wanted to share what I came up with.

The main thing: I had to construct a sort of custom client. I'm not completely sure why pglite doesn't support some of these client features, not sure if pg-boss would want to add some compatibility or support.

Hopefully this helps people who might be searching for it!

import { PGlite } from "@electric-sql/pglite"
import PgBoss from "pg-boss"

// NOTE: PGlite runs in single-user mode
const db = new PGlite()

const createPlan = PgBoss.getConstructionPlans("infra_pg_boss")

await db.exec(createPlan)

function encodePgArray(arr: any[]): string {
  // Only works for string/number/null arrays (sufficient for PgBoss job ids)
  return `{${arr
    .map((v) => {
      if (v === null || v === undefined) return "NULL"
      // Escape double quotes and backslashes
      if (typeof v === "string")
        return `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`
      return String(v)
    })
    .join(",")}}`
}

const boss = new PgBoss({
  db: {
    async executeSql(text: string, values: any[] = []) {
      let sql = text
      if (values && values.length > 0) {
        sql = text.replace(/\$(\d+)/g, (_, n) => {
          const v = values[Number(n) - 1]
          if (v === null || v === undefined) return "NULL"
          if (Array.isArray(v)) {
            // Encode as Postgres array literal
            return `'${encodePgArray(v)}'`
          }
          if (typeof v === "number") return v.toString()
          if (typeof v === "object") {
            try {
              return `'${JSON.stringify(v).replace(/'/g, "''")}'`
            } catch (e) {
              return `'${String(v).replace(/'/g, "''")}'`
            }
          }
          return `'${String(v).replace(/'/g, "''")}'`
        })
      }
      const results = await db.exec(sql)
      if (
        Array.isArray(results) &&
        results.length > 0 &&
        results[0] !== undefined &&
        "rows" in results[0]
      ) {
        return { rows: results[0].rows }
      }
      return { rows: [] }
    },
  },
  schema: "infra_pg_boss",
})

boss.createQueue("do_the_thing")

boss.send(
  "do_the_thing",
  {
    doTheThing: true,
  },
  {
    singletonKey: "key1",
    singletonMinutes: 30,
  },
)
boss.send(
  "do_the_thing",
  {
    doTheThing: true,
    someOtherParam: 1,
  },
  {
    singletonKey: "key1",
    singletonMinutes: 30,
  },
)

const jobs1 = await boss.fetch("do_the_thing", {
  batchSize: 10,
})
console.log("jobs1", jobs1)

const jobs2 = await boss.fetch("do_the_thing", {
  batchSize: 10,
})
console.log("jobs2", jobs2)

console.log("jobs1[0]!.id", jobs1[0]!.id)

await boss.complete("do_the_thing", jobs1[0]!.id)

seveibar avatar Jun 12 '25 00:06 seveibar

Can you include some of the error messages you received? I've had issues in the past with other apps (not pg-boss) regarding json parsing which this reminds me of.

timgit avatar Jun 12 '25 21:06 timgit