@zola_do/docx
v0.2.9
Published
DOCX template processing for NestJS
Maintainers
Readme
@zola_do/docx
DOCX template processing for NestJS using docx-templates.
Overview
@zola_do/docx provides:
- Template Population — Fill DOCX templates with data
- Placeholder Validation — Check for required placeholders
- Handlebars Syntax — Supports loops, conditionals, images
- Buffer Output — Returns generated DOCX as Buffer
Installation
# Install individually
npm install @zola_do/docx
# Or via meta package
npm install @zola_do/nestjs-sharedDependencies
npm install docx-templatesQuick Start
1. Create a Template
Create a Word document with placeholders:
Dear {{name}},
Thank you for your order #{{orderNumber}}.
Order Details:
{{#each items}}
- {{this.name}}: ${{this.price}}
{{/each}}
Total: ${{total}}
{{#if isPremium}}
As a premium member, you get 10% off your next order!
{{/if}}
Best regards,
{{companyName}}2. Register Module
import { Module } from '@nestjs/common';
import { DocxModule } from '@zola_do/docx';
@Module({
imports: [DocxModule],
})
export class AppModule {}3. Generate Document
import { Injectable } from '@nestjs/common';
import { DocxService } from '@zola_do/docx';
@Injectable()
export class ReportService {
constructor(private readonly docxService: DocxService) {}
async generateInvoice(order: Order) {
const templateBuffer = await this.loadTemplate('invoice.docx');
const result = await this.docxService.generateDocx(templateBuffer, {
name: order.customerName,
orderNumber: order.number,
items: order.items,
total: order.total,
isPremium: order.customer.isPremium,
companyName: 'My Company',
});
return result; // Buffer of filled DOCX
}
}Template Syntax
The package uses docx-templates for template rendering.
Basic Placeholders
{{variableName}}
{{nested.property}}
{{array.index}}Conditionals
{{#if condition}}
Content when true
{{else}}
Content when false
{{/if}}
{{#unless condition}}
Content when false
{{/unless}}Loops
{{#each items}}
{{this.name}}
- ${{this.price}}
{{/each}}
{{#each orders}}
Order #{{this.number}}:
{{#each this.items}}
-
{{this.name}}
{{/each}}
{{/each}}Nested Data
{{customer.name}}
{{customer.address.city}}
{{customer.address.zip}}Helpers
{{uppercase name}}
{{lowercase email}}
{{formatDate date 'MMMM YYYY'}}
{{currency amount}}Images
{{#image filename width height}}
logo.png
{{/image}}
{{#imageUrl url width height}}
https://example.com/logo.png
{{/imageUrl}}Advanced Templates
Table with Rows
| Product | Quantity | Price | |---------|----------|-------|
{{#each items}}
|
{{this.name}}
|
{{this.quantity}}
| ${{this.price}}
|
{{/each}}
| **Total** | | **${{total}}** |Nested Tables
{{#each orders}}
Order #{{this.number}}
-
{{this.date}}
| Item | Qty | Price | |------|-----|-------|
{{#each this.items}}
|
{{name}}
|
{{qty}}
| ${{price}}
|
{{/each}}
---
{{/each}}Conditional Sections
{{#if hasDiscount}}
Original Price: ~~${{originalPrice}}~~ Discounted Price: ${{discountedPrice}}
{{else}}
Price: ${{price}}
{{/if}}Validation
Validate Document
Check that a template contains all required placeholders:
async validateInvoiceTemplate() {
const templateBuffer = await this.loadTemplate('invoice.docx');
const missingProps = await this.docxService.validateDocument(
templateBuffer,
['name', 'orderNumber', 'items', 'total', 'companyName'],
);
if (missingProps.length > 0) {
throw new Error(`Missing placeholders: ${missingProps.join(', ')}`);
}
}Validation Result
interface ValidationResult {
missing: string[]; // Placeholders not in template
unused: string[]; // Data properties not in template
}Real-World Examples
Invoice Generation
async generateInvoice(orderId: string): Promise<Buffer> {
const order = await this.orderService.findOne(orderId);
const template = await this.loadTemplate('invoice.docx');
const data = {
invoiceNumber: order.invoiceNumber,
date: new Date().toLocaleDateString(),
customer: {
name: order.customer.name,
email: order.customer.email,
address: `${order.customer.address}\n${order.customer.city}, ${order.customer.zip}`,
},
items: order.items.map(item => ({
name: item.productName,
quantity: item.quantity,
price: item.unitPrice.toFixed(2),
subtotal: item.subtotal.toFixed(2),
})),
subtotal: order.subtotal.toFixed(2),
tax: order.tax.toFixed(2),
total: order.total.toFixed(2),
isPaid: order.status === 'PAID',
paymentInfo: order.paymentInfo,
};
return this.docxService.generateDocx(template, data);
}Certificate Generation
async generateCertificate(userId: string, courseId: string): Promise<Buffer> {
const [user, course] = await Promise.all([
this.userService.findOne(userId),
this.courseService.findOne(courseId),
]);
const template = await this.loadTemplate('certificate.docx');
return this.docxService.generateDocx(template, {
recipientName: user.fullName,
courseName: course.name,
completionDate: new Date().toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
}),
instructorName: course.instructor.name,
certificateId: generateCertificateId(),
companyName: 'My Organization',
logoPath: '/templates/logo.png',
});
}Template Preparation
Creating Templates
- Open Microsoft Word or LibreOffice
- Create a new document
- Add placeholders using
{{placeholderName}}syntax - Save as
.docxformat
Recommended Placeholder Names
{{firstName}}
{{lastName}}
{{email}}
{{phone}}
{{address}}
{{city}}
{{orderNumber}}
{{orderDate}}
{{totalAmount}}
{{status}}
{{companyName}}
{{logoPath}}Testing Templates
const testData = {
name: 'Test User',
orderNumber: 'ORD-001',
items: [
{ name: 'Product A', price: 10 },
{ name: 'Product B', price: 20 },
],
total: 30,
isPremium: false,
companyName: 'Test Company',
};
const result = await docxService.generateDocx(templateBuffer, testData);
// Verify result is valid DOCXAPI Reference
Module
DocxModule.forRoot(options?: DocxModuleOptions)Service
class DocxService {
async generateDocx(
template: Buffer,
data: Record<string, any>,
): Promise<Buffer>;
async validateDocument(
template: Buffer,
requiredPlaceholders: string[],
): Promise<string[]>;
}Troubleshooting
Q: Placeholder not replaced?
Check for typos in template:
{{naem}}
← Wrong
{{name}}
← CorrectQ: Loop not working?
Ensure data is an array:
// ❌ Won't iterate
items: 'not an array';
// ✅ Will iterate
items: [{ name: 'A' }, { name: 'B' }];Q: Image not displaying?
Use correct image syntax:
{{#image path width height}}
relative/path/to/image.png
{{/image}}Q: Conditional not working?
Ensure condition is boolean:
// ❌ String
isPremium: 'true';
// ✅ Boolean
isPremium: true;Related Packages
- @zola_do/document-manipulator — PDF/DOCX merge and conversion
- @zola_do/minio — Store generated documents
License
ISC
