Supastarter
This guide walks through integrating Queuebase into a Supastarter Next.js application. By the end, you’ll have background jobs, scheduled tasks, and queues running in your app with no additional infrastructure.
Before you start, make sure you have a Queuebase account. The free tier includes 10,000 job runs per month — no credit card required.
How Queuebase works
Section titled “How Queuebase works”Queuebase uses a callback model: your app defines job handlers at a webhook endpoint, and Queuebase calls back to your app to execute them. Your job code lives in your app. There is no separate worker process or infrastructure to manage. Queuebase handles storage, scheduling, and retries.
Install the SDK
Section titled “Install the SDK”pnpm add @queuebase/nextjs zodpnpm add -D @queuebase/cliThe SDK provides the job router, client, and webhook handler. The CLI runs the local dev server and handles syncing schedules to production.
Zod is used in this guide, but Queuebase supports any Standard Schema-compatible validation library (Valibot, ArkType, etc.).
Define your jobs
Section titled “Define your jobs”Create a job router with validated inputs. Each job gets a schema for type-safe input validation and a handler that runs when the job executes:
import { createJobRouter, job } from "@queuebase/nextjs";import { z } from "zod";
export const jobs = createJobRouter({ sendWelcomeEmail: job({ input: z.object({ to: z.string().email(), name: z.string(), }), handler: async ({ input, jobId, attempt }) => { // your email sending logic here console.info(`[job ${jobId}] Sending welcome email to ${input.to}`); return { sent: true }; }, defaults: { retries: 3, backoff: "exponential", }, }),
processSubscription: job({ input: z.object({ userId: z.string(), plan: z.enum(["free", "pro", "enterprise"]), }), handler: async ({ input, jobId }) => { // subscription processing logic console.info(`[job ${jobId}] Processing ${input.plan} subscription for ${input.userId}`); return { processed: true }; }, }),});
export type JobRouter = typeof jobs;The handler receives a context object with:
input- the validated payload (typed from your schema)jobId- unique identifier for this job executionattempt/maxAttempts- retry trackingfail(reason)- explicitly mark the job as failed
Create a client
Section titled “Create a client”The client gives you a type-safe way to enqueue jobs from anywhere in your app:
import { createClient } from "@queuebase/nextjs";import { jobs } from "./index";
export const jobClient = createClient(jobs, { apiUrl: process.env.QUEUEBASE_API_URL ?? "http://localhost:3847", apiKey: process.env.QUEUEBASE_API_KEY, callbackUrl: process.env.QUEUEBASE_CALLBACK_URL ?? "http://localhost:3000/api/queuebase",});apiUrl— the Queuebase API. The CLI dev server runs onhttp://localhost:3847. In production, this is your hosted Queuebase API URL.apiKey— authenticates with the production API. Not needed during local development.callbackUrl— the URL Queuebase POSTs to when it’s time to execute a job.
Create the webhook handler
Section titled “Create the webhook handler”This is the endpoint Queuebase calls to execute your jobs:
import { createHandler } from "@queuebase/nextjs/handler";import { jobs } from "@repo/jobs";
export const POST = createHandler(jobs);Enqueue jobs
Section titled “Enqueue jobs”Call .enqueue() from server actions, API routes, or anywhere server-side:
"use server";
import { jobClient } from "@repo/jobs/client";
export async function sendWelcomeEmail(to: string, name: string) { const { jobId } = await jobClient.sendWelcomeEmail.enqueue({ to, name, });
return jobId;}The SDK validates the input against your schema at compile time and runtime, sends the job to the Queuebase API, and returns a jobId you can use to track status.
Add scheduled jobs
Section titled “Add scheduled jobs”Scheduled jobs are defined inline using the schedule property. Scheduled jobs must have an empty input schema (z.object({})), since the scheduler has no payload to pass:
import { createJobRouter, job } from "@queuebase/nextjs";import { z } from "zod";
export const jobs = createJobRouter({ // ... your other jobs
dailyCleanup: job({ input: z.object({}), schedule: "every day at 2am", handler: async ({ jobId }) => { // cleanup expired sessions, old data, etc. console.info(`[job ${jobId}] Running daily cleanup`); return { cleaned: true }; }, }),});The schedule property accepts plain English ("every 5 minutes", "every weekday at 9am") or raw cron expressions ("0 2 * * *"). You can also pass a config object for more control:
schedule: { cron: "every day at 2am", timezone: "America/New_York", overlap: "skip",}Schedules sync automatically when you run npx queuebase dev locally or npx queuebase sync for production.
Environment variables
Section titled “Environment variables”Add these to your .env file:
# Development (defaults work out of the box)QUEUEBASE_API_URL=http://localhost:3847QUEUEBASE_CALLBACK_URL=http://localhost:3000/api/queuebase
# ProductionQUEUEBASE_API_URL=https://api.queuebase.comQUEUEBASE_API_KEY=your_api_keyQUEUEBASE_CALLBACK_URL=https://your-app.com/api/queuebaseRun locally
Section titled “Run locally”In one terminal, start the Queuebase dev server:
npx queuebase devIn another terminal, start your app:
pnpm devThe dev server stores jobs in a local SQLite database and polls for pending jobs to execute. Trigger an enqueue from your app and you should see the job being received and executed in the CLI output.
Deploy to production
Section titled “Deploy to production”- Create a project at queuebase.com
- Set the environment variables (
QUEUEBASE_API_URL,QUEUEBASE_API_KEY,QUEUEBASE_CALLBACK_URL) in your hosting provider - Run
npx queuebase syncto sync your job types and schedules to production - Deploy your app — Queuebase will start calling back to your webhook endpoint
Next steps
Section titled “Next steps”- Defining Jobs — defaults, config, and advanced handler patterns
- Enqueueing Jobs — options, delays, and checking job status
- Scheduled Jobs — cron schedules and automatic execution
- Configuration — environment variables and webhook security