New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

laramail

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

laramail

Laravel-style mailer for Node.js — Mail.fake() + Mail.assertSent() for zero-setup email testing, provider switching via env var, works with Express/Fastify/any framework.

latest
Source
npmnpm
Version
1.4.3
Version published
Maintainers
1
Created
Source

laramail

npm version License: MIT TypeScript Tests Coverage Documentation

AdonisJS mailer, but framework-agnostic — works with Express, Fastify, or any Node.js app.

Test emails without Mailtrap or mocking setup. Switch providers with one env var. Send with Mail.to(user).send(new WelcomeEmail()).

// Zero-setup email testing — no SMTP server, no network, no mocks.
Mail.fake();
await Mail.to('user@example.com').send(new WelcomeEmail(user));
Mail.assertSent(WelcomeEmail, (mail) => mail.hasTo('user@example.com'));

How laramail Compares

Featurelaramailnodemailer@sendgrid/mailresend
Mail.fake() + Mail.assertSent()
Mailable classes (OOP email objects)
Switch provider via MAIL_DRIVER env
Provider failover (auto chain)
Queue support (Bull / BullMQ)
Rate limiting (sliding window)
Staging redirect (Mail.alwaysTo())
Works with Express, Fastify, any framework

Installation

npm install laramail

Add providers and engines as needed — only install what you use:

npm install @sendgrid/mail    # SendGrid
npm install @aws-sdk/client-ses  # AWS SES
npm install mailgun.js form-data # Mailgun
npm install resend               # Resend
npm install postmark             # Postmark
npm install handlebars           # Template engine
npm install ejs                  # Template engine
npm install pug                  # Template engine
npm install marked juice         # Markdown emails
npm install bullmq               # Queue support

Quick Start

import { Mail } from 'laramail';

// 1. Configure once
Mail.configure({
  default: 'smtp',
  from: { address: 'noreply@example.com', name: 'My App' },
  mailers: {
    smtp: {
      driver: 'smtp',
      host: process.env.SMTP_HOST,
      port: 587,
      auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
    },
  },
});

// 2. Send emails
await Mail.to('user@example.com')
  .subject('Welcome!')
  .html('<h1>Hello World!</h1>')
  .send();

Switch providers by changing the driver — no code changes:

mailers: {
  sendgrid: { driver: 'sendgrid', apiKey: process.env.SENDGRID_API_KEY },
  ses:      { driver: 'ses', region: 'us-east-1', accessKeyId: '...', secretAccessKey: '...' },
  mailgun:  { driver: 'mailgun', domain: '...', apiKey: '...' },
  resend:   { driver: 'resend', apiKey: '...' },
  postmark: { driver: 'postmark', serverToken: '...' },
}

Mailable Classes

Create reusable, testable email classes — just like Laravel:

import { Mailable } from 'laramail';

class WelcomeEmail extends Mailable {
  constructor(private user: { name: string }) {
    super();
  }

  build() {
    return this
      .subject(`Welcome, ${this.user.name}!`)
      .html(`<h1>Hello ${this.user.name}!</h1>`);
  }
}

await Mail.to('user@example.com').send(new WelcomeEmail(user));

Testing with Mail.fake()

Test emails without sending — Laravel-style assertions:

beforeEach(() => Mail.fake());
afterEach(() => Mail.restore());

it('sends welcome email', async () => {
  await Mail.to('user@example.com').send(new WelcomeEmail('John'));

  Mail.assertSent(WelcomeEmail);
  Mail.assertSent(WelcomeEmail, (mail) =>
    mail.hasTo('user@example.com') && mail.subjectContains('Welcome')
  );
  Mail.assertSentCount(WelcomeEmail, 1);
  Mail.assertNotSent(PasswordResetEmail);
});

Assertions:

MethodWhat it checks
Mail.assertSent(Klass)Class was sent at least once
Mail.assertSent(Klass, fn)Class was sent and fn returns true for at least one message
Mail.assertSentCount(Klass, n)Class was sent exactly N times
Mail.assertNotSent(Klass)Class was NOT sent
Mail.assertNothingSent()No emails sent at all

AssertableMessage helpers (use inside the fn callback):

MethodWhat it checks
hasTo(email)Recipient address
hasCc(email)CC recipient
hasBcc(email)BCC recipient
hasSubject(subject)Exact subject match
subjectContains(text)Subject partial match (case-insensitive)
htmlContains(text)HTML body content
textContains(text)Plain text content
hasAttachment(filename)Attachment by filename
hasPriority(level)'high', 'normal', or 'low'
hasHeader(name, value?)Email header

Full API including queue assertions → docs/testing.md

Template Engines

Use Handlebars, EJS, or Pug for email templates:

Mail.configure({
  // ...mailer config
  templates: {
    engine: 'handlebars', // or 'ejs' or 'pug'
    viewsPath: './views/emails',
    cache: true,
  },
});

await Mail.to('user@example.com')
  .subject('Welcome!')
  .template('welcome')
  .data({ name: 'John', appName: 'My App' })
  .send();

Markdown Emails

Write emails in Markdown with built-in components:

import { MarkdownMailable } from 'laramail';

class WelcomeEmail extends MarkdownMailable {
  build(): this {
    return this.subject('Welcome!').markdown(`
# Hello, {{name}}!

Thanks for joining.

[button url="https://example.com" color="primary"]Get Started[/button]

[panel]Need help? Contact support@example.com[/panel]
    `, { name: this.user.name });
  }
}

