Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Sending email

The blog needs emails: a welcome after sign-up, a notice to the author about a new comment. In AdonisJS the @adonisjs/mail package sends them. The email body is an ordinary Edge template, and the delivery method (SMTP, Mailgun, Resend and others) is chosen in the settings.

Installing

node ace add @adonisjs/mail

The command asks which delivery services to set up — choose SMTP to start. config/mail.ts appears, and .env gets the variables:

SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=blog@example.com
SMTP_PASSWORD=secret

For development a trap service such as Mailpit or Mailtrap is handy: it accepts emails over SMTP and shows them in the browser without sending them to anyone.

The email template

{{-- resources/views/emails/welcome.edge --}}
<h1>Hello, {{ user.fullName }}!</h1>
<p>Thank you for signing up for our blog.</p>
<p><a href="https://example.com/posts">Read the posts</a></p>

Links in an email are written as full addresses with the domain: the mail program does not know which site the email came from.

Sending

// app/controllers/register_controller.ts
import mail from '@adonisjs/mail/services/main'
 
await mail.send((message) => {
  message
    .to(user.email)
    .from('blog@example.com')
    .subject('Welcome to the blog')
    .htmlView('emails/welcome', { user })
})

The function receives a message object and configures it with a chain: recipient, sender, subject and HTML from the template with data. After sign-up the email arrives at the given address.

Sending in the background

mail.send waits until the SMTP server accepts the email, and the visitor watches the page load all that time. The sendLater method puts the email in a queue and returns right away:

// app/controllers/register_controller.ts
await mail.sendLater((message) => {
  message.to(user.email).subject('Welcome to the blog').htmlView('emails/welcome', { user })
})

By default the queue lives in process memory: emails go out right after the response, but are lost if the server restarts.

A mail class

When the same email is sent from different places, it is written as a class:

node ace make:mail welcome
// app/mails/welcome_notification.ts
import { BaseMail } from '@adonisjs/mail'
import User from '#models/user'
 
export default class WelcomeNotification extends BaseMail {
  from = 'blog@example.com'
  subject = 'Welcome to the blog'
 
  constructor(private user: User) {
    super()
  }
 
  prepare() {
    this.message.to(this.user.email).htmlView('emails/welcome', { user: this.user })
  }
}
// app/controllers/register_controller.ts
import WelcomeNotification from '#mails/welcome_notification'
 
await mail.sendLater(new WelcomeNotification(user))

Now everything about the email is in one file, and the controller keeps one line.

Common mistake

The sender address in from does not match the domain the SMTP account belongs to. Mail services treat such emails as forged, and they land in spam or are rejected. Send from an address on your own domain, such as blog@example.com, and set up SPF and DKIM records for the domain.

Thanks for reading!

Donate to the site — we work hard for you.