diff --git a/src/sendEmail.ts b/src/sendEmail.ts index 9768aad..2fddda6 100644 --- a/src/sendEmail.ts +++ b/src/sendEmail.ts @@ -1,5 +1,5 @@ import { Payload } from 'payload' -import { getMailing, renderTemplate, parseAndValidateEmails, sanitizeFromName } from './utils/helpers.js' +import { getMailing, renderTemplateWithId, parseAndValidateEmails, sanitizeFromName } from './utils/helpers.js' import { BaseEmailDocument } from './types/index.js' import { processJobById } from './utils/emailProcessor.js' import { createContextLogger } from './utils/logger.js' @@ -50,23 +50,8 @@ export const sendEmail = async = { ...options.data } as Partial if (options.template) { - // Find the template document to get its ID for the relationship field - const templatesCollection = mailingConfig.collections.templates || 'email-templates' - const { docs: templateDocs } = await payload.find({ - collection: templatesCollection as any, - where: { - slug: { - equals: options.template.slug, - }, - }, - limit: 1, - }) - - if (!templateDocs || templateDocs.length === 0) { - throw new Error(`Template not found: ${options.template.slug}`) - } - - const { html, text, subject } = await renderTemplate( + // Look up and render the template in a single operation to avoid duplicate lookups + const { html, text, subject, templateId } = await renderTemplateWithId( payload, options.template.slug, options.template.variables || {} @@ -74,7 +59,7 @@ export const sendEmail = async { + this.ensureInitialized() + const emailContent = await this.renderEmailTemplate(template, variables) const subject = await this.renderTemplateString(template.subject || '', variables) diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 6e7de20..e3662fa 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -130,6 +130,53 @@ export const renderTemplate = async (payload: Payload, templateSlug: string, var return mailing.service.renderTemplate(templateSlug, variables) } +/** + * Render a template and return both rendered content and template ID + * This is used by sendEmail to avoid duplicate template lookups + * @internal + */ +export const renderTemplateWithId = async ( + payload: Payload, + templateSlug: string, + variables: TemplateVariables +): Promise<{ html: string; text: string; subject: string; templateId: PayloadID }> => { + const mailing = getMailing(payload) + const templatesCollection = mailing.config.collections?.templates || 'email-templates' + + // Runtime validation: Ensure the collection exists in Payload + if (!payload.collections[templatesCollection]) { + throw new Error( + `Templates collection '${templatesCollection}' not found. ` + + `Available collections: ${Object.keys(payload.collections).join(', ')}` + ) + } + + // Look up the template document once + const { docs: templateDocs } = await payload.find({ + collection: templatesCollection as any, + where: { + slug: { + equals: templateSlug, + }, + }, + limit: 1, + }) + + if (!templateDocs || templateDocs.length === 0) { + throw new Error(`Template not found: ${templateSlug}`) + } + + const templateDoc = templateDocs[0] + + // Render using the document directly to avoid duplicate lookup + const rendered = await mailing.service.renderTemplateDocument(templateDoc, variables) + + return { + ...rendered, + templateId: templateDoc.id, + } +} + export const processEmails = async (payload: Payload): Promise => { const mailing = getMailing(payload) return mailing.service.processEmails()