Creating custom transports

Creating custom transports

AdonisJS Mail transports are built on top of Nodemailer transports; therefore, you must create/use a nodemailer transport before you can register it with the Mail package.

In this guide, we will wrap the nodemailer-postmark-transport to an AdonisJS Mail transport.

npm i nodemailer nodemailer-postmark-transport

As you can see in the following example, the heavy lifting of sending an email is done by the nodemailer. The AdonisJS transport acts as an adapter forwarding the message to nodemailer and normalizing its response to an instance of MailResponse.

import nodemailer from 'nodemailer'
import nodemailerTransport from 'nodemailer-postmark-transport'
import { MailResponse } from '@adonisjs/mail'
import type {
NodeMailerMessage,
MailTransportContract
} from '@adonisjs/mail/types'
/**
* Configuration accepted by the transport
*/
export type PostMarkConfig = {
apiKey: string
}
/**
* Transport implementation
*/
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> {
/**
* Create nodemailer transport
*/
const transporter = this.#createNodemailerTransport({
...this.#config,
...config,
})
/**
* Send email
*/
const response = await transporter.sendMail(message)
/**
* Normalize response to an instance of the "MailResponse" class
*/
return new MailResponse(response.messageId, response.envelope)
}
}

Creating the config factory function

To reference the above transport inside the config/mail.ts file, you must create a factory function that returns an instance of the transport.

You may write the following code within the same file as your transport's implementation.

import type {
NodeMailerMessage,
MailTransportContract,
MailManagerTransportFactory
} from '@adonisjs/mail/types'
export function postMarkTransport(
config: PostMarkConfig
): MailManagerTransportFactory {
return () => {
return new PostMarkTransport(config)
}
}

Using the transport

Finally, you can reference the transport inside your config file using the postMarkTransport helper.

import env from '#start/env'
import { defineConfig } from '@adonisjs/mail'
import { postMarkTransport } from 'my-custom-package'
const mailConfig = defineConfig({
mailers: {
postmark: postMarkTransport({
apiKey: env.get('POSTMARK_API_KEY'),
}),
},
})