Email (Resend)
The email module sends transactional emails via Resend. Emails are dispatched through a BullMQ queue for reliable async delivery, with a synchronous fallback when Redis is unavailable.
Architecture
EmailService.sendWelcome(...)
|
v
dispatch()
|-- BullMQ available? --> Queue job --> EmailProcessor picks it up --> Resend API
|-- BullMQ unavailable? --> sendDirect() --> Resend API
|-- Resend not configured? --> Log to console
Email Templates
| Method | Template | Description |
|---|---|---|
sendWelcome(to, name) | Welcome | Sent after registration |
sendMagicLink(to, token) | Magic Link | Passwordless login link |
sendTeamInvite(to, ...) | Team Invite | Invitation to join a team |
sendPasswordReset(to, token) | Password Reset | Password reset link |
sendInvoicePaymentFailed(to, name) | Invoice Failed | Notification of failed payment |
All templates are rendered by the renderEmail function which returns a { subject, html } object.
Queue Integration
When BullMQ is available (Redis connected), emails are added to the email queue with these defaults:
- Retries: 3 attempts with exponential backoff (1s base delay)
- Cleanup: completed jobs are removed automatically; up to 100 failed jobs are retained for inspection
The EmailProcessor class extends WorkerHost and processes jobs from the email queue. It renders the template and sends via Resend.
Graceful Fallbacks
The module degrades gracefully in two scenarios:
- No Redis / BullMQ unavailable:
EmailServicedetects that the queue was not injected (using@Optional()) and sends the email directly in-process viasendDirect(). - No
RESEND_API_KEY: both the service and processor log the email subject and HTML to the console instead of calling the Resend API. This allows local development without external credentials.
Environment Variables
RESEND_API_KEY=re_... # Optional in development
EMAIL_FROM=hello@example.com
APP_URL=http://localhost:5173
REDIS_URL=redis://localhost:6379 # Required for queue-based delivery