メール
@adonisjs/mail
パッケージを使用して、AdonisJSアプリケーションからメールを送信できます。このメールパッケージは、Nodemailerをベースにしており、以下のような利便性の向上をもたらします。
- メールメッセージを設定するためのフルエントAPI。
- より良い組織とテストのために、メールをクラスとして定義する機能。
- 公式にメンテナンスされているトランスポートの包括的なスイート。
smtp
、ses
、mailgun
、sparkpost
、resend
、brevo
を含みます。 - Fakes APIを使用したテストの改善。
- メールをキューに入れるためのメールメッセンジャー。
- カレンダーイベントを生成するための機能API。
インストール
次のコマンドを使用してパッケージをインストールし、設定します:
node ace add @adonisjs/mail
# CLIフラグを使用して使用するトランスポートを事前に定義する
node ace add @adonisjs/mail --transports=resend --transports=smtp
-
検出されたパッケージマネージャを使用して
@adonisjs/mail
パッケージをインストールします。 -
次のサービスプロバイダとコマンドを
adonisrc.ts
ファイル内に登録します。{commands: [// ...他のコマンド() => import('@adonisjs/mail/commands')],providers: [// ...他のプロバイダ() => import('@adonisjs/mail/mail_provider')]} -
config/mail.ts
ファイルを作成します。 -
選択したメールサービスの環境変数とそのバリデーションを定義します。
設定
メールパッケージの設定はconfig/mail.ts
ファイルに保存されます。このファイル内で、複数のメールサービスをmailers
として設定できます。
詳細はこちら:Config stub
import env from '#start/env'
import { defineConfig, transports } from '@adonisjs/mail'
const mailConfig = defineConfig({
default: 'smtp',
/**
* "from"プロパティのための静的なアドレス。メールで明示的なfromアドレスが設定されていない場合に使用されます。
*/
from: {
address: '',
name: '',
},
/**
* "reply-to"プロパティのための静的なアドレス。メールで明示的なreplyToアドレスが設定されていない場合に使用されます。
*/
replyTo: {
address: '',
name: '',
},
/**
* mailersオブジェクトは、異なるトランスポートまたは異なるオプションを使用する同じトランスポートを使用して複数のメーラーを設定するために使用できます。
*/
mailers: {
smtp: transports.smtp({
host: env.get('SMTP_HOST'),
port: env.get('SMTP_PORT'),
}),
resend: transports.resend({
key: env.get('RESEND_API_KEY'),
baseUrl: 'https://api.resend.com',
}),
},
})
-
default
-
デフォルトでメールを送信するために使用するメーラーの名前。
-
from
-
from
プロパティに使用する静的なグローバルアドレス。メールで明示的なfrom
アドレスが定義されていない場合に使用されます。 -
replyTo
-
reply-to
プロパティに使用する静的なグローバルアドレス。メールで明示的なreplyTo
アドレスが定義されていない場合に使用されます。 -
mailers
-
mailers
オブジェクトは、メールを送信するために使用する1つ以上のメーラーを設定するために使用されます。mail.use
メソッドを使用してメーラーを実行時に切り替えることができます。
トランスポートの設定
公式にサポートされているトランスポートが受け入れる設定オプションの完全なリファレンスは次のとおりです。
詳細はこちら:TypeScript types for config object
次の設定オプションは、Mailgunの/messages.mime
APIエンドポイントに送信されます。
{
mailers: {
mailgun: transports.mailgun({
baseUrl: 'https://api.mailgun.net/v3',
key: env.get('MAILGUN_API_KEY'),
domain: env.get('MAILGUN_DOMAIN'),
/**
* メールの`mail.send`メソッドを呼び出す際にランタイムでオーバーライドできる次のオプション。
*/
oDkim: true,
oTags: ['transactional', 'adonisjs_app'],
oDeliverytime: new Date(2024, 8, 18),
oTestMode: false,
oTracking: false,
oTrackingClick: false,
oTrackingOpens: false,
headers: {
// h:プレフィックス付きヘッダー
},
variables: {
appId: '',
userId: '',
// v:プレフィックス付き変数
}
})
}
}
次の設定オプションは、Nodemailerにそのまま転送されます。そのため、Nodemailerのドキュメントも参照してください。
{
mailers: {
smtp: transports.smtp({
host: env.get('SMTP_HOST'),
port: env.get('SMTP_PORT'),
secure: false,
auth: {
type: 'login',
user: env.get('SMTP_USERNAME'),
pass: env.get('SMTP_PASSWORD')
},
tls: {},
ignoreTLS: false,
requireTLS: false,
pool: false,
maxConnections: 5,
maxMessages: 100,
})
}
}
次の設定オプションは、Nodemailerにそのまま転送されます。そのため、Nodemailerのドキュメントも参照してください。
SESトランスポートを使用するには、@aws-sdk/client-ses
パッケージをインストールする必要があります。
{
mailers: {
ses: transports.ses({
/**
* aws sdkに転送されます
*/
apiVersion: '2010-12-01',
region: 'us-east-1',
credentials: {
accessKeyId: env.get('AWS_ACCESS_KEY_ID'),
secretAccessKey: env.get('AWS_SECRET_ACCESS_KEY'),
},
/**
* Nodemailer固有の設定
*/
sendingRate: 10,
maxConnections: 5,
})
}
}
次の設定オプションは、SparkPostの/transmissions
APIエンドポイントに送信されます。
{
mailers: {
sparkpost: transports.sparkpost({
baseUrl: 'https://api.sparkpost.com/api/v1',
key: env.get('SPARKPOST_API_KEY'),
/**
* メールの`mail.send`メソッドを呼び出す際にランタイムでオーバーライドできる次のオプション。
*/
startTime: new Date(),
openTracking: false,
clickTracking: false,
initialOpen: false,
transactional: true,
sandbox: false,
skipSuppression: false,
ipPool: '',
})
}
}
次の設定オプションは、Resendの/emails
APIエンドポイントに送信されます。
{
mailers: {
resend: transports.resend({
baseUrl: 'https://api.resend.com',
key: env.get('RESEND_API_KEY'),
/**
* メールの`mail.send`メソッドを呼び出す際にランタイムでオーバーライドできる次のオプション。
*/
tags: [
{
name: 'category',
value: 'confirm_email'
}
]
})
}
}
基本的な例
初期設定が完了したら、mail.send
メソッドを使用してメールを送信できます。メールサービスは、設定ファイルで構成されたdefault
メーラーのシングルトンインスタンスであり、MailManagerクラスのインスタンスです。
mail.send
メソッドは、Messageクラスのインスタンスをコールバックに渡し、メールをconfig
ファイルで構成されたdefault
メーラーを使用して配信します。
次の例では、新しいユーザーアカウントを作成した後、コントローラーからメールをトリガーしています。
import User from '#models/user'
import { HttpContext } from '@adonisjs/core/http'
import mail from '@adonisjs/mail/services/main'
export default class UsersController {
async store({ request }: HttpContext) {
/**
* デモンストレーションのためだけです。データをデータベースに保存する前にデータをバリデーションする必要があります。
*/
const user = await User.create(request.all())
await mail.send((message) => {
message
.to(user.email)
.from('info@example.org')
.subject('メールアドレスの確認')
.htmlView('emails/verify_email', { user })
})
}
}
メールのキューイング
メールの送信には時間がかかる場合があるため、キューに入れてバックグラウンドでメールを送信あります。mail.sendLater
メソッドを使用して同じことができます。
sendLater
メソッドは、send
メソッドと同じパラメーターを受け入れます。ただし、メールを即座に送信する代わりに、Mailメッセンジャーを使用してキューに入れます。
await mail.send((message) => {
await mail.sendLater((message) => {
message
.to(user.email)
.from('info@example.org')
.subject('メールアドレスの確認')
.htmlView('emails/verify_email', { user })
})
デフォルトでは、mailメッセンジャーはインメモリキューを使用します。つまり、プロセスが保留中のジョブで終了すると、キューはジョブを破棄します。アプリケーションのUIが手動のアクションでメールの再送信を許可している場合、これは大きな問題ではありません。ただし、カスタムメッセンジャーを設定し、データベースをバックエンドキューとして使用することもできます。
メールのキューイングにBullMQを使用する
npm i bullmq
次の例では、mail.setMessenger
メソッドを使用して、ジョブを保存するためにbullmq
を使用するカスタムキューを設定します。
ジョブにはコンパイルされたメール、ランタイム設定、およびメーラー名が含まれます。後でこれらのデータを使用してワーカープロセス内でメールを送信します。
import { Queue } from 'bullmq'
import mail from '@adonisjs/mail/services/main'
const emailsQueue = new Queue('emails')
mail.setMessenger((mailer) => {
return {
async queue(mailMessage, config) {
await emailsQueue.add('send_email', {
mailMessage,
config,
mailerName: mailer.name,
})
}
}
})
最後に、キューワーカーのコードを記述します。アプリケーションのワークフローに応じて、ジョブを処理するために別のプロセスを起動する必要があるかもしれません。
次の例では:
emails
キューからsend_email
という名前のジョブを処理します。- ジョブデータからコンパイルされたメール、ランタイム設定、およびメーラー名にアクセスします。
mailer.sendCompiled
メソッドを使用してメールを送信します。
import { Worker } from 'bullmq'
import mail from '@adonisjs/mail/services/main'
new Worker('emails', async (job) => {
if (job.name === 'send_email') {
const {
mailMessage,
config,
mailerName
} = job.data
await mail
.use(mailerName)
.sendCompiled(mailMessage, config)
}
})
以上です!mail.sendLater
メソッドを引き続き使用できます。ただし、今回はメールがredisデータベースにキューイングされます。
メーラーの切り替え
mail.use
メソッドを使用して、設定ファイルで定義されたメーラー間を切り替えることができます。mail.use
メソッドは、メーラーの名前(設定ファイルで定義された名前)を受け入れ、Mailerクラスのインスタンスを返します。
import mail from '@adonisjs/mail/services/main'
mail.use() // Instance of default mailer
mail.use('mailgun') // Mailgun mailer instance
以下の例では、メーラーインスタンスを使用してメールを送信するために mailer.send
または mailer.sendLater
メソッドを呼び出すことができます。
await mail
.use('mailgun')
.send((message) => {
})
await mail
.use('mailgun')
.sendLater((message) => {
})
メーラーインスタンスはプロセスのライフサイクルの間キャッシュされます。既存のインスタンスを破棄し、新しいインスタンスをゼロから作成するために mail.close
メソッドを使用できます。
import mail from '@adonisjs/mail/services/main'
/**
* トランスポートを閉じてインスタンスをキャッシュから削除します
*/
await mail.close('mailgun')
/**
* 新しいインスタンスを作成します
*/
mail.use('mailgun')
テンプレートエンジンの設定
デフォルトでは、メールパッケージはメールの HTML および プレーンテキスト コンテンツを定義するために Edge テンプレートエンジン を使用するように設定されています。
ただし、次の例に示すように、Message.templateEngine
プロパティをオーバーライドしてカスタムテンプレートエンジンを登録することもできます。
参照: メールコンテンツの定義
import { Message } from '@adonisjs/mail'
Message.templateEngine = {
async render(templatePath, data) {
return someTemplateEngine.render(templatePath, data)
}
}
イベント
@adonisjs/mail
パッケージによってディスパッチされるイベントのリストを表示するには、イベントリファレンスガイドを参照してください。
メッセージの設定
メールのプロパティは、mail.send
または mail.sendLater
メソッドを使用して作成されたコールバック関数に提供される Message クラスを使用して定義されます。
import { Message } from '@adonisjs/mail'
import mail from '@adonisjs/mail/services/main'
await mail.send((message) => {
console.log(message instanceof Message) // true
})
await mail.sendLater((message) => {
console.log(message instanceof Message) // true
})
件名と送信元の定義
message.subject
メソッドを使用してメールの件名を定義し、message.from
メソッドを使用してメールの送信元を定義できます。
await mail.send((message) => {
message
.subject('メールアドレスの確認')
.from('info@example.org')
})
from
メソッドは、メールアドレスを文字列または送信者名とメールアドレスのオブジェクトとして受け入れます。
message
.from({
address: 'info@example.com',
name: 'AdonisJS'
})
送信者は、個々のメッセージに明示的な送信者が定義されていない場合に、グローバルに定義された設定ファイル内の送信者が使用されます。
const mailConfig = defineConfig({
from: {
address: 'info@example.com',
name: 'AdonisJS'
}
})
受信者の定義
message.to
、message.cc
、および message.bcc
メソッドを使用してメールの受信者を定義できます。これらのメソッドは、メールアドレスを文字列または受信者名とメールアドレスのオブジェクトとして受け入れます。
await mail.send((message) => {
message
.to(user.email)
.cc(user.team.email)
.bcc(user.team.admin.email)
})
await mail.send((message) => {
message
.to({
address: user.email,
name: user.fullName,
})
.cc({
address: user.team.email,
name: user.team.name,
})
.bcc({
address: user.team.admin.email,
name: user.team.admin.fullName,
})
})
複数の cc
および bcc
受信者を、メールアドレスの配列またはメールアドレスと受信者名のオブジェクトの配列として定義できます。
await mail.send((message) => {
message
.cc(['first@example.com', 'second@example.com'])
.bcc([
{
name: 'First recipient',
address: 'first@example.com'
},
{
name: 'Second recipient',
address: 'second@example.com'
}
])
})
message.replyTo
メソッドを使用して replyTo
メールアドレスを定義することもできます。
await mail.send((message) => {
message
.from('info@example.org')
.replyTo('noreply@example.org')
})
メールコンテンツの定義
message.html
または message.text
メソッドを使用してメールの HTML および プレーンテキスト コンテンツを定義できます。
await mail.send((message) => {
/**
* HTML コンテンツ
*/
message.html(`
<h1> メールアドレスの確認 </h1>
<p> <a href="https://myapp.com">こちらをクリック</a>してメールアドレスを確認してください</a>
`)
/**
* プレーンテキストコンテンツ
*/
message.text(`
メールアドレスの確認
メールアドレスを確認するには、https://myapp.com にアクセスしてください
`)
})
Edge テンプレートの使用
インラインコンテンツを記述することは煩雑な場合があるため、代わりにEdgeテンプレートを使用することもできます。すでに Edge の設定 を行っている場合は、message.htmlView
および message.textView
メソッドを使用してテンプレートをレンダリングできます。
node ace make:view emails/verify_email_html
node ace make:view emails/verify_email_text
await mail.send((message) => {
message.htmlView('emails/verify_email_html', stateToShare)
message.textView('emails/verify_email_text', stateToShare)
})
メールマークアップのための MJML の使用
MJMLは、すべてのメールクライアントでメールが見栄え良く表示されるようにするための複雑なHTMLを書かずにメールを作成するためのマークアップ言語です。
まず、mjml パッケージをnpmからインストールします。
npm i mjml
インストールが完了したら、Edgeテンプレート内でMJMLマークアップを記述するために@mjml
タグで囲むことができます。
MJMLの出力にはhtml
、head
、およびbody
タグが含まれているため、Edgeテンプレート内でこれらを定義する必要はありません。
@mjml()
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text>
Hello World!
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
@end
@mjml
タグに MJML の設定オプション をプロップスとして渡すこともできます。
@mjml({
keepComments: false,
fonts: {
Lato: 'https://fonts.googleapis.com/css?family=Lato:400,500,700'
}
})
ファイルの添付
message.attach
メソッドを使用して、メールに添付ファイルを送信できます。attach
メソッドは、添付ファイルの絶対パスまたはファイルシステムのURLを受け入れます。
import app from '@adonisjs/core/services/app'
await mail.send((message) => {
message.attach(app.makePath('uploads/invoice.pdf'))
})
options.filename
プロパティを使用して添付ファイルのファイル名を定義することもできます。
message.attach(app.makePath('uploads/invoice.pdf'), {
filename: 'invoice_october_2023.pdf'
})
message.attach
メソッドが受け入れるオプションの完全なリストは次のとおりです。
オプション | 説明 |
---|---|
filename |
添付ファイルの表示名。デフォルトは添付ファイルパスのベース名です。 |
contentType |
添付ファイルのコンテンツタイプ。設定されていない場合、contentType はファイルの拡張子から推測されます。 |
contentDisposition |
添付ファイルのコンテンツディスポジションタイプ。デフォルトは attachment です |
headers |
添付ファイルノードのカスタムヘッダー。ヘッダープロパティはキーと値のペアです |
ストリームとバッファからのファイルの添付
message.attachData
メソッドを使用して、ストリームとバッファからメール添付ファイルを作成できます。メソッドは、読み込み可能なストリームまたはバッファを第1引数として、オプションオブジェクトを第2引数として受け入れます。
message.attachData
メソッドは、mail.sendLater
メソッドを使用してメールをキューに入れる場合には使用しないでください。キューに入れられたジョブはシリアライズされ、データベース内に永続化されるため、生データを添付するとストレージサイズが増加します。
また、message.attachData
メソッドを使用してストリームを添付すると、メールのキューイングが失敗します。
message.attachData(fs.createReadStream('./invoice.pdf'), {
filename: 'invoice_october_2023.pdf'
})
message.attachData(Buffer.from('aGVsbG8gd29ybGQh'), {
encoding: 'base64',
filename: 'greeting.txt',
})
画像の埋め込み
embedImage
ビューヘルパーを使用して、メールのコンテンツ内に画像を埋め込むことができます。embedImage
メソッドは、画像を添付ファイルとしてマークし、そのコンテンツIDを画像のソースとして使用します。
<img src="{{
embedImage(app.makePath('assets/hero.jpg'))
}}" />
以下が出力されるHTMLです。
<img src="cid:a-random-content-id" />
次の添付ファイルが自動的にメールペイロードに定義されます。
{
attachments: [{
path: '/root/app/assets/hero.jpg',
filename: 'hero.jpg',
cid: 'a-random-content-id'
}]
}
バッファから画像を埋め込む
embedImage
メソッドと同様に、embedImageData
メソッドを使用して生データから画像を埋め込むことができます。
<img src="{{
embedImageData(rawBuffer, { filename: 'hero.jpg' })
}}" />
カレンダーイベントの添付
message.icalEvent
メソッドを使用して、メールにカレンダーイベントを添付できます。icalEvent
メソッドは、最初のパラメータとしてイベントの内容、2番目のパラメータとして options
オブジェクトを受け入れます。
const contents = 'BEGIN:VCALENDAR\r\nPRODID:-//ACME/DesktopCalendar//EN\r\nMETHOD:REQUEST\r\n...'
await mail.send((message) => {
message.icalEvent(contents, {
method: 'PUBLISH',
filename: 'invite.ics',
})
})
イベントファイルの内容を手動で定義するのは煩雑な場合があるため、icalEvent
メソッドにコールバック関数を渡し、JavaScript APIを使用して招待内容を生成することもできます。
コールバック関数に提供される calendar
オブジェクトは、ical-generatornpmパッケージの参照ですので、パッケージのREADMEファイルも参照してください。
message.icalEvent((calendar) => {
calendar
.createEvent({
summary: 'ALS のサポートを追加',
start: DateTime.local().plus({ minutes: 30 }),
end: DateTime.local().plus({ minutes: 60 }),
})
}, {
method: 'PUBLISH',
filename: 'invite.ics',
})
ファイルまたは URL からの招待内容の読み取り
icalEventFromFile
メソッドまたは icalEventFromUrl
メソッドを使用して、ファイルまたは HTTP URL から招待内容を定義できます。
message.icalEventFromFile(
app.resourcesPath('calendar-invites/invite.ics'),
{
filename: 'invite.ics',
method: 'PUBLISH'
}
)
message.icalEventFromFile(
'https://myapp.com/users/1/invite.ics',
{
filename: 'invite.ics',
method: 'PUBLISH'
}
)
メールヘッダの定義
message.header
メソッドを使用して、追加のメールヘッダを定義できます。メソッドは、第1パラメータとしてヘッダキー、第2パラメータとして値を受け入れます。
message.header('x-my-key', 'ヘッダの値')
/**
* 値の配列を定義することもできます
*/
message.header('x-my-key', ['ヘッダの値', '別の値'])
デフォルトでは、メールヘッダはエンコードされ、78バイトを超えないプレーンASCIIメッセージの要件を満たすために折り返されます。ただし、エンコーディングルールをバイパスしたい場合は、message.preparedHeader
メソッドを使用してヘッダを設定することもできます。
message.preparedHeader(
'x-unprocessed',
'非ASCII文字を含む非常に長いヘッダまたは値 👮',
)
List
ヘッダの定義
メッセージクラスには、List-UnsubscribeやList-Help のような複雑なヘッダを簡単に定義するためのヘルパーメソッドが用意されています。List
ヘッダのエンコーディングルールについては、nodemailerのウェブサイトを参照してください。
message.listHelp('admin@example.com?subject=help')
// List-Help: <mailto:admin@example.com?subject=help>
message.listUnsubscribe({
url: 'http://example.com',
comment: 'コメント'
})
// List-Unsubscribe: <http://example.com> (コメント)
/**
* ヘッダを複数回繰り返す
*/
message.listSubscribe('admin@example.com?subject=subscribe')
message.listSubscribe({
url: 'http://example.com',
comment: '購読'
})
// List-Subscribe: <mailto:admin@example.com?subject=subscribe>
// List-Subscribe: <http://example.com> (購読)
その他の任意の List
ヘッダについては、addListHeader
メソッドを使用できます。
message.addListHeader('post', 'http://example.com/post')
// List-Post: <http://example.com/post>
クラスベースのメール
mail.send
メソッドのクロージャ内でメールを記述する代わりに、より良い組織化と簡単なテストのために、専用のメールクラスに移動できます。
メールクラスは ./app/mails
ディレクトリに保存され、各ファイルが1つのメールを表します。make:mail
aceコマンドを実行してメールクラスを作成できます。
参照: メール作成コマンド
node ace make:mail verify_email
メールクラスは BaseMail クラスを拡張し、以下のプロパティとメソッドが用意されています。prepare
メソッド内で this.message
プロパティを使用してメールメッセージを設定できます。
import User from '#models/user'
import { BaseMail } from '@adonisjs/mail'
export default class VerifyEmailNotification extends BaseMail {
from = 'sender_email@example.org'
subject = 'Verify email'
prepare() {
this.message.to('user_email@example.org')
}
}
-
from
-
送信者のメールアドレスを設定します。このプロパティを省略する場合は、送信者を定義するために
message.from
メソッドを呼び出す必要があります。 -
subject
-
メールの件名を設定します。このプロパティを省略する場合は、
message.subject
メソッドを使用してメールの件名を定義する必要があります。 -
replyTo
-
replyTo
のメールアドレスを設定します。 -
prepare
-
prepare
メソッドは、build
メソッドによって自動的に呼び出され、メールメッセージの送信の準備をします。このメソッド内でメールの内容、添付ファイル、受信者などを定義する必要があります。
-
build 継承
-
build
メソッドはBaseMail
クラスから継承されたメソッドです。このメソッドはメールの送信時に自動的に呼び出されます。このメソッドをオーバーライドする場合は、元の実装を参照してください。
メールクラスを使用してメールを送信する
mail.send
メソッドを呼び出し、メールクラスのインスタンスを渡すことでメールを送信できます。例:
import mail from '@adonisjs/mail/services/main'
import VerifyEmailNotification from '#mails/verify_email'
await mail.send(new VerifyEmailNotification())
import mail from '@adonisjs/mail/services/main'
import VerifyEmailNotification from '#mails/verify_email'
await mail.sendLater(new VerifyEmailNotification())
メールクラスにデータを共有する場合は、コンストラクタ引数を使用してデータを渡すことができます。例:
/**
* ユーザーを作成する
*/
const user = await User.create(payload)
await mail.send(
/**
* メールクラスにユーザーを渡す
*/
new VerifyEmailNotification(user)
)
メールクラスのテスト
メールクラスを使用する主な利点の1つは、テストの経験が向上することです。メールを送信せずにメールクラスをビルドし、メッセージのプロパティに対してアサーションを記述できます。
import { test } from '@japa/runner'
import VerifyEmailNotification from '#mails/verify_email'
test.group('Verify email notification', () => {
test('prepare email for sending', async () => {
const email = new VerifyEmailNotification()
/**
* メールメッセージをビルドし、テンプレートをレンダリングして
* メールのHTMLとプレーンテキストの内容を計算します
*/
await email.buildWithContents()
/**
* メッセージが期待どおりにビルドされたことを確認するためのアサーションを記述します
*/
email.message.assertTo('user@example.org')
email.message.assertFrom('info@example.org')
email.message.assertSubject('Verify email address')
email.message.assertReplyTo('no-reply@example.org')
})
})
次のようにメッセージの内容に対してアサーションを記述することもできます。
const email = new VerifyEmailNotification()
await email.buildWithContents()
email.message.assertHtmlIncludes(
`<a href="/emails/1/verify"> メールアドレスを確認する </a>`
)
email.message.assertTextIncludes('メールアドレスを確認する')
また、添付ファイルに対してもアサーションを記述できます。アサーションはファイルベースの添付ファイルに対してのみ機能し、ストリームや生データには対応していません。
const email = new VerifyEmailNotification()
await email.buildWithContents()
email.message.assertAttachment(
app.makePath('uploads/invoice.pdf')
)
利用可能なアサーションメソッドの一覧については、Message クラスのソースコードを参照してください。
フェイクメーラー
テスト中にメールを送信しないようにするために、フェイクメーラーを使用できます。フェイクメーラーは、メモリ内で送信されたすべてのメールを収集し、それらに対してアサーションを行うための使いやすいAPIを提供します。
次の例では、以下の手順でフェイクメーラーを使用します。
mail.fake
メソッドを使用して FakeMailer のインスタンスを作成します。/register
エンドポイントAPIを呼び出します。- フェイクメーラーの
mails
プロパティを使用してVerifyEmailNotification
が送信されたことをアサートします。
import { test } from '@japa/runner'
import mail from '@adonisjs/mail/services/main'
import VerifyEmailNotification from '#mails/verify_email'
test.group('Users | register', () => {
test('create a new user account', async ({ client, route }) => {
/**
* フェイクモードをオンにする
*/
const { mails } = mail.fake()
/**
* APIコールを行う
*/
await client
.post(route('users.store'))
.send(userData)
/**
* コントローラが VerifyEmailNotification メールを送信したことをアサートする
*/
mails.assertSent(VerifyEmailNotification, ({ message }) => {
return message
.hasTo(userData.email)
.hasSubject('メールアドレスの確認')
})
})
})
テストの記述が終わったら、mail.restore
メソッドを使用してフェイクを元に戻す必要があります。
test('create a new user account', async ({ client, route, cleanup }) => {
const { mails } = mail.fake()
/**
* cleanupフックは、テストが正常に終了するかエラーが発生すると実行されます。
*/
cleanup(() => {
mail.restore()
})
})
アサーションの記述
mails.assertSent
メソッドは、メールクラスのコンストラクタを最初の引数として受け入れ、期待されるクラスのメールが見つからない場合に例外をスローします。
const { mails } = mail.fake()
/**
* メールが送信されたことをアサートする
*/
mails.assertSent(VerifyEmailNotification)
assertSent
メソッドにコールバック関数を渡すことで、メールが期待される受信者に送信されたか、正しい件名を持っているかなどをさらにチェックできます。
コールバック関数はメールクラスのインスタンスを受け取り、.message
プロパティを使用して message オブジェクトにアクセスできます。
mails.assertSent(VerifyEmailNotification, (email) => {
return email.message.hasTo(userData.email)
})
コールバック内で message
オブジェクトに対してアサーションを実行できます。例:
mails.assertSent(VerifyEmailNotification, (email) => {
email.message.assertTo(userData.email)
email.message.assertFrom('info@example.org')
email.message.assertSubject('メールアドレスの確認')
/**
* すべてのアサーションがパスしたため、メールが送信されたとみなすために true を返します。
*/
return true
})
送信されなかったことをアサートする
mails.assertNotSent
メソッドを使用して、メールが送信されなかったことをアサートできます。このメソッドは assertSent
メソッドの逆であり、同じ引数を受け入れます。
const { mails } = mail.fake()
mails.assertNotSent(PasswordResetNotification)
送信されたメールの数をアサートする
最後に、assertSentCount
メソッドと assertNoneSent
メソッドを使用して送信されたメールの数をアサートできます。
const { mails } = mail.fake()
// 2通のメールが合計で送信されたことをアサートする
mails.assertSentCount(2)
// VerifyEmailNotification が1回だけ送信されたことをアサートする
mails.assertSentCount(VerifyEmailNotification, 1)
const { mails } = mail.fake()
// 送信されたメールがないことをアサートする
mails.assertNoneSent()
キューに入れられたメールのアサート
mail.sendLater
メソッドを使用してキューに入れられたメールがある場合、次のメソッドを使用してアサーションを記述できます。
const { mails } = mail.fake()
/**
* VerifyEmailNotification メールがキューに入れられたことをアサートする
* オプションで、メールを絞り込むためのファインダ関数を渡すこともできます
*/
mails.assertQueued(VerifyEmailNotification)
/**
* PasswordResetNotification メールがキューに入れられなかったことをアサートする
* オプションで、メールを絞り込むためのファインダ関数を渡すこともできます
*/
mails.assertNotQueued(PasswordResetNotification)
/**
* 合計2通のメールがキューに入れられたことをアサートする
*/
mails.assertQueuedCount(2)
/**
* VerifyEmailNotification メールが1回だけキューに入れられたことをアサートする
*/
mails.assertQueuedCount(VerifyEmailNotification , 1)
/**
* キューに何も入っていないことをアサートする
*/
mails.assertNoneQueued()
送信またはキューに入れられたメールのリストを取得する
テスト中に送信/キューに入れられたメールの配列を取得するには、mails.sent
またはmails.queued
メソッドを使用できます。
const { mails } = mail.fake()
const sentEmails = mails.sent()
const queuedEmails = mails.queued()
const email = sentEmails.find((email) => {
return email instanceof VerifyEmailNotification
})
if (email) {
email.message.assertTo(userData.email)
email.message.assertFrom(userData.email)
email.message.assertHtmlIncludes('<a href="/verify/email"> メールアドレスを確認する</a>')
}
カスタムトランスポートの作成
AdonisJSメールトランスポートは、Nodemailerトランスポートを基にして構築されているため、Mailパッケージに登録する前にNodemailerトランスポートを作成/使用する必要があります。
このガイドでは、nodemailer-postmark-transportをAdonisJSメールトランスポートにラップします。
npm i nodemailer nodemailer-postmark-transport
以下の例では、メールの送信はnodemailer
によって行われます。AdonisJSトランスポートは、メッセージをnodemailerに転送し、その応答をMailResponseのインスタンスに正規化します。
import nodemailer from 'nodemailer'
import nodemailerTransport from 'nodemailer-postmark-transport'
import { MailResponse } from '@adonisjs/mail'
import type {
NodeMailerMessage,
MailTransportContract
} from '@adonisjs/mail/types'
/**
* トランスポートが受け入れる設定
*/
export type PostMarkConfig = {
auth: {
apiKey: string
}
}
/**
* トランスポートの実装
*/
export class PostMarkTransport implements MailTransportContract {
#config: PostMarkConfig
constructor(config: PostMarkConfig) {
this.#config = config
}
#createNodemailerTransport(config: PostMarkConfig) {
return nodemailer.createTransport(nodemailerTransport(config))
}
async send(
message: NodeMailerMessage,
config?: PostMarkConfig
): Promise<MailResponse> {
/**
* Nodemailerトランスポートを作成する
*/
const transporter = this.#createNodemailerTransport({
...this.#config,
...config,
})
/**
* メールを送信する
*/
const response = await transporter.sendMail(message)
/**
* 応答を「MailResponse」クラスのインスタンスに正規化する
*/
return new MailResponse(response.messageId, response.envelope, response)
}
}
設定ファクトリ関数の作成
トランスポートをconfig/mail.ts
ファイル内で参照するためには、トランスポートの実装を返すファクトリ関数を作成する必要があります。
トランスポートの実装と同じファイルに以下のコードを書くことができます。
import type {
NodeMailerMessage,
MailTransportContract,
MailManagerTransportFactory
} from '@adonisjs/mail/types'
export function postMarkTransport(
config: PostMarkConfig
): MailManagerTransportFactory {
return () => {
return new PostMarkTransport(config)
}
}
トランスポートの使用
最後に、postMarkTransport
ヘルパーを使用して設定ファイル内でトランスポートを参照できます。
import env from '#start/env'
import { defineConfig } from '@adonisjs/mail'
import { postMarkTransport } from 'my-custom-package'
const mailConfig = defineConfig({
mailers: {
postmark: postMarkTransport({
auth: {
apiKey: env.get('POSTMARK_API_KEY'),
},
}),
},
})