Skip to content

platform-mailing

Multi-strategy email delivery system with pluggable transports (SMTP, Postmark, Resend, SendGrid, Mailgun, Log), template engines (file-based, blob-based), and email verification support. Each external provider SDK is an optional peer dependency — install only the one you need.

Package: @breadstone/archipel-platform-mailing

npm install @breadstone/archipel-platform-mailing

Architecture

mermaid
graph TD
    Module["MailModule"]
    MailService["MailService<br/><i>send() / sendTemplate()</i>"]
    Base["DeliveryStrategyBase<br/><i>Abstract send() contract</i>"]

    SMTP["SmtpDeliveryStrategy<br/><i>Built-in (nodemailer)</i>"]
    Log["LogDeliveryStrategy<br/><i>Built-in (dev)</i>"]
    Postmark["PostmarkDeliveryStrategy<br/><i>postmark</i>"]
    Resend["ResendDeliveryStrategy<br/><i>resend</i>"]
    SendGrid["SendGridDeliveryStrategy<br/><i>@sendgrid/mail</i>"]
    Mailgun["MailgunDeliveryStrategy<br/><i>mailgun.js</i>"]

    Module --> MailService
    MailService --> Base
    SMTP -->|extends| Base
    Log -->|extends| Base
    Postmark -->|extends| Base
    Resend -->|extends| Base
    SendGrid -->|extends| Base
    Mailgun -->|extends| Base

Quick Start

typescript
import { Module } from '@nestjs/common';
import { MailModule } from '@breadstone/archipel-platform-mailing';

@Module({
  imports: [MailModule],
})
export class AppModule {}

The module auto-selects delivery and template strategies based on environment variables.


Environment Variables

Core

VariableDescriptionValuesRequired
MAIL_SENDER_EMAILDefault "from" addressnoreply@example.comYes
MAIL_DELIVERY_STRATEGYTransport strategy'smtp' / 'postmark' / 'resend' / 'sendgrid' / 'mailgun' / 'log'Yes
MAIL_TEMPLATE_STRATEGYTemplate source'file' / 'blob'Yes
MAIL_TEMPLATE_ENGINE_FORMATTemplate format'html' / 'txt'Yes

SMTP (built-in)

VariableDescriptionDefaultRequired
MAIL_SMTP_HOSTSMTP server hostlocalhostIf SMTP
MAIL_SMTP_PORTSMTP server port587If SMTP
MAIL_SMTP_SECUREUse TLSfalseNo
MAIL_SMTP_USERAuth usernameIf SMTP
MAIL_SMTP_PASSWORDAuth passwordIf SMTP

Delivery Providers

Postmark

Subpath: @breadstone/archipel-platform-mailing/postmarkSDK: postmark ≥ 4.0.0

VariableDescriptionRequired
MAIL_POSTMARK_API_KEYPostmark API keyYes
typescript
import { PostmarkDeliveryStrategy, POSTMARK_CONFIG_ENTRIES } from '@breadstone/archipel-platform-mailing/postmark';

Resend

Subpath: @breadstone/archipel-platform-mailing/resendSDK: resend ≥ 4.0.0

VariableDescriptionRequired
MAIL_RESEND_API_KEYResend API keyYes
typescript
import { ResendDeliveryStrategy, RESEND_CONFIG_ENTRIES } from '@breadstone/archipel-platform-mailing/resend';

SendGrid

Subpath: @breadstone/archipel-platform-mailing/sendgridSDK: @sendgrid/mail ≥ 8.0.0

VariableDescriptionRequired
MAIL_SENDGRID_API_KEYSendGrid API keyYes
typescript
import { SendGridDeliveryStrategy, SENDGRID_CONFIG_ENTRIES } from '@breadstone/archipel-platform-mailing/sendgrid';

Mailgun

Subpath: @breadstone/archipel-platform-mailing/mailgunSDK: mailgun.js ≥ 10.0.0

VariableDescriptionRequired
MAIL_MAILGUN_API_KEYMailgun API keyYes
MAIL_MAILGUN_DOMAINMailgun domain (e.g. mg.example.com)Yes
typescript
import { MailgunDeliveryStrategy, MAILGUN_CONFIG_ENTRIES } from '@breadstone/archipel-platform-mailing/mailgun';

