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

npm install @breadstone/archipel-platform-documents

Quick Start

typescript
import { Module } from '@nestjs/common';
import { DocumentModule } from '@breadstone/archipel-platform-documents';
import { SharpImageProcessor } from '@breadstone/archipel-platform-documents/sharp';
import { DocxDocumentRenderer2 } from '@breadstone/archipel-platform-documents/docx';
import { PdfDocumentRenderer } from '@breadstone/archipel-platform-documents/pdf';

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

Module Configuration

IDocumentModuleOptions

PropertyTypeDefaultDescription
imageProcessorType<IImageProcessor>Required. Image processor class (e.g. SharpImageProcessor)
renderersType<IDocumentRenderer>[]Required. Renderer classes (e.g. [DocxDocumentRenderer2, PdfDocumentRenderer])
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. Emits debug-level logs for document format detection and renderer initialization to aid troubleshooting.

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);
  }
}

Error Handling

The library provides a hierarchy of domain error classes for structured error handling:

Error classBase classWhen thrown
DocumentErrorErrorBase class for all document errors
DocumentRenderErrorDocumentErrorTemplate rendering failures (DOCX, PDF)
DocumentValidationErrorDocumentErrorInvalid templates, missing placeholders
ImageProcessingErrorDocumentErrorImage resize/conversion failures (Sharp)
typescript
import {
  DocumentRenderError,
  DocumentValidationError,
  ImageProcessingError,
} from '@breadstone/archipel-platform-documents';

All errors extend DocumentError, so a single catch (error instanceof DocumentError) handles any document-related failure.


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
DocumentErrorError classBase document error
DocumentRenderErrorError classRender failure error
DocumentValidationErrorError classValidation failure error
ImageProcessingErrorError classImage processing failure error

Released under the MIT License.