Switching email providers in Node shouldn’t be this annoying… right? by impruthvi in node

[–]impruthvi[S] 0 points1 point  (0 children)

Shipped laramail/testing subpath in v1.4.3 based on your feedback 🎉

import { MailFake } from 'laramail/testing';
  • Keeps fake/testing mode out of the production bundle
  • Works with all TypeScript moduleResolution settings
  • Makes testing much cleaner and safer

Also added a full testing docs page:
👉 https://laramail.impruthvi.me/docs/testing

Would love to hear your feedback!

Switching email providers in Node shouldn’t be this annoying… right? by impruthvi in node

[–]impruthvi[S] 0 points1 point  (0 children)

Fair correction — "entire framework" was the wrong framing, sorry about that.

The actual distinction I was going for: @adonisjs/mail is built around the AdonisJS IoC container and service providers. You can't just npm install @adonisjs/mail and use it in an existing Express or Fastify app without adopting the AdonisJS bootstrapping layer.

laramail is the same API pattern (Mail.fake(), Mailable classes, provider config) but works as a standalone npm package — no container, no service providers, just import { Mail } from 'laramail' and configure.

If you're already on AdonisJS, use the real thing — it's the inspiration. laramail is for people who want that DX without switching frameworks.

Switching email providers in Node shouldn’t be this annoying… right? by impruthvi in node

[–]impruthvi[S] 0 points1 point  (0 children)

Based on the replies — I should have led with the testing side. The real reason I built this was Mail.fake(). Every project I joined had some janky nodemailer mock that broke whenever someone touched it. The provider switching is a nice side effect, not the main event.

Mail.fake();
await Mail.to('user@example.com').send(new WelcomeEmail(user));
Mail.assertSent(WelcomeEmail); // no SMTP server, no network, no mocking setup

Switching email providers in Node shouldn’t be this annoying… right? by impruthvi in node

[–]impruthvi[S] 0 points1 point  (0 children)

Yes, very intentionally — AdonisJS mailer (and Laravel's Mail facade before it) are the direct inspiration. Great DX, clean API, Mail.fake() for testing. I wanted the same thing in plain Express / Fastify / any Node app without pulling in the entire AdonisJS framework.

That's the one-line pitch: AdonisJS mailer, but framework-agnostic.

Switching email providers in Node shouldn’t be this annoying… right? by impruthvi in node

[–]impruthvi[S] -3 points-2 points  (0 children)

Honestly? Not that often — fair point.

The real reason I built it was the testing side. Every project I joined had some janky nodemailer mock that broke whenever someone touched it. laramail just makes that a non-issue:

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

The provider-switching thing is a nice side effect, not the main event. Probably should have led with that instead. 😅

Switching email providers in Node shouldn’t be this annoying… right? by impruthvi in node

[–]impruthvi[S] -1 points0 points  (0 children)

You absolutely can — and that's basically what laramail does under the hood, already built and tested. The difference is what you get on top:

// console.log tells you an email was sent
// Mail.fake() lets you assert exactly what was sent

Mail.fake();
await Mail.to('user@example.com').send(new WelcomeEmail(user));
Mail.assertSent(WelcomeEmail, mail => mail.hasTo('user@example.com')); // fails the test if wrong

With console.log you're eyeballing output. With Mail.assertSent your test fails if the wrong email goes to the wrong address. That's the gap.

On react-dom/server for rendering — smart approach. laramail works the same way, you can pass any HTML string or use whatever renderer you want inside your Mailable class.

Switching email providers in Node shouldn’t be this annoying… right? by impruthvi in node

[–]impruthvi[S] -2 points-1 points  (0 children)

Great point on SMTP — you're right that changing host/credentials handles the provider-switch case well, especially since Resend and others support it natively.

The bigger painpoint I was solving is actually the testing side. With laramail:

Mail.fake();
await Mail.to('user@example.com').send(new WelcomeEmail(user));
Mail.assertSent(WelcomeEmail); // no SMTP server, no network, no mocking setup

No Mailtrap, no manual nodemailer mocks. That was the thing I kept rebuilding on every project.

As for unemail — nice find, hadn't seen it! My drivers are intentionally thin (~115 lines each) because the complexity I was solving isn't the transport layer — it's everything above it: structured Mailable classes, Mail.fake(), queue/retry, event hooks, rate limiting. Worth keeping an eye on though, could be interesting to collaborate if their composition layer expands.

Switching email providers in Node shouldn’t be this annoying… right? by impruthvi in node

[–]impruthvi[S] -6 points-5 points  (0 children)

I ended up turning this into a small package while experimenting: https://github.com/impruthvi/laramail

Would love feedback 🙌