Open source MIT license

Background jobs without the infrastructure

Job queue for Next.js that uses your existing database. SQLite for dev, Postgres for prod, Redis when you scale.

Pidgey - Job queue mascot
Quick Start
npm install @pidgeyjs/core @pidgeyjs/next @pidgeyjs/sqlite

// jobs/send-email.ts
import { pidgey } from '../lib/pidgey';

export const sendEmail = pidgey.defineJob({
  name: 'send-email',
  handler: async (data: { to: string }) => {
    await sendEmailService(data.to);
  },
});

// Enqueue from anywhere
import { sendEmail } from '@/jobs/send-email';
await sendEmail.enqueue({ to: 'user@example.com' });

Background jobs are harder than they should be

Extra infrastructure

Most job queues need Redis or a managed service. You already have a database.

Local dev is painful

Mock servers, tunnels, Docker. Just use SQLite locally.

Stuck with one solution

Start with Postgres, switch to Redis when you need scale. Same code, no refactoring.

Everything you need for background jobs

File-Based Jobs

Just like Next.js routes. One file per job, automatic discovery.

Type-Safe

Full TypeScript inference from job definition to enqueue.

Progressive Scaling

SQLite → Postgres → Redis. Same code, different backend.

Self-Hosted

Your infrastructure, your data. No vendor lock-in.

Automatic Retries

Exponential backoff, configurable attempts.

Dead Letter Queue

Failed jobs preserved for debugging.

Delayed Jobs

Schedule jobs to run later with delay options.

Easy Testing

Test handlers as plain functions, no mocking required.

Scale progressively

Same API, different backends. Start simple, scale when needed.

// pidgey.config.ts
import { defineConfig } from '@pidgeyjs/core';

export default defineConfig({
  adapter: 'sqlite',
  filename: './pidgey.db',
});

// Perfect for development
// Zero dependencies
// File-based persistence

Outgrow Postgres? Switch to Redis with one config change. Same jobs, no refactoring.

Get started in 4 steps

File-based jobs that just work.

1

Install Pidgey

npm install @pidgeyjs/core @pidgeyjs/next @pidgeyjs/sqlite
2

Create your config

// pidgey.config.ts
import { defineConfig } from '@pidgeyjs/core';

export default defineConfig({
  adapter: 'sqlite',
  filename: './pidgey.db',
  worker: {
    jobsDir: 'jobs',
    concurrency: 10,
  },
});
3

Define a job

// jobs/send-welcome-email.ts
import { pidgey } from '@/lib/pidgey';

export const sendWelcomeEmail = pidgey.defineJob({
  name: 'send-welcome-email',
  handler: async (data: { userId: string }) => {
    const user = await db.user.findUnique({ 
      where: { id: data.userId } 
    });
    await sendEmail({
      to: user.email,
      subject: 'Welcome!',
    });
  },
  config: {
    retries: 3,
    timeout: 30000,
  },
});
4

Enqueue jobs

// app/actions/signup.ts
'use server';

import { sendWelcomeEmail } from '@/jobs/send-welcome-email';

export async function signup(email: string) {
  const user = await db.user.create({ data: { email } });
  
  // Job runs in background
  await sendWelcomeEmail.enqueue({ userId: user.id });
  
  return user;
}