Open source MIT license

Background jobs without the infrastructure

A type-safe job queue for Next.js that runs on your database — and grows with your workload.

Start with SQLite locally, use Postgres in production, or choose Redis for high-throughput workloads.

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

Choose the backend that fits your workload. Same code works with SQLite, Postgres, or Redis.

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.

Flexible Backends

SQLite, Postgres, or 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.

Choose your backend

Same API, different backends. Pick the one that fits your workload.

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

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

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

Switch backends without rewriting jobs. Pick the adapter that fits your workload — one config change.

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;
}

Made with ❤️ in Chicago