Components: [button url="..." color="primary|success|error"], [panel]...[/panel], [table]...[/table]

Provider Failover

Automatic failover to backup providers with retries and monitoring:

Mail.configure({
  default: 'smtp',
  mailers: { smtp: { ... }, sendgrid: { ... }, ses: { ... } },
  failover: {
    chain: ['sendgrid', 'ses'],
    maxRetriesPerProvider: 2,
    retryDelay: 1000,
    onFailover: (event) => console.log(`${event.failedMailer}${event.nextMailer}`),
  },
});

Queue Support

Background sending with Bull or BullMQ:

Mail.configure({
  // ...mailer config
  queue: {
    driver: 'bullmq',
    connection: { host: 'localhost', port: 6379 },
    retries: 3,
    backoff: { type: 'exponential', delay: 1000 },
  },
});

await Mail.to('user@example.com').queue(new WelcomeEmail(user));          // Immediate
await Mail.to('user@example.com').later(60, new WelcomeEmail(user));      // 60s delay
await Mail.to('user@example.com').at(scheduledDate, new WelcomeEmail(user)); // Scheduled
await Mail.processQueue();                                                 // Worker

Email Events

Hook into the email lifecycle:

Mail.onSending((event) => {
  console.log(`Sending to ${event.options.to}`);
  event.options.headers = { ...event.options.headers, 'X-Tracking': '123' };
  // return false to cancel
});

Mail.onSent((event) => console.log(`Sent! ID: ${event.response.messageId}`));
Mail.onFailed((event) => console.error(`Failed: ${event.error}`));

Rate Limiting

Per-provider sliding window rate limiting:

Mail.configure({
  // ...mailer config
  rateLimit: { maxPerWindow: 100, windowMs: 60000 },
});

// Per-mailer override
mailers: {
  smtp: {
    driver: 'smtp', host: '...',
    rateLimit: { maxPerWindow: 10, windowMs: 1000 },
  },
}

When exceeded, returns { success: false } — never throws.

Log Transport

Use the log driver during development — emails are printed to console instead of sent:

Mail.configure({
  default: 'log',
  from: { address: 'dev@example.com', name: 'Dev' },
  mailers: {
    log: { driver: 'log' },
  },
});

await Mail.to('user@example.com').subject('Test').html('<p>Hi</p>').send();
// Prints formatted email to console — no SMTP needed

Custom Providers

Register your own mail provider with Mail.extend():

import { Mail } from 'laramail';

Mail.extend('custom-api', (config) => ({
  async send(options) {
    const res = await fetch('https://api.example.com/send', {
      method: 'POST',
      body: JSON.stringify(options),
    });
    return { success: res.ok, messageId: (await res.json()).id };
  },
}));

Mail.configure({
  default: 'api',
  from: { address: 'noreply@example.com', name: 'App' },
  mailers: { api: { driver: 'custom-api' } },
});

Staging Redirect (alwaysTo)

Redirect all emails to a single address — perfect for staging environments:

Mail.alwaysTo('dev-team@example.com');
// All emails now go to dev-team@example.com, CC/BCC cleared
// Call Mail.alwaysTo(undefined) to disable

// Or via config:
Mail.configure({
  // ...
  alwaysTo: 'dev-team@example.com',
});

Email Preview

Preview rendered emails without sending:

const preview = await Mail.to('user@example.com')
  .subject('Hello')
  .html('<p>Hi</p>')
  .priority('high')
  .preview();

console.log(preview.html, preview.headers);

Complete Fluent API

await Mail.to('user@example.com')
  .subject('Complete Example')
  .html('<h1>Hello!</h1>')
  .text('Hello!')
  .from('custom@example.com')
  .cc(['manager@example.com'])
  .bcc('archive@example.com')
  .replyTo('support@example.com')
  .attachments([{ filename: 'report.pdf', path: './report.pdf' }])
  .priority('high')
  .headers({ 'X-Custom': 'value' })
  .send();

CLI Tools

npx laramail queue:work              # Process queued emails
npx laramail queue:status            # Show queue job counts
npx laramail queue:clear -s failed   # Clear failed jobs
npx laramail queue:retry             # Retry failed jobs
npx laramail preview --mailable ./src/mail/WelcomeEmail.ts
npx laramail send:test --to you@example.com
npx laramail make:mailable WelcomeEmail
npx laramail make:mailable NewsletterEmail --markdown
npx laramail config:check            # Validate configuration
npx laramail config:check --test     # Test provider connections

Configuration File

// laramail.config.ts
import { defineConfig } from 'laramail';

export default defineConfig({
  default: 'smtp',
  from: { address: 'noreply@example.com', name: 'My App' },
  mailers: {
    smtp: {
      driver: 'smtp',
      host: process.env.SMTP_HOST,
      port: 587,
      auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
    },
  },
});

Why laramail?

If you've used Laravel's Mail, you know how elegant it is:

// Laravel (PHP)
Mail::to($user->email)->send(new WelcomeEmail($user));

laramail brings this same elegance to Node.js:

// laramail (TypeScript)
await Mail.to(user.email).send(new WelcomeEmail(user));

Lightweight by design — base package is ~25MB (SMTP only). Add providers as needed.

Contributing

git clone https://github.com/impruthvi/laramail.git
cd laramail
npm install
npm run build
npm test

See CONTRIBUTING.md for guidelines.

License

MIT © Pruthvi

Support

If laramail helps you, give it a star! It helps others discover the project.

Keywords

email

FAQs

Package last updated on 30 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts