Skip to content

platform-documents

Template-based document generation engine supporting DOCX and PDF output. Uses pluggable renderers and template parsers with image processing via Sharp.

Package: @breadstone/archipel-platform-documents

Quick Start

typescript
import { Module } from '@nestjs/common';
import { DocumentModule } from '@breadstone/archipel-platform-documents';

@Module({
  imports: [
    DocumentModule.forRoot({
      maxImageWidth: 1920,
      maxImageHeight: 1080,
      delimiters: { start: '[[', end: ']]' },
      debug: false,
    }),
  ],
})
export class AppModule {}

Module Configuration

IDocumentModuleOptions

PropertyTypeDefaultDescription
maxImageWidthnumber1920Maximum width for embedded images
maxImageHeightnumber1080Maximum height for embedded images
delimiters.startstring'[['Template variable start delimiter
delimiters.endstring']]'Template variable end delimiter
debugbooleanfalseEnable debug output

DocumentEngine

Main orchestrator for document rendering.

Render from Template

typescript
import { DocumentEngine } from '@breadstone/archipel-platform-documents';

@Injectable()
export class InvoiceService {
  constructor(private readonly _docEngine: DocumentEngine) {}

  public async generateInvoice(data: IInvoiceData): Promise<Buffer> {
    const result = await this._docEngine.render({
      templateId: 'invoice-template',
      data: {
        invoiceNumber: data.number,
        customerName: data.customerName,
        items: data.items.map((item) => ({
          description: item.description,
          qty: item.quantity,
          unitPrice: `€${item.unitPrice.toFixed(2)}`,
          total: `€${(item.quantity * item.unitPrice).toFixed(2)}`,
        })),
        subtotal: `€${data.subtotal.toFixed(2)}`,
        tax: `€${data.tax.toFixed(2)}`,
        total: `€${data.total.toFixed(2)}`,
        date: new Date().toLocaleDateString('de-DE'),
      },
      templateFormat: 'docx',
    });

    return result.buffer;
  }
}

Render from Buffer

typescript
const templateBuffer = await fs.readFile('./templates/contract.docx');

const result = await this._docEngine.render({
  templateBuffer,
  data: { clientName: 'Acme Corp', startDate: '2025-01-01' },
  templateFormat: 'docx',
});

Templates with Images

typescript
const result = await this._docEngine.render({
  templateId: 'report-with-charts',
  data: {
    title: 'Q4 Report',
    summary: 'Revenue increased by 23%.',
  },
  images: [
    {
      tag: 'revenueChart', // Matches [[revenueChart]] in template
      buffer: chartImageBuffer,
      contentType: 'image/png',
      width: 800,
      height: 400,
    },
  ],
  templateFormat: 'docx',
});

Document Renderers

DOCX Renderer

Renders DOCX (Microsoft Word) documents using docxtemplater:

  • Variable substitution: [[variableName]]
  • Loops: [[#items]][[name]] - [[price]][[/items]]
  • Conditionals: [[#if showDiscount]]...[[/if]]
  • Image placeholders: [[imageName]]

PDF Renderer

Renders PDF documents using pdf-lib:

  • Text substitution in PDF form fields
  • Image insertion
  • Page manipulation

Image Processing

SharpImageProcessor

Resizes and optimizes images before embedding in documents:

typescript
import { SharpImageProcessor } from '@breadstone/archipel-platform-documents';

// Used internally by DocumentEngine
// Respects maxImageWidth and maxImageHeight from module options

Supported formats: PNG, JPEG, WebP, TIFF, AVIF.


Real-World Example: Contract Generator

typescript
@Controller('api/v1/contracts')
export class ContractController {
  constructor(
    private readonly _docEngine: DocumentEngine,
    private readonly _blobService: BlobService,
  ) {}

  @Post(':id/generate')
  public async generate(@Param('id') contractId: string, @Res() res: Response): Promise<void> {
    const contract = await this._contractService.findById(contractId);

    const result = await this._docEngine.render({
      templateId: 'service-agreement',
      data: {
        clientName: contract.clientName,
        startDate: contract.startDate.toLocaleDateString('de-DE'),
        endDate: contract.endDate.toLocaleDateString('de-DE'),
        monthlyFee: `€${contract.monthlyFee.toFixed(2)}`,
        services: contract.services,
      },
      templateFormat: 'docx',
    });

    // Optionally store in blob storage
    await this._blobService.uploadFile(
      `contracts/${contractId}/${result.name}`,
      result.buffer,
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    );

    res.set({
      'Content-Type': result.contentType,
      'Content-Disposition': `attachment; filename="${result.name}"`,
    });
    res.send(result.buffer);
  }
}

Exports Summary

ExportTypeDescription
DocumentModuleNestJS ModuleforRoot() configuration
DocumentEngineServiceMain rendering orchestrator
BaseDocumentRendererAbstract classRenderer base
IDocumentRendererInterfaceRenderer contract
SharpImageProcessorServiceImage resize/optimization
IImageProcessorInterfaceImage processor contract
DocumentFormatEnum'docx' / 'pdf'
TemplateFormatEnumTemplate format identifiers
IRenderResultInterfaceRender output (buffer, name, contentType)
IDocumentProbeReportInterfaceTemplate validation report

Released under the MIT License.