Log (built-in)

Logs emails to the console. No SDK or env vars required — useful for development and testing.

Switching Providers

Change a single environment variable — no code change needed:

bash
# Switch from SMTP to Resend
MAIL_DELIVERY_STRATEGY=resend
MAIL_RESEND_API_KEY=re_xxxxxxxxxxxx

DeliveryStrategyBase

All providers extend this abstract contract:

typescript
abstract class DeliveryStrategyBase {
  abstract send(
    from: string,
    to: string,
    subject: string,
    content: string,
    isHtml: boolean,
    attachments?: Array<IMailAttachment>,
  ): Promise<void>;
}

Attachment Support

MailService.send() accepts an optional fourth parameter for file attachments:

typescript
import type { IMailAttachment } from '@breadstone/archipel-platform-mailing';

const attachment: IMailAttachment = {
  filename: 'invoice.pdf',
  content: pdfBuffer,       // Buffer or string
  contentType: 'application/pdf',
};

await this._mail.send(
  { to: 'user@example.com', subject: 'Your Invoice' },
  '<p>Please find your invoice attached.</p>',
  true,
  [attachment],
);

Validation

ConstraintDetail
MIME typesPDF, Word (.doc/.docx), Excel (.xls/.xlsx), ZIP, PNG, JPEG, GIF, WebP, plain text, CSV
Max size25 MB per attachment
BehaviorInvalid MIME type or oversized attachments throw immediately — the email is not sent

IMailAttachment Interface

typescript
interface IMailAttachment {
  readonly filename: string;
  readonly content: Buffer | string;
  readonly contentType: string;
}

MailService

Core service for sending emails.

Send Plain Email

typescript
import { MailService } from '@breadstone/archipel-platform-mailing';

@Injectable()
export class NotificationService {
  constructor(private readonly _mail: MailService) {}

  public async sendWelcome(userEmail: string, userName: string): Promise<void> {
    await this._mail.send(
      { to: userEmail, subject: `Welcome, ${userName}!` },
      `<h1>Welcome</h1><p>Hey ${userName}, we're glad you're here.</p>`,
      true, // isHtml
    );
  }
}

Send Templated Email

typescript
await this._mail.sendTemplate(
  { to: userEmail, subject: 'Your Order Confirmation' },
  {
    templateName: 'order-confirmation',
    templateParams: {
      orderNumber: 'ORD-12345',
      total: '€39.97',
    },
  },
);

Custom Sender

typescript
await this._mail.send(
  {
    from: 'support@example.com', // overrides MAIL_SENDER_EMAIL
    to: 'customer@example.com',
    subject: 'Support Ticket #4567',
  },
  'Your ticket has been received.',
  false, // plain text
);

Template Strategies

File-Based Templates

Load templates from the filesystem:

typescript
// Set MAIL_TEMPLATE_STRATEGY=file
// Templates stored as files: assets/templates/welcome.html

Blob-Based Templates

Load templates from blob storage (e.g., Vercel Blob):

typescript
// Set MAIL_TEMPLATE_STRATEGY=blob
// Templates stored in blob storage: templates/welcome.html

SmtpConnectionVerifier

Verifies SMTP server connectivity. Replaces the deprecated MailVerificationService.

typescript
import { SmtpConnectionVerifier } from '@breadstone/archipel-platform-mailing';

@Injectable()
export class HealthCheckService {
  constructor(private readonly _smtpVerifier: SmtpConnectionVerifier) {}

  public async checkSmtp(): Promise<boolean> {
    return this._smtpVerifier.verifyConnection();
  }

  public async sendTestEmail(to: string): Promise<boolean> {
    return this._smtpVerifier.sendTestEmail(to);
  }
}

MailVerificationService (Deprecated)

Deprecated

MailVerificationService is deprecated. Use SmtpConnectionVerifier instead — it provides verifyConnection() for SMTP connectivity checks and sendTestEmail() for sending test messages.

typescript
import { MailVerificationService } from '@breadstone/archipel-platform-mailing';

@Injectable()
export class RegistrationService {
  constructor(private readonly _mailVerification: MailVerificationService) {}

  public async sendVerification(email: string): Promise<void> {
    await this._mailVerification.sendVerificationEmail(email);
  }
}

MailModule.register()

