@semantq/mail
v1.0.2
Published
Lightweight email service for semantqQL projects
Maintainers
Readme
@semantq/mail
Lightweight, Auto-Configuring Email Service for semantq full stack (semantqQL) Projects
Effortlessly send emails from your semantqQL applications with zero boilerplate. Auto-configures from your existing server.config.js, providing both simple and advanced emailing sending APIs.
Quick Start
Installation
npm install @semantq/mailConfiguration
Add email configuration to your existing semantqQL/server.config.js:
export default {
// ... your existing database, server config
email: {
driver: 'resend', // 'resend' | 'sendgrid' | 'smtp' | 'log'
resend_api_key: process.env.RESEND_API_KEY,
sendgrid_api_key: process.env.SENDGRID_API_KEY,
email_from: process.env.EMAIL_FROM || '[email protected]',
email_from_name: process.env.EMAIL_FROM_NAME || 'Our Team'
},
brand: {
name: process.env.BRAND_NAME || 'My App',
support_email: process.env.BRAND_SUPPORT_EMAIL || '[email protected]'
}
};Complete Setup Guide
1. Generate Email Resources with CLI
Create a complete email service with one command:
# Generate Welcome email service
semantq make:mail Welcome
# Creates these files:
# - semantqQL/services/WelcomeService.js
# - semantqQL/mail/templates/welcome/welcome.js
# - semantqQL/mail/test-welcomeMail.jsQuick Start: Where to Focus
Focus on this file: semantqQL/mail/test-welcomeMail.js
This is your starting template. Copy the email payload from here and customize it for your actual use case. The file contains complete examples for both basic and full-featured emails.
Basic Email Setup
// In your actual application (anywhere in your codebase)
import welcomeMail from './services/WelcomeService.js'; // Adjust path as needed
const emailData = {
recipients: ['[email protected]','[email protected]'], // Required: recipient emails
subject: 'Basic Welcome Test', // Required: email subject (customise)
template: 'welcome/welcome', // Required: template path (folder/filename) (leave as is)
recipient: { // Optional: recipient details for personalization
email: '[email protected]',
name: 'User Name' // Shows as "Hi User Name,"
},
text: 'This is your email content.' // Content: Use text, html, OR body
};
await welcomeMail.sendWelcome(emailData);Content Options (Use ONE of these):
// 1. Plain text
text: 'Simple text message'
// 2. HTML content
html: '<h2>Styled HTML</h2><p>With <strong>formatting</strong></p>'
// 3. Simple body (auto-detects HTML/text)
body: 'Can be plain text or HTML'Full Featured Setup
const fullOptions = {
recipients: ['[email protected]', '[email protected]'],
subject: 'Complete Order Details',
template: 'orders/confirmation',
recipient: {
email: '[email protected]',
name: 'John Smith'
},
// Content options (choose one):
text: 'Plain text content here',
// OR html: '<p>HTML content here</p>',
// OR body: 'Simple content here',
// Optional features:
cc: ['[email protected]'],
bcc: ['[email protected]'],
replyTo: '[email protected]',
from: '[email protected]',
fromName: 'Your Company',
attachments: [
{
filename: 'invoice.pdf',
content: 'base64String',
contentType: 'application/pdf'
}
],
templateData: { // Optional template variables
themeColor: '#4CAF50', // Changes button colors
orderNumber: '12345',
orderDate: '2024-12-10'
}
};Important: Import Paths
The generated test script assumes you're running from semantqQL directory. If you're calling emails from elsewhere:
// If running from project root:
import welcomeMail from './semantqQL/services/WelcomeService.js';
// If running from other directories:
import welcomeMail from '../services/WelcomeService.js';
// If your service is in a different location:
import welcomeMail from '../../path/to/services/WelcomeService.js';Pro Tip: Test your import path by running the generated test script first:
cd semantqQL
node mail/test-welcomeMail.jsOnce the test works, copy the payload structure to your actual application code.
2. Generated Files Structure
A. Service File (semantqQL/services/WelcomeService.js)
import { MailService } from '@semantq/mail';
class WelcomeService {
constructor() {
this.mail = new MailService();
}
async sendWelcome(user, options = {}) {
return this.mail.send({
to: user.email,
subject: `Welcome to ${this.mail.config.brand?.name}!`,
template: 'welcome',
templateData: { name: user.name, ...options }
});
}
}
export default new WelcomeService();B. Content Template (semantqQL/mail/templates/welcome/welcome.js)
// Welcome Email Template
// This template returns EMPTY content since content comes from payload
export default {
subject: ({ data, brand }) => {
return data.subject || `Message from ${brand?.name || 'Our Service'}`;
},
html: ({ data, recipient, brand }) => {
// Content comes from payload (text, html, or body fields)
return '';
},
text: ({ data, recipient, brand }) => {
return '';
}
};C. Test Script (semantqQL/mail/test-welcomeMail.js)
// test-welcomeMail.js
// Minimal test script for Welcome email service
// Run: cd semantqQL && node mail/test-welcomeMail.js
import welcomeMail from '../services/WelcomeService.js';
async function testWelcomeMailEmail() {
try {
// 1. Initialize service
await welcomeMail.init();
console.log('[STATUS] Service ready | Driver: ' + welcomeMail.mail?.getDriverName());
// ============================================
// CONFIGURATION: EDIT EMAIL ADDRESS BELOW
// ============================================
// BASIC EXAMPLE - Simple text email
const emailData = {
recipients: ['[email protected]'], // <--- Replace with your email
subject: 'Basic Welcome Test',
template: 'welcome/welcome',
recipient: {
email: '[email protected]',
name: 'Test User'
},
text: 'This is a test email from the Welcome service.'
};
// ============================================
// CORE FUNCTION: DO NOT ALTER
// ============================================
console.log('\n[SENDING] Dispatching email...');
const result = await welcomeMail.sendWelcome(emailData);
if (result.success) {
console.log('[RESULT] Basic test: PASS');
} else {
console.log(`[RESULT] Basic test: FAIL - ${result.message || 'Unknown Error'}`);
}
return result.success;
} catch (error) {
console.error('\n[ERROR] Setup/Runtime Failure:', error.message);
return false;
}
}
// Run test
testWelcomeMailEmail()
.then(success => {
process.exit(success ? 0 : 1);
})
.catch(() => {
process.exit(1);
});3. Customizing Your Email Service
A. Update the Service Method
Edit services/WelcomeService.js to add custom logic:
// Add custom methods
class WelcomeService {
constructor() {
this.mail = new MailService();
}
async sendWelcome(user) {
return this.mail.send({
recipients: [user.email],
subject: `Welcome to ${this.mail.config.brand?.name}!`,
template: 'welcome/welcome',
recipient: {
email: user.email,
name: user.fullName
},
text: `Welcome ${user.fullName}! We're excited to have you on board.`
});
}
async sendPasswordReset(user, resetUrl) {
return this.mail.send({
recipients: [user.email],
subject: 'Reset Your Password',
template: 'auth/password-reset',
recipient: {
email: user.email,
name: user.firstName
},
html: `
<h2>Password Reset</h2>
<p>Click the link below to reset your password:</p>
<a href="${resetUrl}">Reset Password</a>
`
});
}
}B. Customize Content in Payload
Content comes from text, html, or body fields in your payload:
// Plain text email
const emailData = {
recipients: ['[email protected]'],
subject: 'Basic Email',
template: 'welcome/welcome',
recipient: { email: '[email protected]', name: 'John' },
text: 'This is a plain text message.'
};
// HTML email
const emailData = {
recipients: ['[email protected]'],
subject: 'Styled Email',
template: 'welcome/welcome',
recipient: { email: '[email protected]', name: 'John' },
html: `
<h2>Welcome John!</h2>
<p>This is a custom HTML email with styling.</p>
<a href="https://example.com" style="color: #007bff;">Visit Website</a>
`
};
// Using templateData for additional variables
const emailData = {
recipients: ['[email protected]'],
subject: 'Order Confirmation',
template: 'orders/confirmation',
recipient: { email: '[email protected]', name: 'John' },
text: 'Your order #123 has been confirmed.',
templateData: {
orderNumber: '123',
orderDate: '2024-12-10',
themeColor: '#4CAF50' // Customizes button colors
}
};4. Email Payload Structure
Basic Email Payload
{
// REQUIRED fields
recipients: ['[email protected]'],
subject: 'Email Subject',
template: 'template-name',
// OPTIONAL content (use one)
text: 'Plain text content',
html: '<p>HTML content</p>',
body: 'Simple text or HTML',
// OPTIONAL recipient info
recipient: {
email: '[email protected]',
name: 'User Name'
},
// OPTIONAL template data
templateData: {
themeColor: '#007bff',
customField: 'Any data'
},
// OPTIONAL email routing
cc: ['[email protected]'],
bcc: ['[email protected]'],
replyTo: '[email protected]',
from: '[email protected]',
fromName: 'Your Brand',
// OPTIONAL attachments
attachments: [
{
filename: 'document.pdf',
content: 'base64String',
contentType: 'application/pdf'
}
]
}Full Featured Example
const fullOptions = {
// REQUIRED: Core email fields
recipients: ['[email protected]', '[email protected]'],
subject: 'Full Featured Email',
template: 'template-name',
// OPTIONAL: Content (use one or more)
text: 'Plain text version of the email',
html: '<p>HTML <strong>version</strong> of the email</p>',
// OPTIONAL: Recipient info
recipient: {
email: '[email protected]',
name: 'Customer Name'
},
// OPTIONAL: Email routing
cc: ['[email protected]'],
bcc: ['[email protected]'],
replyTo: '[email protected]',
from: '[email protected]',
fromName: 'Your Team',
// OPTIONAL: Attachments
attachments: [
{
filename: 'test.txt',
content: 'This is a test attachment',
contentType: 'text/plain'
}
],
// OPTIONAL: Additional template data
templateData: {
features: ['Feature 1', 'Feature 2'],
customField: 'Any data you need',
themeColor: '#667eea'
}
};5. Running Your Email Tests
# Run the generated test script
cd semantqQL
node mail/test-welcomeMail.js
# Output:
# [STATUS] Service ready | Driver: resend
# [SENDING] Dispatching email...
# [RESULT] Basic test: PASS6. Customizing Email Templates
Option A: Custom Content Template
Create a template that generates content:
// semantqQL/mail/templates/welcome/custom-welcome.js
export default {
subject: ({ data, brand }) => {
return data.subject || `Welcome to ${brand?.name}`;
},
html: ({ data, recipient, brand }) => {
const recipientName = recipient?.name || 'there';
return `
<div>
<h2>Welcome ${recipientName}!</h2>
<p>Thank you for joining ${brand?.name}.</p>
${data.customMessage ? `<p>${data.customMessage}</p>` : ''}
</div>
`;
},
text: ({ data, recipient, brand }) => {
const recipientName = recipient?.name || 'there';
return `Welcome ${recipientName}!\n\nThank you for joining ${brand?.name}.`;
}
};
// Usage
const emailData = {
recipients: ['[email protected]'],
subject: 'Welcome',
template: 'welcome/custom-welcome',
recipient: { email: '[email protected]', name: 'John' },
templateData: {
customMessage: 'Check out our getting started guide!'
}
};Option B: Use BaseLayout for Consistent Design
All emails use BaseLayout which provides:
- Clean, non-boxed design
- Responsive layout
- Brand header (optional)
- Footer with support info
- Automatic text version generation
Customize via templateData.themeColor:
templateData: {
themeColor: '#4CAF50' // Changes button and link colors
}API Reference
MailService Class
import { MailService } from '@semantq/mail';
const mail = new MailService();
// Send email
await mail.send({
recipients: ['[email protected]'],
subject: 'Test Email',
template: 'template-name',
text: 'Email content',
recipient: { email: '[email protected]', name: 'User Name' }
});
// Check driver
console.log(mail.getDriverName()); // 'resend', 'sendgrid', 'smtp', or 'log'
// Check if using real driver
console.log(mail.isRealDriver()); // true for resend/sendgrid/smtp, false for logContent Sources Priority
When sending emails, content is determined in this order:
htmlfield in payloadtextfield in payloadbodyfield in payload- Template-generated content (if template provides it)
- Fallback: "Your message here"
Configuration Options
Email Drivers
// Resend (Recommended)
email: {
driver: 'resend',
resend_api_key: 're_xxxxxxxxxx',
email_from: '[email protected]',
email_from_name: 'Your Brand'
}
// SendGrid
email: {
driver: 'sendgrid',
sendgrid_api_key: 'SG.xxxxxxxxxx'
}
// SMTP
email: {
driver: 'smtp',
smtp_host: 'smtp.gmail.com',
smtp_port: 587,
smtp_user: '[email protected]',
smtp_pass: 'password'
}
// Log (Development)
email: {
driver: 'log'
}Brand Configuration
brand: {
name: 'Your App Name',
support_email: '[email protected]'
}Troubleshooting
Emails not sending
- Check
server.config.jshas correct email configuration - Verify API keys are set (for Resend/SendGrid)
- Check
NODE_ENVis not forcing log driver - Run test script to verify connectivity
Recipient name not showing
Ensure recipient field includes both email and name:
recipient: {
email: '[email protected]',
name: 'User Name' // Required for personalized greeting
}Content not appearing
Content must come from text, html, or body fields:
// WRONG - templateData.message won't show
templateData: { message: 'Hello' }
// CORRECT - use text, html, or body
text: 'Hello' // or
html: '<p>Hello</p>' // or
body: 'Hello'Template not found
Check template path matches structure:
// Template file: mail/templates/orders/confirmation.js
template: 'orders/confirmation' // folder/filename (no .js)Best Practices
- Always include recipient name for personalized emails
- Use test scripts to verify email sending before production
- Keep templates simple - content comes from payload
- Use environment variables for API keys
- Test with log driver during development
- Customize via templateData.themeColor for brand consistency
License
MIT © semantq Team
