render-transactional-emailslisted
Install: claude install-skill kennguyen887/agent-foundation
# Render transactional emails (and documents)
Producing transactional content — order receipts, OTPs, invites, notices, PDFs — from templates rather
than hand-built strings. Examples NestJS/TS (an `email-templates`/EJS + CSS-inliner stack), neutral
domain. principle → **▸ Example** → **▸ Other stacks**. Sending lives behind the notification facade in
`integrate-external-services` §1; this skill is how the *content* is produced.
## Core principle
**Never concatenate email HTML in a handler.** Render from a **versioned template + a typed data
context** through one shared service — localized and CSS-inlined — and return a `{ subject, html, text }`
the transport sends. Templates hold markup; code holds logic.
## 1. One render service over a template engine
Wrap the template lib (EJS / Handlebars / …) + a CSS inliner (so styles survive email clients) in a
single `render(templateName, context)`; resolve the file by **name + locale convention**; configure the
template root via DI, not hard-coded paths.
```ts
@Injectable() export class TemplateService {
private email = new Email({
views: { root, options: { extension: 'ejs' } },
getPath: (file, template, ctx) => path.join(dir, template, `${file}.${ctx.language ?? 'en'}`), // <template>/<file>.<lang>
juiceResources: { /* inline CSS */ },
});
render(name: string, ctx: { language?: string; data: unknown }) {
return this.email.renderAll(name, ctx.data); // → { subject, html, text }
}
}
```
## 2. Templates are