npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

laramail

v1.4.3

Published

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.

Readme

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('[email protected]').send(new WelcomeEmail(user));
Mail.assertSent(WelcomeEmail, (mail) => mail.hasTo('[email protected]'));

How laramail Compares

| Feature | laramail | nodemailer | @sendgrid/mail | resend | |---------|:--------:|:----------:|:--------------:|:------:| | 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: '[email protected]', 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('[email protected]')
  .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('[email protected]').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('[email protected]').send(new WelcomeEmail('John'));

  Mail.assertSent(WelcomeEmail);
  Mail.assertSent(WelcomeEmail, (mail) =>
    mail.hasTo('[email protected]') && mail.subjectContains('Welcome')
  );
  Mail.assertSentCount(WelcomeEmail, 1);
  Mail.assertNotSent(PasswordResetEmail);
});

Assertions:

| Method | What 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):

| Method | What 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('[email protected]')
  .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 [email protected][/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('[email protected]').queue(new WelcomeEmail(user));          // Immediate
await Mail.to('[email protected]').later(60, new WelcomeEmail(user));      // 60s delay
await Mail.to('[email protected]').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: '[email protected]', name: 'Dev' },
  mailers: {
    log: { driver: 'log' },
  },
});

await Mail.to('[email protected]').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: '[email protected]', name: 'App' },
  mailers: { api: { driver: 'custom-api' } },
});

Staging Redirect (alwaysTo)

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

Mail.alwaysTo('[email protected]');
// All emails now go to [email protected], CC/BCC cleared
// Call Mail.alwaysTo(undefined) to disable

// Or via config:
Mail.configure({
  // ...
  alwaysTo: '[email protected]',
});

Email Preview

Preview rendered emails without sending:

const preview = await Mail.to('[email protected]')
  .subject('Hello')
  .html('<p>Hi</p>')
  .priority('high')
  .preview();

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

Complete Fluent API

await Mail.to('[email protected]')
  .subject('Complete Example')
  .html('<h1>Hello!</h1>')
  .text('Hello!')
  .from('[email protected]')
  .cc(['[email protected]'])
  .bcc('[email protected]')
  .replyTo('[email protected]')
  .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 [email protected]
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: '[email protected]', 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.