src/server/services/email/README.md
A flexible email service implementation supporting multiple email providers.
Based on the search service pattern, this service provides a unified interface for sending emails across different providers.
EmailService
└── EmailServiceImpl (interface)
└── NodemailerImpl (SMTP provider)
import { EmailService } from '@/server/services/email';
const emailService = new EmailService();
// Send a simple text email
await emailService.sendMail({
from: '[email protected]',
to: '[email protected]',
subject: 'Welcome to LobeChat',
text: 'Thanks for signing up!',
html: '<p>Thanks for signing up!</p>',
});
await emailService.sendMail({
from: '[email protected]',
to: ['[email protected]', '[email protected]'],
subject: 'Team Update',
text: 'Check out our latest updates',
});
await emailService.sendMail({
from: '[email protected]',
to: '[email protected]',
subject: 'Your Invoice',
text: 'Please find your invoice attached.',
attachments: [
{
filename: 'invoice.pdf',
path: '/path/to/invoice.pdf',
},
],
});
await emailService.sendMail({
from: '[email protected]',
replyTo: '[email protected]',
to: '[email protected]',
subject: 'Contact Us',
text: 'Reply to this email for support.',
});
Configure SMTP settings using environment variables:
# SMTP Server Configuration
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false # true for port 465, false for other ports
SMTP_USER=your-username
SMTP_PASS=your-password
If you prefer Resend, configure the following and initialize the service with EmailImplType.Resend:
RESEND_API_KEY=your-resend-api-key
[email protected]
RESEND_FROM is used when from is not provided in the payload.
Set EMAIL_SERVICE_PROVIDER to nodemailer or resend to pick the default implementation without changing code:
EMAIL_SERVICE_PROVIDER=resend
You can also use well-known email services (Gmail, SendGrid, etc.):
import { EmailImplType, EmailService } from '@/server/services/email';
import { NodemailerImpl } from '@/server/services/email/impls/nodemailer';
const emailService = new EmailService(EmailImplType.Nodemailer);
// Configure in constructor with service name
For development and testing, use Ethereal Email:
// The preview URL will be logged automatically in development
const result = await emailService.sendMail({...});
console.log('Preview URL:', result.previewUrl);
Before sending emails, verify your SMTP configuration:
import { EmailService } from '@/server/services/email';
const emailService = new EmailService();
try {
await emailService.verify();
console.log('SMTP connection verified ✓');
} catch (error) {
console.error('SMTP verification failed:', error);
}
Example integration for email verification:
import { betterAuth } from 'better-auth';
import { EmailService } from '@/server/services/email';
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
sendResetPasswordEmail: async ({ user, url }) => {
const emailService = new EmailService();
await emailService.sendMail({
from: '[email protected]',
to: user.email,
subject: 'Reset Your Password',
text: `Click here to reset your password: ${url}`,
html: `
<h1>Reset Your Password</h1>
<p>Click the link below to reset your password:</p>
<a href="${url}">Reset Password</a>
`,
});
},
},
emailVerification: {
enabled: true,
sendVerificationEmail: async ({ user, url }) => {
const emailService = new EmailService();
await emailService.sendMail({
from: '[email protected]',
to: user.email,
subject: 'Verify Your Email',
text: `Click here to verify your email: ${url}`,
html: `
<h1>Verify Your Email</h1>
<p>Click the link below to verify your email address:</p>
<a href="${url}">Verify Email</a>
`,
});
},
},
});
To add a new email provider (e.g., Resend, SendGrid):
impls/[provider-name]/index.ts:import { EmailPayload, EmailResponse, EmailServiceImpl } from '../type';
export class ResendImpl implements EmailServiceImpl {
async sendMail(payload: EmailPayload): Promise<EmailResponse> {
// Implement using Resend API
}
}
impls/index.ts:export enum EmailImplType {
Nodemailer = 'nodemailer',
Resend = 'resend', // Add new provider
}
impls/index.ts:export const createEmailServiceImpl = (type: EmailImplType) => {
switch (type) {
case EmailImplType.Nodemailer:
return new NodemailerImpl();
case EmailImplType.Resend:
return new ResendImpl();
default:
return new NodemailerImpl();
}
};
The service throws TRPCError for various failure scenarios:
try {
await emailService.sendMail({...});
} catch (error) {
if (error.code === 'SERVICE_UNAVAILABLE') {
// Handle SMTP connection issues
} else if (error.code === 'PRECONDITION_FAILED') {
// Handle configuration errors
}
}
Enable debug logging:
DEBUG=lobe-email:* node your-app.js
This will log detailed information about email sending operations.