Messaging

Email sending with pluggable backends, sent message storage, and dev-friendly overrides.

Sending email

val result = funktorMessaging.mailing.send(
    Email(
        source = "noreply@example.com",
        destination = EmailDestination.to("user@example.com"),
        subject = "Your order has shipped",
        body = EmailBody.Html {
            body { p { +"Your package is on the way." } }
        },
    )
)

if (result.success) {
    log.info("Sent email: ${result.messageId}")
}

Configuring a sender

Wire your email sender in the Kontainer blueprint. The sender is wrapped with hooks for storage and logging.

val blueprint = kontainer {
    funktor(
        config = config,
        messaging = { useKarango() },  // persist sent messages
        // ...
    )

    // AWS SES sender with hooks
    val mailSender: AwsSesSender by lazy {
        AwsSesSender.of(config = config.aws.ses)
    }

    singleton(EmailSender::class) { log: Log, storing: StoringEmailHook ->
        mailSender
            .applyDevConfig(config = config, devConfig = config.devOverrides?.mailing)
            .withHooks(log) {
                onAfterSend(storing)
                onAfterSend { email, result ->
                    log.debug("Email sent to ${email.destination}")
                }
            }
    }
}

Email senders

Sender Use case
AwsSesSender AWS Simple Email Service. Production-grade delivery.
SendgridSender SendGrid API. Alternative cloud provider.
NullEmailSender No-op. Discards all emails silently.
OverridingEmailSender Wraps another sender and redirects all emails to a dev inbox.

Dev overrides

In development, applyDevConfig() wraps your real sender with an OverridingEmailSender that redirects all emails to your dev inbox. No accidental emails to real users.

The override is applied based on your app config. In production, the real sender is used directly. In dev/staging, all emails go to the address you configure — with the original recipient noted in the subject line.

Email hooks

The withHooks() builder wraps any sender and calls your hooks after sending. The built-in StoringEmailHook persists every sent email to the database for auditing.

Sent message storage

Every sent email is persisted to a SentMessagesStorage via the storing hook. Query by reference (e.g. user ID) to see what was sent and when. Storage backends: useKarango() for ArangoDB, useMonko() for MongoDB.