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.

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' });Most job queues need Redis or a managed service. You already have a database.
Mock servers, tunnels, Docker. Just use SQLite locally.
Choose the backend that fits your workload. Same code works with SQLite, Postgres, or Redis.
Just like Next.js routes. One file per job, automatic discovery.
Full TypeScript inference from job definition to enqueue.
SQLite, Postgres, or Redis. Same code, different backend.
Your infrastructure, your data. No vendor lock-in.
Exponential backoff, configurable attempts.
Failed jobs preserved for debugging.
Schedule jobs to run later with delay options.
Test handlers as plain functions, no mocking required.
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 persistenceSwitch backends without rewriting jobs. Pick the adapter that fits your workload — one config change.
File-based jobs that just work.
npm install @pidgeyjs/core @pidgeyjs/next @pidgeyjs/sqlite// pidgey.config.ts
import { defineConfig } from '@pidgeyjs/core';
export default defineConfig({
adapter: 'sqlite',
filename: './pidgey.db',
worker: {
jobsDir: 'jobs',
concurrency: 10,
},
});// 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,
},
});// 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;
}