Skip to content

Mail

SemitraMailer and SemitraMailbox are the mail subsystem in Semitra.

Use them for outbound and inbound email workflows.

SemitraMailer turns validated params into a message object.

Core capabilities:

  • payload validation through a schema
  • direct delivery through deliver()
  • queued delivery through enqueue() and deliverLater()
  • tenant propagation
  • delivery observation hooks

Example mailer:

import { s } from "@semitra/cli";
import ApplicationMailer from "./application_mailer.ts";
export const WelcomeMailParams = s.object({
email: s.string(),
title: s.string().min(3)
});
export default class WelcomeMailer extends ApplicationMailer<{
email: string;
title: string;
}> {
static params = WelcomeMailParams;
mail(payload: { email: string; title: string }) {
return {
to: payload.email,
subject: `Welcome to ${payload.title}`,
text: `Hello from ${payload.title}`
};
}
}

SemitraMailbox handles inbound mail.

It is the inverse of a mailer:

  • parse and validate payloads
  • receive an inbound message
  • perform the application-specific side effect
  • optionally observe the receive flow

Example mailbox:

import { SemitraEvents, s } from "@semitra/cli";
import ApplicationMailbox from "./application_mailbox.ts";
const SupportInboundParams = s.object({
from: s.string().email(),
subject: s.string().min(3),
text: s.string().min(1)
});
export default class SupportMailbox extends ApplicationMailbox<{
from: string;
subject: string;
text: string;
}> {
static params = SupportInboundParams;
async receive(payload: {
from: string;
subject: string;
text: string;
}): Promise<void> {
await SemitraEvents.emitAsync("support:email_received", {
from: payload.from,
subject: payload.subject,
excerpt: payload.text.slice(0, 120)
});
}
}

That shape works well when inbound support email should be validated once and then handed off to the rest of the app as a normal domain event.

  • send a welcome email after user creation
  • notify a user when a background report is ready
  • process inbound support email into a ticket
  • react to inbound webhook-style email events through a mailbox
  • transactional email
  • operational email
  • inbound email handling
  • scheduled or queued mail delivery

Keep the mail subsystem focused on message composition and message handling. If the work is really domain logic, put the domain decision in the controller, model, or job that calls the mailer or mailbox.