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:
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
}),
],
};
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>) => ;
setMetadata(optionalSetMetadataFn: SetMetadataFn<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
(listener: EmailEventListener<T>, event: Type<Event>) => EmailEventHandler
filter
(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
(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
(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
(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
(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
(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
A function which allows OptionalAddressFields to be specified such as "cc" and "bcc".
setMetadata
A function which allows EmailMetadata to be specified for the email. This can be used to store arbitrary data about the email which can be used for tracking or other purposes.
It will be exposed in the EmailSendEvent as event.metadata
. Here's an example of usage:
- An OrderStateTransitionEvent occurs, and the EmailEventListener starts processing it.
- The EmailEventHandler attaches metadata to the email:
new EmailEventListener(EventType.ORDER_CONFIRMATION)
.on(OrderStateTransitionEvent)
.setMetadata(event => ({
type: EventType.ORDER_CONFIRMATION,
orderId: event.order.id,
})); - Then, the EmailPlugin tries to send the email and publishes EmailSendEvent, passing ctx, emailDetails, error or success, and this metadata.
- In another part of the server, we have an eventBus that subscribes to EmailSendEvent. We can use
metadata.type
andmetadata.orderId
to identify the related order. For example, we can indicate on the order that the email was successfully sent, or in case of an error, send a notification confirming the order in another available way.
setAttachments
(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
(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
(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
(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.