Отправка писем
Блогу нужны письма: приветствие после регистрации, уведомление автору о новом комментарии. В AdonisJS их отправляет пакет @adonisjs/mail. Текст письма — обычный шаблон Edge, а сам способ доставки (SMTP, Mailgun, Resend и другие) выбирается в настройках.
Установка
node ace add @adonisjs/mailКоманда спросит, какие сервисы доставки подключить, — для начала выберите SMTP. Появится config/mail.ts, а в .env — переменные:
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=blog@example.com
SMTP_PASSWORD=secretДля разработки удобен сервис-ловушка вроде Mailpit или Mailtrap: он принимает письма по SMTP и показывает их в браузере, никому не отправляя.
Шаблон письма
{{-- resources/views/emails/welcome.edge --}}
<h1>Здравствуйте, {{ user.fullName }}!</h1>
<p>Спасибо за регистрацию в нашем блоге.</p>
<p><a href="https://example.com/posts">Читать статьи</a></p>В письме ссылки пишут полным адресом с доменом: почтовая программа не знает, с какого сайта пришло письмо.
Отправка
// 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('Добро пожаловать в блог')
.htmlView('emails/welcome', { user })
})Функция получает объект message и настраивает его цепочкой: получатель, отправитель, тема и HTML из шаблона с данными. После регистрации письмо придёт на указанную почту.
Отправка в фоне
mail.send ждёт, пока SMTP-сервер примет письмо, и посетитель всё это время смотрит на загрузку страницы. Метод sendLater ставит письмо в очередь и сразу возвращает управление:
// app/controllers/register_controller.ts
await mail.sendLater((message) => {
message.to(user.email).subject('Добро пожаловать в блог').htmlView('emails/welcome', { user })
})По умолчанию очередь хранится в памяти процесса: письма уходят сразу после ответа, но пропадут, если сервер перезапустится.
Класс письма
Когда одно письмо отправляют из разных мест, его оформляют классом:
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 = 'Добро пожаловать в блог'
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))Теперь всё о письме собрано в одном файле, а в контроллере остаётся одна строка.
Частая ошибка
Адрес отправителя в from не совпадает с доменом, к которому привязан SMTP-аккаунт. Почтовые сервисы считают такие письма поддельными, и они попадают в спам или вовсе отклоняются. Отправляйте с адреса на своём домене, например blog@example.com, и настройте для домена записи SPF и DKIM.