Skip to main content

EmailEventHandler

EmailEventHandler

The EmailEventHandler defines how the EmailPlugin will respond to a given event.

A handler is created by creating a new EmailEventListener and calling the .on() method to specify which event to respond to.

Example

const confirmationHandler = new EmailEventListener('order-confirmation')
.on(OrderStateTransitionEvent)
.filter(event => event.toState === 'PaymentSettled')
.setRecipient(event => event.order.customer.emailAddress)
.setFrom('{{ fromAddress }}')
.setSubject(`Order confirmation for #{{ order.code }}`)
.setTemplateVars(event => ({ order: event.order }));

This example creates a handler which listens for the OrderStateTransitionEvent and if the Order has transitioned to the 'PaymentSettled' state, it will generate and send an email.

The string argument passed into the EmailEventListener constructor is used to identify the handler, and also to locate the directory of the email template files. So in the example above, there should be a directory <app root>/static/email/templates/order-confirmation which contains a Handlebars template named body.hbs.

Handling other languages

By default, the handler will respond to all events on all channels and use the same subject ("Order confirmation for #12345" above) and body template. Where the server is intended to support multiple languages, the .addTemplate() method may be used to define the subject and body template for specific language and channel combinations.

The language is determined by looking at the languageCode property of the event's ctx (RequestContext) object.

Example

const extendedConfirmationHandler = confirmationHandler
.addTemplate({
channelCode: 'default',
languageCode: LanguageCode.de,
templateFile: 'body.de.hbs',
subject: 'Bestellbestätigung für #{{ order.code }}',
})

Defining a custom handler

Let's say you have a plugin which defines a new event type, QuoteRequestedEvent. In your plugin you have defined a mutation which is executed when the customer requests a quote in your storefront, and in your resolver, you use the EventBus to publish a new QuoteRequestedEvent.

You now want to email the customer with their quote. Here are the steps you would take to set this up:

1. Create a new handler

import { EmailEventListener } from `@vendure/email-plugin`;
import { QuoteRequestedEvent } from `./events`;

const quoteRequestedHandler = new EmailEventListener('quote-requested')
.on(QuoteRequestedEvent)
.setRecipient(event => event.customer.emailAddress)
.setSubject(`Here's the quote you requested`)
.setFrom('{{ fromAddress }}')
.setTemplateVars(event => ({ details: event.details }));

2. Create the email template

Next you need to make sure there is a template defined at <app root>/static/email/templates/quote-requested/body.hbs. The path segment quote-requested must match the string passed to the EmailEventListener constructor.

The template would look something like this:

{{> header title="Here's the quote you requested" }}

<mj-section background-color="#fafafa">
<mj-column>
<mj-text color="#525252">
Thank you for your interest in our products! Here's the details
of the quote you recently requested:
</mj-text>

<--! your custom email layout goes here -->
</mj-column>
</mj-section>


{{> footer }}

You can find pre-made templates on the MJML website.

3. Register the handler

Finally, you need to register the handler with the EmailPlugin:

import { defaultEmailHandlers, EmailPlugin } from '@vendure/email-plugin';
import { quoteRequestedHandler } from './plugins/quote-plugin';

const config: VendureConfig = {
// Add an instance of the plugin to the plugins array
plugins: [
EmailPlugin.init({
handler: [...defaultEmailHandlers, quoteRequestedHandler],
// ... etc
}),
],
};
Signature
class EmailEventHandler<T extends string = string, Event extends EventWithContext = EventWithContext> {
constructor(listener: EmailEventListener<T>, event: Type<Event>)
filter(filterFn: (event: Event) => boolean) => EmailEventHandler<T, Event>;
setRecipient(setRecipientFn: (event: Event) => string) => EmailEventHandler<T, Event>;
setLanguageCode(setLanguageCodeFn: (event: Event) => LanguageCode | undefined) => EmailEventHandler<T, Event>;
setTemplateVars(templateVarsFn: SetTemplateVarsFn<Event>) => EmailEventHandler<T, Event>;
setSubject(defaultSubject: string | SetSubjectFn<Event>) => EmailEventHandler<T, Event>;
setFrom(from: string) => EmailEventHandler<T, Event>;
setOptionalAddressFields(optionalAddressFieldsFn: SetOptionalAddressFieldsFn<Event>) => ;
setAttachments(setAttachmentsFn: SetAttachmentsFn<Event>) => ;
addTemplate(config: EmailTemplateConfig) => EmailEventHandler<T, Event>;
loadData(loadDataFn: LoadDataFn<Event, R>) => EmailEventHandlerWithAsyncData<R, T, Event, EventWithAsyncData<Event, R>>;
setMockEvent(event: Omit<Event, 'ctx' | 'data'>) => EmailEventHandler<T, Event>;
}

