Context
Templating
The Templating provides powerful template rendering with automatic i18n integration using IdentityHub's existing i18next system.
Overview
The Templating is available in the GraphQL context and automatically integrates with:
- Eta Template Engine: Full template logic support (
if,for, includes, partials) - Template Namespace: Uses the
templatenamespace by default for i18n - Locale-Aware Formatting: Pre-injected
t(),formatDate(),formatNumber()functions
Basic Usage
Simple Template
export const sendWelcomeEmail = async (source, args, context) => {
const { userId } = args;
const user = await context.ldap.findUserById(userId);
const emailBody = context.templating.render(
`
<h1>Welcome <%= data.user.givenName %>!</h1>
<p>Your account: <%= data.user.uid %></p>
<% if (data.user.isFirstLogin) { %>
<p>This is your first login - please update your password.</p>
<% } %>
`,
{
data: { user },
},
);
return context.messaging.send({
transport: 'smtp',
to: user.mail,
subject: 'Welcome!',
html: emailBody,
});
};
Template with Internationalization
export const sendLocalizedWelcome = async (source, args, context) => {
const { userId } = args;
const user = await context.ldap.findUserById(userId);
// Automatically uses current request locale with 'templating' namespace
const emailBody = context.templating.render(
`
<h1><%= t('welcome.greeting', { name: data.user.givenName }) %></h1>
<p><%= t('welcome.account_info', { account: data.user.uid }) %></p>
<% if (data.user.isFirstLogin) { %>
<p><%= t('welcome.first_login_notice') %></p>
<% } %>
<p><%= formatDate(new Date()) %></p>
`,
{
data: { user },
},
);
return context.messaging.send({
transport: 'smtp',
to: user.mail,
subject: t('welcome.email_subject', { ns: 'templating' }),
html: emailBody,
});
};
Translation Files
The Templating uses the templating namespace by default. Create translation files:
English Translations
# config/locales/en/templating.yaml
welcome:
greeting: 'Welcome, {{name}}!'
message: 'Thank you for joining our platform.'
email_subject: 'Welcome to our platform'
first_login_notice: 'This is your first login - please update your password.'
notification:
title: 'Important Notice'
greeting: 'Dear {{name}},'
German Translations
# config/locales/de/templating.yaml
welcome:
greeting: 'Willkommen, {{name}}!'
message: 'Vielen Dank, dass Sie sich unserer Plattform angeschlossen haben.'
email_subject: 'Willkommen auf unserer Plattform'
first_login_notice: 'Dies ist Ihr erster Login - bitte aktualisieren Sie Ihr Passwort.'
notification:
title: 'Wichtiger Hinweis'
greeting: 'Liebe/r {{name}},'
Advanced Features
Using Partials
export const sendFormattedNotification = async (source, args, context) => {
const { userId, message } = args;
const user = await context.ldap.findUserById(userId);
const emailBody = context.templating.render(
`
<%~ include('@header', { title: t('notification.title') }) %>
<div>
<p><%= t('notification.greeting', { name: data.user.givenName }) %></p>
<div><%~ data.unsafeContent %></div>
</div>
<%~ include('@footer', data) %>
`,
{
data: { user, unsafeContent: message },
partials: {
'@header': '<header><h1><%= data.title %></h1></header>',
'@footer': '<footer><p>Company Footer</p></footer>',
},
},
);
return context.messaging.send({
transport: 'smtp',
to: user.mail,
subject: 'Notification',
html: emailBody,
});
};
Named Templates
export const sendWelcomeFromTemplate = async (source, args, context) => {
const { userId } = args;
const user = await context.ldap.findUserById(userId);
const welcomeTemplate = '<h1>Welcome <%= data.user.name %>';
// Render the '@welcome-email' template from partials
const emailBody = context.templating.render('@welcome-email', {
data: { user },
partials: {
'@welcome-email': welcomeTemplate,
},
});
return context.messaging.send({
transport: 'smtp',
to: user.mail,
subject: 'Welcome!',
html: emailBody,
});
};
Using Other Namespaces
// Uses 'templating' namespace (default)
t('welcome.greeting', { name: 'John' });
// Uses 'fieldset' namespace explicitly
t('username.label', { ns: 'fieldset' });
// Uses 'common' namespace explicitly
t('save', { ns: 'common' });
Global Functions
The Templating automatically injects these functions:
Translation Function t()
- Default namespace:
templating - Automatic locale detection: Uses current request locale
- Fallback support: Proper locale fallback chains
Date Formatting formatDate()
- Locale-aware: Uses current request locale
- Intl.DateTimeFormat: Full internationalization support
Number Formatting formatNumber()
- Locale-aware: Uses current request locale
- Intl.NumberFormat: Currency, percentage, etc.
Configuration
Configure the templating service in your config file:
# config/customer.toml
[templating]
debug = false # Disable debug formatting in production
varName = "data" # Default data object name
tags = ["<%", "%>"] # Template delimiters
API Reference
render(template, context)
Renders a template synchronously.
Parameters:
template(string): Template content or named template (@template-name)context(object): Template context with data, partials, etc.
Returns: Rendered string
renderAsync(template, context)
Renders a template asynchronously.
Parameters:
template(string): Template content or named template (@template-name)context(object): Template context with data, partials, etc.
Returns: Promise
Template Context Properties
data: Dynamic data passed to templatespartials: Reusable template components@globals: Custom functions (automatically includest,formatDate,formatNumber)tags: Template delimiters (optional)varName: Data object name (optional)debug: Debug mode (optional)
Best Practices
- Use the templating namespace: Keep template translations organized
- Leverage partials: Create reusable email components
- Test with multiple locales: Ensure proper i18n support
- Use named templates: For complex, reusable templates
- Separate concerns: Keep business logic in resolvers, presentation in templates