The Email Notification Service provides a reliable, scalable system for dispatching transactional emails to users. It features provider abstraction (AWS SES/SendGrid), asynchronous processing with BullMQ, exponential backoff for rate limiting, and comprehensive template variable mapping for personalized communications.
-
BaseEmailProvider (
services/emailProviders/BaseEmailProvider.js)- Abstract interface for email providers
- Standardized error handling and response formatting
- Email validation and normalization
- Rate limit detection and retry logic
-
SESProvider (
services/emailProviders/SESProvider.js)- AWS Simple Email Service integration
- Template and simple email support
- Comprehensive SES API features
- Rate limit handling
-
SendGridProvider (
services/emailProviders/SendGridProvider.js)- SendGrid API integration
- Dynamic template support
- Advanced features (validation, suppression)
- Rate limit handling
-
EmailQueueService (
services/emailQueue.js)- BullMQ-based asynchronous processing
- Exponential backoff retry logic
- Rate limiting and concurrency control
- Job status tracking
-
NotificationService (
services/notificationService.js)- Unified service with provider abstraction
- Template variable mapping system
- Provider switching and management
- Predefined templates
-
API Routes (
routes/notifications.js)- RESTful endpoints for email management
- Queue monitoring and control
- Provider management
- Template management
# Email Provider Configuration
DEFAULT_EMAIL_PROVIDER=ses # or 'sendgrid'
# AWS SES Configuration
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_SESSION_TOKEN=your-session-token # Optional
# SendGrid Configuration
SENDGRID_API_KEY=your-sendgrid-api-key
# Redis Configuration (for BullMQ)
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your-redis-password
REDIS_DB=0
# Queue Configuration
NOTIFICATION_QUEUE_NAME=notification-queue
NOTIFICATION_CONCURRENCY=5
NOTIFICATION_RATE_LIMIT_MAX=100
NOTIFICATION_RATE_LIMIT_DURATION=60000
# Default Email Configuration
DEFAULT_FROM_EMAIL=noreply@substream-protocol.comconst notificationService = new NotificationService({
defaultProvider: 'ses',
ses: {
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
},
sendgrid: {
apiKey: process.env.SENDGRID_API_KEY
},
redis: {
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
password: process.env.REDIS_PASSWORD
},
queueName: 'notification-queue',
concurrency: 5,
rateLimitMax: 100,
rateLimitDuration: 60000,
globalTemplateMappings: {
companyName: 'SubStream Protocol',
website: 'https://substream-protocol.com'
}
});POST /api/v1/notifications/send
Authorization: Bearer <JWT_TOKEN>
{
"to": "user@example.com",
"from": "noreply@example.com",
"subject": "Welcome to SubStream",
"templateId": "welcome-template",
"templateData": {
"name": "John Doe",
"plan": "premium"
},
"provider": "ses",
"options": {
"attempts": 3,
"delay": 0,
"priority": 0
}
}
Response:
{
"success": true,
"data": {
"jobId": "uuid-1234",
"queue": "notification-queue",
"provider": "ses",
"addedAt": "2024-01-15T10:00:00.000Z",
"message": "Email queued for processing"
}
}POST /api/v1/notifications/send-simple
Authorization: Bearer <JWT_TOKEN>
{
"to": "user@example.com",
"from": "noreply@example.com",
"subject": "Simple Email",
"text": "This is a plain text email",
"html": "<p>This is an HTML email</p>"
}
POST /api/v1/notifications/send-bulk
Authorization: Bearer <JWT_TOKEN>
{
"recipients": [
{
"email": "user1@example.com",
"templateData": { "name": "User 1" }
},
{
"email": "user2@example.com",
"templateData": { "name": "User 2" }
}
],
"from": "noreply@example.com",
"subject": "Bulk Announcement",
"templateId": "announcement-template",
"templateData": {
"company": "SubStream Protocol"
}
}
POST /api/v1/notifications/send-template
Authorization: Bearer <JWT_TOKEN>
{
"templateType": "welcome",
"to": "user@example.com",
"templateData": {
"name": "John Doe",
"plan": "premium"
}
}
GET /api/v1/notifications/job/:jobId
Authorization: Bearer <JWT_TOKEN>
Response:
{
"success": true,
"data": {
"jobId": "uuid-1234",
"state": "completed",
"progress": 100,
"createdAt": "2024-01-15T10:00:00.000Z",
"processedOn": "2024-01-15T10:00:05.000Z",
"finishedOn": "2024-01-15T10:00:05.000Z",
"returnvalue": {
"success": true,
"messageId": "aws-ses-message-id",
"provider": "SESProvider"
}
}
}GET /api/v1/notifications/queue/stats
Authorization: Bearer <JWT_TOKEN>
Response:
{
"success": true,
"data": {
"queueName": "notification-queue",
"stats": {
"waiting": 5,
"active": 2,
"completed": 100,
"failed": 3,
"delayed": 1,
"total": 111
}
}
}GET /api/v1/notifications/queue/jobs?state=completed&start=0&end=50
Authorization: Bearer <JWT_TOKEN>
POST /api/v1/notifications/queue/pause
POST /api/v1/notifications/queue/resume
Authorization: Bearer <JWT_TOKEN>
POST /api/v1/notifications/queue/clear
Authorization: Bearer <JWT_TOKEN>
{
"state": "waiting"
}
GET /api/v1/notifications/providers
Authorization: Bearer <JWT_TOKEN>
Response:
{
"success": true,
"data": {
"availableProviders": ["ses", "sendgrid"],
"defaultProvider": "ses",
"providerStats": {
"ses": {
"name": "SESProvider",
"service": "AWS SES",
"region": "us-east-1"
},
"sendgrid": {
"name": "SendGridProvider",
"service": "SendGrid",
"baseUrl": "https://api.sendgrid.com/v3"
}
}
}
}POST /api/v1/notifications/providers/switch
Authorization: Bearer <JWT_TOKEN>
{
"provider": "sendgrid"
}
POST /api/v1/notifications/providers/test
Authorization: Bearer <JWT_TOKEN>
{
"provider": "ses"
}
GET /api/v1/notifications/templates
Authorization: Bearer <JWT_TOKEN>
POST /api/v1/notifications/templates
Authorization: Bearer <JWT_TOKEN>
{
"templateId": "custom-welcome",
"mapping": {
"defaultVariables": {
"appName": "My App",
"supportEmail": "support@example.com"
}
}
}
DELETE /api/v1/notifications/templates/:templateId
Authorization: Bearer <JWT_TOKEN>
GET /api/v1/notifications/health
Response:
{
"success": true,
"data": {
"status": "healthy",
"defaultProvider": "ses",
"availableProviders": ["ses", "sendgrid"],
"queue": {
"queueName": "notification-queue",
"provider": "ses",
"redisConnected": true,
"workerActive": true
},
"providers": { ... },
"templateMappings": 5
}
}GET /api/v1/notifications/stats
Authorization: Bearer <JWT_TOKEN>
The service includes predefined templates for common use cases:
{
"templateId": "welcome",
"defaultVariables": {
"appName": "SubStream Protocol",
"supportEmail": "support@substream-protocol.com",
"currentYear": 2024
}
}{
"templateId": "payment_failure",
"defaultVariables": {
"appName": "SubStream Protocol",
"supportEmail": "support@substream-protocol.com",
"billingUrl": "https://app.substream-protocol.com/billing"
}
}{
"templateId": "low_balance_warning",
"defaultVariables": {
"appName": "SubStream Protocol",
"supportEmail": "support@substream-protocol.com",
"addFundsUrl": "https://app.substream-protocol.com/wallet/add-funds"
}
}{
"templateId": "pre_billing_warning",
"defaultVariables": {
"appName": "SubStream Protocol",
"supportEmail": "support@substream-protocol.com",
"warningDays": 3
}
}You can create custom templates with dynamic variables:
notificationService.addTemplateMapping('custom_template', {
defaultVariables: {
companyName: 'Your Company',
website: 'https://yourcompany.com',
logoUrl: 'https://yourcompany.com/logo.png',
currentYear: () => new Date().getFullYear(),
timestamp: () => new Date().toISOString()
}
});The system supports various variable types:
- Static Variables: Simple key-value pairs
- Function Variables: Dynamic values generated at send time
- Nested Objects: Complex data structures
- Global Mappings: Variables applied to all templates
- Template-Specific Mappings: Variables for specific templates
The system implements intelligent retry logic with exponential backoff:
{
"attempts": 3,
"backoff": {
"type": "exponential",
"delay": 2000 // Start with 2 seconds
}
}Each provider detects rate limit errors:
ThrottlingException: 30 seconds retryTooManyRequestsException: 60 seconds retrySendingPausedException: 60 seconds retry
- HTTP 429: Uses
Retry-Afterheader or defaults to 60 seconds
The queue itself implements rate limiting:
{
"limiter": {
"max": 100, // Max jobs per duration
"duration": 60000 // 1 minute
}
}All errors follow a consistent format:
{
"success": false,
"error": "Error message",
"code": "ERROR_CODE",
"isRateLimit": false,
"retryAfter": null,
"timestamp": "2024-01-15T10:00:00.000Z",
"context": { ... }
}- Validation Errors: Missing required fields, invalid data
- Provider Errors: AWS SES/SendGrid API errors
- Queue Errors: BullMQ processing errors
- Rate Limit Errors: Provider rate limits
- Configuration Errors: Missing configuration
The system provides comprehensive metrics:
- Queue Metrics: Waiting, active, completed, failed jobs
- Provider Metrics: Success rates, error rates, response times
- Template Metrics: Usage statistics, variable mapping
- Health Metrics: Service status, connection status
All operations are logged with appropriate levels:
- INFO: Successful operations, queue status
- WARN: Rate limits, retries, degraded performance
- ERROR: Failed operations, provider errors
- DEBUG: Detailed processing information
Multiple health check endpoints:
/api/v1/notifications/health: Service health/api/v1/notifications/providers/test: Provider connections/api/v1/notifications/queue/stats: Queue status
- Authentication: JWT token required for all endpoints
- Authorization: Role-based access control
- Input Validation: All inputs validated and sanitized
- Rate Limiting: API-level rate limiting
- Sensitive Data: API keys and secrets stored in environment variables
- Template Data: User data processed securely
- Audit Trail: All email operations logged
- Data Retention: Configurable job retention policies
- AWS SES: IAM roles with least privilege
- SendGrid: API key restrictions and IP allowlists
- Redis: Authentication and TLS encryption
- HTTPS: All external communications encrypted
- Concurrency: Configurable worker concurrency
- Batching: Bulk email processing
- Prioritization: Job priority levels
- Memory Management: Efficient job processing
- Connection Pooling: Reuse provider connections
- Caching: Template and configuration caching
- Timeouts: Configurable request timeouts
- Retry Logic: Intelligent retry strategies
- Indexes: Optimized database queries
- Connection Pooling: Database connection reuse
- Query Optimization: Efficient data retrieval
- Cleanup: Automatic cleanup of old data
# Run all notification tests
npm test notificationService.test.js
# Run provider-specific tests
npm test -- --testNamePattern="SES Provider"
npm test -- --testNamePattern="SendGrid Provider"# Run queue integration tests
npm test -- --testNamePattern="Email Queue"
# Run API endpoint tests
npm test -- --testNamePattern="API Endpoints"The acceptance criteria are tested with:
npm test -- --testNamePattern="Acceptance Criteria"All providers are mocked for testing:
// Mock AWS SES
jest.mock('aws-sdk');
// Mock SendGrid
jest.mock('axios');
// Mock BullMQ
jest.mock('bullmq');-
Environment Setup
export NODE_ENV=production export DEFAULT_EMAIL_PROVIDER=ses export AWS_REGION=us-east-1 export REDIS_HOST=your-redis-host
-
Service Initialization
const notificationService = new NotificationService({ defaultProvider: process.env.DEFAULT_EMAIL_PROVIDER, concurrency: parseInt(process.env.NOTIFICATION_CONCURRENCY) || 10 });
-
Queue Worker
# Start queue worker node workers/notificationWorker.js
FROM node:18-alpine
# Install dependencies
COPY package*.json ./
RUN npm ci --only=production
# Copy application code
COPY . .
# Set environment variables
ENV NODE_ENV=production
ENV DEFAULT_EMAIL_PROVIDER=ses
# Start application
CMD ["node", "index.js"]apiVersion: apps/v1
kind: Deployment
metadata:
name: notification-service
spec:
replicas: 3
selector:
matchLabels:
app: notification-service
template:
metadata:
labels:
app: notification-service
spec:
containers:
- name: notification-service
image: substream/notification-service:latest
env:
- name: DEFAULT_EMAIL_PROVIDER
value: "ses"
- name: REDIS_HOST
value: "redis-service"-
Email Not Sending
- Check provider configuration
- Verify API keys and credentials
- Check queue status and job details
-
Rate Limit Errors
- Monitor rate limit headers
- Adjust queue concurrency
- Implement provider switching
-
Queue Processing Issues
- Check Redis connection
- Verify worker status
- Monitor queue statistics
-
Template Issues
- Validate template variables
- Check template mappings
- Verify provider template existence
Enable debug logging:
DEBUG=notification:* node index.js# View notification logs
tail -f logs/notification.log
# Filter errors
grep "ERROR" logs/notification.log
# Monitor queue status
grep "queue" logs/notification.log-
Advanced Templates
- Visual template editor
- A/B testing support
- Template versioning
-
Multi-Channel Support
- SMS notifications
- Push notifications
- In-app notifications
-
Advanced Analytics
- Open tracking
- Click tracking
- Engagement analytics
-
Enhanced Security
- Email encryption
- Advanced authentication
- Compliance features
-
Performance Features
- Distributed processing
- Load balancing
- Auto-scaling
This comprehensive email notification service provides enterprise-grade functionality with provider abstraction, reliable queue processing, and extensive customization options for all transactional email needs.