constructor

method
(listener: EmailEventListener<T>, event: Type<Event>) => EmailEventHandler

filter

method
(filterFn: (event: Event) => boolean) => EmailEventHandler<T, Event>

Defines a predicate function which is used to determine whether the event will trigger an email. Multiple filter functions may be defined.

setRecipient

method
(setRecipientFn: (event: Event) => string) => EmailEventHandler<T, Event>

A function which defines how the recipient email address should be extracted from the incoming event.

The recipient can be a plain email address: 'foobar@example.com' Or with a formatted name (includes unicode support): 'Ноде Майлер <foobar@example.com>' Or a comma-separated list of addresses: 'foobar@example.com, "Ноде Майлер" <bar@example.com>'

setLanguageCode

method
v1.8.0
(setLanguageCodeFn: (event: Event) => LanguageCode | undefined) => EmailEventHandler<T, Event>

A function which allows to override the language of the email. If not defined, the language from the context will be used.

setTemplateVars

method
(templateVarsFn: SetTemplateVarsFn<Event>) => EmailEventHandler<T, Event>

A function which returns an object hash of variables which will be made available to the Handlebars template and subject line for interpolation.

setSubject

method
(defaultSubject: string | SetSubjectFn<Event>) => EmailEventHandler<T, Event>

Sets the default subject of the email. The subject string may use Handlebars variables defined by the setTemplateVars() method.

setFrom

method
(from: string) => EmailEventHandler<T, Event>

Sets the default from field of the email. The from string may use Handlebars variables defined by the setTemplateVars() method.

setOptionalAddressFields

method
v1.1.0
(optionalAddressFieldsFn: SetOptionalAddressFieldsFn<Event>) =>

A function which allows OptionalAddressFields to be specified such as "cc" and "bcc".

setAttachments

method
(setAttachmentsFn: SetAttachmentsFn<Event>) =>

Defines one or more files to be attached to the email. An attachment can be specified as either a path (to a file or URL) or as content which can be a string, Buffer or Stream.

Note: When using the content to pass a Buffer or Stream, the raw data will get serialized into the job queue. For this reason the total size of all attachments passed as content should kept to less than ~50k. If the attachments are greater than that limit, a warning will be logged and errors may result if using the DefaultJobQueuePlugin with certain DBs such as MySQL/MariaDB.

Example

const testAttachmentHandler = new EmailEventListener('activate-voucher')
.on(ActivateVoucherEvent)
// ... omitted some steps for brevity
.setAttachments(async (event) => {
const { imageUrl, voucherCode } = await getVoucherDataForUser(event.user.id);
return [
{
filename: `voucher-${voucherCode}.jpg`,
path: imageUrl,
},
];
});

addTemplate

method
(config: EmailTemplateConfig) => EmailEventHandler<T, Event>

Add configuration for another template other than the default "body.hbs". Use this method to define specific templates for channels or languageCodes other than the default.

loadData

method
(loadDataFn: LoadDataFn<Event, R>) => EmailEventHandlerWithAsyncData<R, T, Event, EventWithAsyncData<Event, R>>

Allows data to be loaded asynchronously which can then be used as template variables. The loadDataFn has access to the event, the TypeORM Connection object, and an inject() function which can be used to inject any of the providers exported by the PluginCommonModule. The return value of the loadDataFn will be added to the event as the data property.

Example

new EmailEventListener('order-confirmation')
.on(OrderStateTransitionEvent)
.filter(event => event.toState === 'PaymentSettled' && !!event.order.customer)
.loadData(({ event, injector }) => {
const orderService = injector.get(OrderService);
return orderService.getOrderPayments(event.order.id);
})
.setTemplateVars(event => ({
order: event.order,
payments: event.data,
}))
// ...

setMockEvent

method
(event: Omit<Event, 'ctx' | 'data'>) => EmailEventHandler<T, Event>

Optionally define a mock Event which is used by the dev mode mailbox app for generating mock emails from this handler, which is useful when developing the email templates.