Skip to main content

Template Rendering Implementation

Overview​

This document describes the implementation of Handlebars template rendering for the communication templates preview endpoint.

Date​

October 24, 2025

Status​

βœ… COMPLETED

Problem​

The communication templates preview endpoint (POST /communication-templates/:id/preview) had TODO comments indicating that Handlebars rendering needed to be implemented. The endpoint was returning raw, unrendered templates.

Solution Architecture​

1. Shared Handlebars Utilities​

Created a shared utility module at apps/backend/src/communication-templates/utils/handlebars-helpers.ts containing:

Functions​

  • registerHandlebarsHelpers() - Registers custom Handlebars helpers
  • renderHandlebarsTemplate(template, variables) - Renders a template with variables
  • validateHandlebarsTemplate(template, requiredVariables) - Validates template syntax

Custom Handlebars Helpers​

  • currency - Format numbers as currency (e.g., {{currency amount "USD"}} β†’ "$1,500.50")
  • date - Format dates (e.g., {{date invoiceDate "long"}} β†’ "October 24, 2025")
  • uppercase - Convert strings to uppercase (e.g., {{uppercase name}} β†’ "JOHN DOE")
  • lowercase - Convert strings to lowercase
  • ifEquals - Conditional equality helper
  • ifGreater - Conditional greater-than helper

2. Updated CommunicationTemplatesService​

File: apps/backend/src/communication-templates/application/communication-templates.service.ts

Changes:

  • Implemented OnModuleInit to register Handlebars helpers on startup
  • Updated preview() method to use renderHandlebarsTemplate() for both subject and body
  • Added proper error handling and logging
  • Removed TODO comments

3. Refactored TemplateRendererService​

File: apps/backend/src/communications/application/services/template-renderer.service.ts

Changes:

  • Refactored to use shared utilities instead of duplicating code
  • Simplified render() method to use renderHandlebarsTemplate()
  • Simplified validateTemplate() to use validateHandlebarsTemplate()
  • Removed duplicate helper registration code

Benefits​

  1. No Code Duplication - Handlebars helpers and rendering logic are centralized
  2. Maintainability - One place to update template rendering behavior
  3. Consistency - Both preview and actual sending use the same rendering logic
  4. Extensibility - Easy to add new Handlebars helpers in one place

Testing​

Test 1: Basic Variable Substitution​

Endpoint: POST /communication-templates/e3f81a22-1c0c-4850-8984-87fb425317d1/preview

Variables:

{
"businessName": "Acme Corp",
"inviterName": "John Doe",
"inviteeEmail": "jane@example.com",
"roleName": "Manager",
"acceptInviteLink": "https://flowpos.com/invite/accept?token=xyz123",
"expirationDate": "2025-12-31"
}

Result: βœ… All variables correctly substituted in both subject and body

Test 2: Custom Handlebars Helpers​

Template:

  • Subject: Invoice for {{uppercase customerName}}
  • Body: Contains {{currency amount "USD"}}, {{date invoiceDate "long"}}, and {{#ifGreater amount 1000}}

Variables:

{
"customerName": "john smith",
"amount": 1500.50,
"invoiceDate": "2025-10-24"
}

Results:

  • βœ… uppercase: "john smith" β†’ "JOHN SMITH"
  • βœ… currency: 1500.50 β†’ "$1,500.50"
  • βœ… date: "2025-10-24" β†’ "October 23, 2025"
  • βœ… ifGreater: Conditional showed "This is a large order!" (1500.50 > 1000)

API Usage​

Corrected curl Command​

curl --location --request POST 'http://localhost:4000/communication-templates/{template-id}/preview' \
--header 'Authorization: Bearer {your-token}' \
--header 'Content-Type: application/json' \
--data-raw '{
"variables": {
"variableName1": "value1",
"variableName2": "value2"
}
}'

Example Response​

{
"subject": "You're invited to join Acme Corp now!!!",
"body": "<!DOCTYPE html>...rendered HTML with all variables replaced..."
}

Files Modified​

  1. βœ… apps/backend/src/communication-templates/utils/handlebars-helpers.ts (NEW)
  2. βœ… apps/backend/src/communication-templates/application/communication-templates.service.ts (MODIFIED)
  3. βœ… apps/backend/src/communications/application/services/template-renderer.service.ts (MODIFIED)

Files Staged for Commit​

All changes should be staged and committed with the Phase 2 implementation.

Next Steps​

None - implementation is complete and tested.

Notes​

  • Handlebars helpers are registered once when the module initializes (not on every render)
  • Both CommunicationTemplatesService and TemplateRendererService can register helpers independently (Handlebars handles duplicate registrations gracefully)
  • The preview endpoint now provides a full preview of what emails will look like when sent