Email Workflows
Common patterns for sending emails as background jobs with Pidgey.
1. Welcome Email
Send a welcome email when a user signs up.
jobs/send-welcome-email.ts
import { pidgey } from '@/lib/pidgey';
export const sendWelcomeEmail = pidgey.defineJob({
name: 'send-welcome-email',
handler: async (data: { userId: string; email: string }) => {
const user = await db.users.findById(data.userId);
await resend.emails.send({
to: data.email,
subject: `Welcome, ${user.name}!`,
template: 'welcome',
});
return { sent: true };
},
config: {
retries: 3,
timeout: 30000,
},
});Enqueue the job after user signup:
app/actions/signup.ts
'use server';
import { sendWelcomeEmail } from '@/jobs/send-welcome-email';
export async function signup(formData: FormData) {
const email = formData.get('email') as string;
const user = await db.users.create({ email });
await sendWelcomeEmail.enqueue({
userId: user.id,
email: user.email,
});
return { success: true };
}2. Delayed Reminder
Send a follow-up email after a delay (e.g., 24 hours):
await sendFollowUpEmail.enqueue(
{ userId: user.id },
{ delay: 24 * 60 * 60 * 1000 } // 24 hours
);3. Daily Digest
Scheduled jobs are ideal for recurring emails.
jobs/daily-digest.ts
export const dailyDigest = pidgey.defineScheduledJob({
name: 'daily-digest',
handler: async () => {
const users = await db.users.findMany({ digestEnabled: true });
for (const user of users) {
const activity = await getActivitySummary(user.id);
await sendDigestEmail(user.email, activity);
}
},
schedule: {
cron: '0 9 * * *', // Every day at 9am
timezone: 'America/New_York',
},
});Scheduled jobs are automatically registered on worker startup.
4. Bulk Email with Rate Limiting
Use a separate queue and throttling to avoid hitting provider limits.
jobs/send-bulk-email.ts
export const sendBulkEmail = pidgey.defineJob({
name: 'send-bulk-email',
handler: async (data: { emails: string[]; template: string }) => {
for (const email of data.emails) {
await resend.emails.send({
to: email,
template: data.template,
});
// Small delay between emails to prevent rate limiting
await new Promise((r) => setTimeout(r, 100));
}
},
config: {
queue: 'bulk', // Dedicated bulk queue
timeout: 600000, // 10-minute timeout
},
});Run the bulk queue with low concurrency:
npx pidgey worker start --queue bulk --concurrency 1Use dedicated queues for bulk operations to prevent slow jobs from blocking high-priority emails.
Best Practices
- Use separate queues for high-priority vs bulk emails.
- Throttle external API calls to prevent rate-limiting.
- Scheduled jobs are ideal for recurring or digest emails.
- Retries & timeouts should match the expected job complexity.