Skip to content

Mail

Register MailServiceProvider and add config/mail.ts:

typescript
export default {
  default: 'log',
  from: { address: 'hello@example.com', name: 'Pondoknusa' },
  connections: {
    log: { driver: 'log', channel: 'stdout' },
    array: { driver: 'array' },
    smtp: {
      driver: 'smtp',
      host: env('SMTP_HOST', '127.0.0.1'),
      port: Number(env('SMTP_PORT', 1025)),
      username: env('SMTP_USERNAME', ''),
      password: env('SMTP_PASSWORD', ''),
      encryption: env('SMTP_ENCRYPTION', ''),
    },
  },
} as const;

Sending mail

Use the Mail facade:

typescript
import { Mail } from '@pondoknusa/core';

// Quick message
await Mail.to('ada@example.com').send({
  subject: 'Welcome!',
  html: '<h1>Welcome aboard</h1>',
  text: 'Welcome aboard',
});

Mailable classes

For reusable mail logic, extend Mailable:

typescript
import { Mailable, type MailMessage } from '@pondoknusa/mail';

export class WelcomeMail extends Mailable {
  constructor(private readonly name: string) {
    super();
  }

  override build(): MailMessage {
    return {
      subject: `Welcome, ${this.name}!`,
      htmlView: 'emails.welcome',
      textView: 'emails.welcome-text',
      viewData: { name: this.name },
    };
  }

  // Queue this mailable instead of sending immediately
  override shouldQueue(): boolean {
    return true;
  }

  connection = 'database';
  queue = 'emails';
}

Send it:

typescript
await Mail.to('ada@example.com').send(new WelcomeMail('Ada'));

Using TSX views

When you register a ViewServiceProvider and set the view engine, a mailable's htmlView and textView resolve from resources/views/:

resources/views/emails/welcome.tsx
resources/views/emails/welcome-text.tsx
tsx
interface Props {
  name?: string;
}

export default function render({ name }: Props) {
  return (
    <html>
      <body>
        <h1>Hello {name}</h1>
        <p>Welcome to the app!</p>
      </body>
    </html>
  );
}

The mail package also ships namespaced layouts (mail::html.message, mail::text.message). Pass subject and body as view data when you use those view names.

Transports

DriverDescription
smtpSend over SMTP (production)
logWrite to a log channel (development)
arrayCollect messages in memory (testing)

Queued mail

Mark a mailable as queued by overriding shouldQueue() to return true. The mail manager will dispatch it through the queue system instead of sending immediately. Configure which queue connection to use:

typescript
export class WelcomeMail extends Mailable {
  override shouldQueue(): boolean {
    return true;
  }
  connection = 'database'; // queue connection from config/queue.ts
  queue = 'emails';
  delaySeconds = 30; // send 30 seconds from now
}

Service provider registration

typescript
import { MailServiceProvider } from '@pondoknusa/core';

app.register(MailServiceProvider);
await app.boot();

Then wire the facade:

typescript
import { setMailApplication } from '@pondoknusa/core';

setMailApplication(app);

If you use mail views, also set the view engine:

typescript
const mail = app.make<MailManager>('mail');
mail.setViewEngine(app.make<ViewEngine>('view.engine'));

Released under the MIT License.