Queuebase
← Back to home

Changelog

New updates and improvements to Queuebase.

feature

API hardening — SSRF prevention & payload limits

Security hardening

The production API now validates callback URLs and enforces request size limits to protect against server-side request forgery (SSRF) and resource exhaustion attacks.

What’s new

  • Callback URL validation — the API rejects callback URLs targeting private networks (127.x, 10.x, 172.16-31.x, 192.168.x), localhost, IPv6 loopback, and non-HTTP schemes (file://, ftp://, etc.). This prevents SSRF attacks where a malicious callback URL could probe internal services.
  • 10MB request body limit — all API endpoints now reject requests with a Content-Length exceeding 10MB, preventing memory exhaustion from oversized payloads.
  • 1MB job payload limit — the payload field on POST /v1/jobs/enqueue is capped at 1MB. If your job data exceeds this, consider storing the data externally and passing a reference (e.g., an S3 URL) as the payload instead.

What’s unchanged

  • The local dev server (queuebase dev) is unaffected — localhost callbacks work as expected in development.
  • Existing jobs and projects with valid public callback URLs are unaffected.
feature

Job execution hardening — timeout & concurrency

Timeout & concurrency enforcement

Jobs can now define execution constraints at the type level. Timeouts abort long-running callbacks, and concurrency limits control how many jobs of a type can run simultaneously.

const sendEmail = job({
  input: emailSchema,
  config: {
    timeout: '30s',
    concurrency: 2,
  },
  handler: async ({ input }) => {
    // aborted after 30s, max 2 running at once
  },
});

What’s new

  • config.timeout — set a maximum duration for job execution (e.g., '30s', '5m'). If the callback doesn’t respond in time, the job is aborted and retried according to your backoff settings.
  • config.concurrency — limit how many jobs of a type can run at the same time. Set to 0 to pause a job type entirely without removing it.
  • job_types table — execution constraints are synced from your manifest via queuebase sync, just like schedules.
  • New JobTypeConfig — separates type-level config (timeout, concurrency) from per-call options (delay, retries, backoff).

Breaking changes

  • ScheduleConfig.timeout has been removed. Set timeout on your job definition’s config instead.
  • EnqueueOptions.concurrency has been removed. Set concurrency on your job definition’s config instead.

Bug fixes

  • Fixed Node.js client missing /v1/ prefix on API URLs
  • Fixed Node.js client sending nested request body instead of flat fields
feature

Pages Router support

Pages Router support

Queuebase now works with Next.js Pages Router. Import createPagesHandler from @queuebase/nextjs/pages and you’re set.

// pages/api/queuebase.ts
import { createPagesHandler } from "@queuebase/nextjs/pages";
import { jobs } from "@/jobs";

export const config = { api: { bodyParser: false } };
export default createPagesHandler(jobs);

The bodyParser: false config is required for webhook signature verification — the same pattern used by Stripe, GitHub, and other webhook providers. If you forget it, the handler returns a clear error telling you exactly what to add.

The existing createClient works with both App Router and Pages Router — no changes needed on the client side.

feature

Dashboard schedules page

Schedules in the dashboard

Your cron schedules now have a dedicated page in the dashboard. See all your schedules at a glance with their cron expressions, next/last run times, and status.

What you can do

  • View all schedules across your projects with filtering by project and status
  • Toggle schedules on/off from the dashboard without redeploying. A clear banner explains when a schedule is paused from the dashboard vs. disabled in code
  • Trigger a schedule manually to run a job immediately, with a confirmation step
  • See recent jobs spawned by each schedule, with links to the full job detail

How it works

Schedules are still defined in your code and synced via queuebase sync. The dashboard provides operational control on top of that: pause a runaway cron without a deploy, or trigger a job manually to test it.

The dashboard toggle uses a separate dashboardEnabled flag, so queuebase sync won’t overwrite your dashboard changes.

Bug fixes

  • Fixed an issue where the CLI sync command wasn’t loading .env files, requiring QUEUEBASE_API_KEY to be set in the shell environment. Now works with .env out of the box.
  • Fixed a Tailwind CSS configuration issue that caused some UI components to render incorrectly.
feature

Production sync command and API URL fix

queuebase sync command

A new CLI command syncs your schedule definitions to the production API:

npx queuebase sync

The command reads your manifest (from queuebase generate), diffs it against the production database, and applies the changes — inserting new schedules, updating changed ones, and soft-deleting removed ones. It’s idempotent and safe to run in CI/CD pipelines.

Dry run mode

Preview what would change without applying:

npx queuebase sync --dry-run
Dry run — no changes applied

  + dailyReport    (0 9 * * *, UTC)
  ~ weeklyDigest   cronExpression: "0 9 * * 1" → "0 10 * * 1"
  - oldCleanup     (will be soft-deleted)

1 to insert, 1 to update, 1 to delete

Configuration

The sync command uses QUEUEBASE_API_KEY (required) and defaults to https://api.queuebase.com. Override the URL with --api-url or QUEUEBASE_API_URL.

API URL fix

The default production API URL was incorrectly set to api.queuebase.io in the Next.js and Node.js SDKs. This has been fixed to api.queuebase.com. If you were setting QUEUEBASE_API_URL explicitly, no action is needed.

Updated docs

  • Updated reference: CLI — now includes queuebase sync command
fix

Schedule ticker fix

Schedule polling loop

Fixed a bug where the dev server synced schedules into the database on startup but never polled them to enqueue jobs when they were due. The dev server now checks for due schedules every 5 seconds and enqueues them automatically.

Overlap policy enforcement

The schedule ticker respects the overlap policy on each schedule:

  • skip (default) — if the previous run is still active (running or pending), the tick is skipped
  • allow — a new job is enqueued regardless of whether the previous run has finished

Job tracking

Schedule-triggered jobs now include a schedule_id linking them back to the schedule that triggered them, and enqueue events are logged to the job logs table for audit consistency.

feature

Scheduled jobs and the generate command

Scheduled jobs

Jobs can now run on a cron schedule. Add a schedule property to any job with an empty input schema and it will run automatically:

const jobs = createJobRouter({
  dailyReport: job({
    input: z.object({}),
    handler: async () => {
      // runs every day at 9am UTC
      return { generated: true };
    },
    schedule: "every day at 9am",
  }),
});

Schedules accept plain English ("every 5 minutes", "every weekday at 2pm"), raw cron expressions ("0 9 * * *"), or a config object for full control over timezone, overlap policy, and timeouts.

queuebase generate command

A new CLI command discovers your job router, extracts schedule metadata, and writes a manifest file:

npx queuebase generate

The CLI looks for your router at convention paths (src/jobs/index.ts, jobs/index.ts) or falls back to a queuebase.config.ts file.

Automatic schedule sync on dev startup

When you run npx queuebase dev, the dev server reads the generated manifest and syncs all schedules into the local database. Your cron jobs start firing immediately — no manual setup needed.

Updated docs

fix

SDK production fixes

API versioning

The SDK now automatically includes the /v1 prefix on all API requests. You no longer need to manually append /v1 to your apiUrl configuration — just set the base URL (e.g. https://api.queuebase.com).

Explicit job failure with ctx.fail()

Job handlers can now call ctx.fail("reason") to explicitly mark a job as failed without needing to throw an unhandled error. This is useful when your handler has its own try/catch logic:

const myJob = job({
  input: z.object({ id: z.string() }),
  handler: async (ctx) => {
    try {
      const result = await someExternalApi(ctx.input.id);
      return result;
    } catch (error) {
      ctx.fail(`External API error: ${error.message}`);
    }
  },
});

Enqueue options fix

Fixed a bug where job options (maxAttempts, delay, backoffStrategy, backoffDelay) were being silently dropped when enqueuing jobs in production. Options are now correctly sent to the API.

Enqueue response consistency

The production API now returns jobId in the enqueue response, matching the format expected by the SDK client.