|
|
||
|---|---|---|
| .. | ||
| src | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| tsup.config.ts | ||
| vitest.config.ts | ||
@lilith/email-client
Shared NestJS client for sending emails through the centralized email service. Any backend feature that needs to send email should use this package rather than implementing direct SMTP/nodemailer.
Installation
Add to your feature's package.json:
{
"dependencies": {
"@lilith/email-client": "*"
}
}
Usage
1. Import the module
// app.module.ts
import { EmailClientModule } from '@lilith/email-client'
@Module({
imports: [
EmailClientModule.forRoot(),
// ...
],
})
export class AppModule {}
The module is @Global() — once imported in AppModule, EmailClientService is injectable everywhere.
2. Inject the service
import { EmailClientService } from '@lilith/email-client'
@Injectable()
export class MyService {
constructor(private readonly emailClient: EmailClientService) {}
async notifyUser() {
await this.emailClient.sendTemplate({
to: 'user@example.com',
templateName: 'my-feature/notification',
variables: { name: 'Alice', action: 'completed' },
category: 'my-feature',
priority: 'normal',
})
}
}
API
Typed methods (SSO/auth emails)
| Method | Purpose |
|---|---|
sendWelcome(data) |
Welcome email to new user |
sendVerification(data) |
Email verification link |
sendPasswordReset(data) |
Password reset (accepts resetToken, NOT resetUrl) |
sendPasswordChanged(data) |
Password changed confirmation |
sendAccountLocked(data) |
Account locked notification |
sendLoginAlert(data) |
New login alert |
sendOtp(data) |
MFA OTP code |
Generic methods (any feature)
| Method | Purpose |
|---|---|
sendTemplate(options) |
Send via named Handlebars template |
sendCustom(options) |
Send with inline HTML content |
SendTemplateEmailOptions
interface SendTemplateEmailOptions {
to: string | string[]
templateName: string // e.g. 'qa/report-alert', 'merch/approval'
variables: Record<string, unknown>
category?: string
userId?: string
priority?: 'high' | 'normal' | 'low'
}
SendCustomEmailOptions
interface SendCustomEmailOptions {
to: string | string[]
subject: string
html: string
text?: string
category?: string
userId?: string
priority?: 'high' | 'normal' | 'low'
}
Configuration
Defaults (zero-config)
With EmailClientModule.forRoot() and no arguments, the client:
- Resolves the email service URL from
@lilith/service-registry - Reads the API key from
EMAIL_INTERNAL_API_KEYenv var - If the API key is missing, email sending is disabled (logs a warning, never throws)
Custom options
EmailClientModule.forRoot({
serviceUrl: 'http://custom-email:3011', // Override URL
apiKeyEnvVar: 'MY_API_KEY_VAR', // Override env var name
})
Async options
EmailClientModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
serviceUrl: config.get('EMAIL_URL'),
}),
})
Environment Variables
| Variable | Required | Default | Purpose |
|---|---|---|---|
EMAIL_INTERNAL_API_KEY |
Yes | — | API key for email service internal endpoints |
EMAIL_SERVICE_URL |
No | service-registry | Override email service URL |
Error Handling
All methods are graceful — email failures log errors but never throw. This ensures email delivery issues never break calling service flows. Methods return string | null (the job ID, or null on failure).
Current Consumers
| Feature | Methods Used |
|---|---|
| SSO | All 7 typed methods (welcome, verification, reset, etc.) |
| Platform Admin | sendTemplate() for QA report alerts |
| Landing | sendTemplate() for merch approval/rejection |
| QA Backend | Domain events (indirect via email service QA processor) |
Internal Architecture
Your Service
│
│ emailClient.sendTemplate({...})
▼
EmailClientService (this package)
│
│ POST /internal/send/template
│ Header: X-Internal-Api-Key
▼
Email Service (codebase/features/email/backend-api)
│
│ BullMQ queue → Handlebars render → Nodemailer
▼
SMTP → Recipient