The MailModule supports a register() static method for explicit module configuration:

typescript
import { MailModule } from '@breadstone/archipel-platform-mailing';

@Module({
  imports: [
    MailModule.register({
      configEntries: MY_CUSTOM_CONFIG_ENTRIES, // optional
      isGlobal: true,                          // optional, default: false
    }),
  ],
})
export class AppModule {}

Health Check

The MailHealthIndicator validates that the required mail configuration values (MAIL_HOST, MAIL_USER, MAIL_PORT) are present and non-empty. Import it from the /health subpath:

typescript
import { MailHealthIndicator } from '@breadstone/archipel-platform-mailing/health';
import { HealthModule } from '@breadstone/archipel-platform-health';

@Module({
  imports: [
    MailModule.register({ /* ... */ }),
    HealthModule.withIndicators([MailHealthIndicator]),
  ],
})
export class AppModule {}
KeyCheckDependencies
mailValidates mail host, user, and port configConfigService

Real-World Example: Transactional Email Pipeline

typescript
@Injectable()
export class OrderEmailService {
  constructor(
    private readonly _mail: MailService,
    private readonly _analytics: AnalyticsService,
  ) {}

  public async sendOrderConfirmation(order: IOrder): Promise<void> {
    try {
      await this._mail.sendTemplate(
        {
          to: order.customerEmail,
          subject: `Order ${order.number} Confirmed`,
        },
        {
          templateName: 'order-confirmation',
          templateParams: {
            customerName: order.customerName,
            orderNumber: order.number,
            total: `€${order.total.toFixed(2)}`,
            deliveryDate: order.estimatedDelivery.toLocaleDateString('de-DE'),
          },
        },
      );
    } catch (error) {
      this._analytics.captureException(error, {
        tags: { module: 'mailing', orderId: order.id },
      });
    }
  }
}

Error Handling

All delivery strategies throw a MailDeliveryError when email sending fails. This domain error class wraps the underlying provider SDK error and exposes structured metadata for error handling and observability.

typescript
import { MailDeliveryError } from '@breadstone/archipel-platform-mailing';

try {
  await this._mail.send(options, content, true);
} catch (error) {
  if (error instanceof MailDeliveryError) {
    logger.error('Mail delivery failed', {
      provider: error.provider,  // 'postmark', 'resend', 'sendgrid', etc.
      code: error.code,          // 'MAIL_DELIVERY'
      cause: error.cause,        // Original provider SDK error
    });
  }
}
PropertyTypeDescription
codestringAlways 'MAIL_DELIVERY'
providerstringProvider that failed (smtp, postmark, etc.)
causeunknownOriginal error from the provider SDK

Exports Summary

Core (@breadstone/archipel-platform-mailing)

ExportTypeDescription
MailModuleNestJS ModuleGlobal mail module
MailServiceServiceSend emails (plain + templated)
SmtpConnectionVerifierServiceSMTP connectivity verification
MailVerificationServiceServiceEmail verification (deprecated — use SmtpConnectionVerifier)
MailTemplateEngineServiceTemplate variable substitution
DeliveryStrategyBaseAbstract classBase for delivery strategies
SmtpDeliveryStrategyStrategySMTP transport (built-in)
LogDeliveryStrategyStrategyLog-only (built-in)
ResendDeliveryStrategyStrategyResend API
SendGridDeliveryStrategyStrategySendGrid API
MailgunDeliveryStrategyStrategyMailgun API
FileTemplateFetchStrategyStrategyFile-based templates
BlobTemplateFetchStrategyStrategyBlob-stored templates
MailHealthIndicatorHealthMail readiness check (/health subpath)
MailDeliveryErrorError classDomain error for delivery failures

Provider subpaths

SubpathExports
/postmarkPostmarkDeliveryStrategy, POSTMARK_API_KEY, POSTMARK_CONFIG_ENTRIES
/resendResendDeliveryStrategy, RESEND_API_KEY, RESEND_CONFIG_ENTRIES
/sendgridSendGridDeliveryStrategy, SENDGRID_API_KEY, SENDGRID_CONFIG_ENTRIES
/mailgunMailgunDeliveryStrategy, MAILGUN_API_KEY, MAILGUN_DOMAIN, MAILGUN_CONFIG_ENTRIES

Released under the MIT License.