Configuring message

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.

Create templates
node ace make:view emails/verify_email_html
node ace make:view emails/verify_email_text
Use them for defining contents
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>