Configuring message
The properties of an email are defined using the Message class. An instance of this class is provided to the callback function created using the mail.send
, or mail.sendLater
methods.
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
})
Defining subject and sender
You may define the email subject using the message.subject
method and the email's sender using the message.from
method.
await mail.send((message) => {
message
.subject('Verify your email address')
.from('info@example.org')
})
The from
method accepts the email address as a string or an object with the sender name and the email address.
message
.from({
address: 'info@example.com',
name: 'AdonisJS'
})
The sender can also be defined globally within the config file. The global sender will be used if no explicit sender is defined for an individual message.
const mailConfig = defineConfig({
from: {
address: 'info@example.com',
name: 'AdonisJS'
}
})
Defining recipients
You may define the email recipients using the message.to
, message.cc
, and the message.bcc
methods. These methods accept the email address as a string or an object with the recipient name and the email address.
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,
})
})
You can define multiple cc
and bcc
recipients as an array of email addresses or an object with email addresses and the recipient name.
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'
}
])
})
You may also define the replyTo
email address using the message.replyTo
method.
await mail.send((message) => {
message
.from('info@example.org')
.replyTo('noreply@example.org')
})
Defining email contents
You may define the HTML and Plain text contents for an email using message.html
or message.text
methods.
await mail.send((message) => {
/**
* HTML contents
*/
message.html(`
<h1> Verify email address </h1>
<p> <a href="https://myapp.com">Click here</a> to verify your email address </a>
`)
/**
* Plain text contents
*/
message.text(`
Verify email address
Please visit https://myapp.com to verify your email address
`)
})
Using Edge templates
Since writing inline content could be cumbersome, you may use Edge templates instead. If you have already configured Edge, you may use the message.htmlView
and message.textView
methods to render templates.
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)
})
Using MJML for email markup
MJML is a markup language for creating emails without writing all the complex HTML to make your emails look good in every email client.
The first step is to install the mjml package from npm.
npm i mjml
Once done, you can write MJML markup inside your Edge templates by wrapping it inside the @mjml
tag.
Since the output of MJML contains the html
, head
, and body
tags, it is unnecessary to define them within your Edge templates.
@mjml()
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text>
Hello World!
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
@end
You may pass the MJML configuration options as props to the @mjml
tag.
@mjml({
keepComments: false,
fonts: {
Lato: 'https://fonts.googleapis.com/css?family=Lato:400,500,700'
}
})
Attaching files
You may use the message.attach
method to send attachments in an email. The attach
method accepts an absolute path or a file system URL of a file you want to send as an attachment.
import app from '@adonisjs/core/services/app'
await mail.send((message) => {
message.attach(app.makePath('uploads/invoice.pdf'))
})
You may define the filename for the attachment using the options.filename
property.
message.attach(app.makePath('uploads/invoice.pdf'), {
filename: 'invoice_october_2023.pdf'
})
The complete list of options accepted by the message.attach
method follows.
Option | Description |
---|---|
filename |
The display name for the attachment. Defaults to the basename of the attachment path. |
contentType |
The content type for the attachment. If not set, the contentType will be inferred from the file extension. |
contentDisposition |
Content disposition type for the attachment. Defaults to attachment |
headers |
Custom headers for the attachment node. The headers property is a key-value pair |
Attaching files from streams and buffers
You may create email attachments from streams and buffers using the message.attachData
method. The method accepts a readable stream or the buffer as the first argument and the options object as the second argument.
The message.attachData
method should not be used when queueing emails using the mail.sendLater
method. Since queued jobs are serialized and persisted inside a database, attaching raw data will increase the storage size.
Moreover, queueing an email will fail if you attach a stream using the message.attachData
method.
message.attach(fs.createReadStream('./invoice.pdf'), {
filename: 'invoice_october_2023.pdf'
})
message.attach(Buffer.from('aGVsbG8gd29ybGQh'), {
encoding: 'base64',
filename: 'greeting.txt',
})
Embedding images
You may embed images within the contents of your email using the embedImage
view helper. The embedImage
method under the hood uses CID to mark the image as an attachment and uses its content id as the source of the image.
<img src="{{
embedImage(app.makePath('assets/hero.jpg'))
}}" />
Following will be the output HTML
<img src="cid:a-random-content-id" />
The following attachment will be defined automatically on the email payload.
{
attachments: [{
path: '/root/app/assets/hero.jpg',
filename: 'hero.jpg',
cid: 'a-random-content-id'
}]
}
Embedding images from buffers
Like the embedImage
method, you may use the embedImageData
method to embed an image from raw data.
<img src="{{
embedImageData(rawBuffer, { filename: 'hero.jpg' })
}}" />
Attaching calendar events
You may attach calendar events to an email using the message.icalEvent
method. The icalEvent
method accepts the event contents as the first parameter and the options
object as the second parameter.
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',
})
})
Since defining the event file contents manually can be cumbersome, you may pass a callback function to the icalEvent
method and generate the invite contents using JavaScript API.
The calendar
object provided to the callback function is a reference of the ical-generator npm package, so make sure to go through the package's README file as well.
message.icalEvent((calendar) => {
calendar
.createEvent({
summary: 'Adding support for ALS',
start: DateTime.local().plus({ minutes: 30 }),
end: DateTime.local().plus({ minutes: 60 }),
})
}, {
method: 'PUBLISH',
filename: 'invite.ics',
})
Reading invite contents from a file or a URL
You may define the invite contents from a file or an HTTP URL using the icalEventFromFile
or icalEventFromUrl
methods.
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'
}
)
Defining email headers
You may define additional email headers using the message.header
method. The method accepts the header key as the first parameter and the value as the second parameter.
message.header('x-my-key', 'header value')
/**
* Define an array of values
*/
message.header('x-my-key', ['header value', 'another value'])
By default, the email headers are encoded and folded to meet the requirement of having plain ASCII messages with lines no longer than 78 bytes. However, if you want to bypass the encoding rules, you may set a header using the message.preparedHeader
method.
message.preparedHeader(
'x-unprocessed',
'a really long header or value with non-ascii characters 👮',
)
Defining List
headers
The message class includes helper methods to define complex headers like List-Unsubscribe or List-Help with ease. You can learn about the encoding rules for List
headers on the nodemailer website.
message.listHelp('admin@example.com?subject=help')
// List-Help: <mailto:admin@example.com?subject=help>
message.listUnsubscribe({
url: 'http://example.com',
comment: 'Comment'
})
// List-Unsubscribe: <http://example.com> (Comment)
/**
* Repeating header multiple times
*/
message.listSubscribe('admin@example.com?subject=subscribe')
message.listSubscribe({
url: 'http://example.com',
comment: 'Subscribe'
})
// List-Subscribe: <mailto:admin@example.com?subject=subscribe>
// List-Subscribe: <http://example.com> (Subscribe)
For all other arbitrary List
headers, you may use the addListHeader
method.
message.addListHeader('post', 'http://example.com/post')
// List-Post: <http://example.com/post>