cancel
Showing results for 
Search instead for 
Did you mean: 
Read only

I built a zero-dependency Excel plugin for SAP CAP — does this solve a problem you have too?

fabi2295
Participant
320

Every time I needed to generate .xlsx files in a CAP project the answer was the same: pull in exceljs or xlsx, wire up the API, and deal with the bundle weight. I decided to solve it differently.

I built @capgio-js/excel — a CAP plugin that generates Excel workbooks using only Node.js built-ins (zlib and buffer). No external runtime dependencies at all.

It's not public yet. Before publishing I want to know if this is just a problem I have or if it resonates with more people.


HOW IT WORKS

Install the package and it auto-discovers itself as a CAP plugin — no manual require in your handlers. The full API is available under cds.excel.


Entity-driven tables — the part I use most

The thing that saves the most time: pass a CDS entity and the plugin builds the columns automatically. It reads your entity's elements, skips associations and compositions, and generates one column per primitive field. It picks up @title and @Common.Label annotations as column headers and maps CDS types to the right Excel cell styles out of the box.

    srv.on('exportReport', async (req) => {
      const { ExcelBuilder } = cds.excel
      const rows = await SELECT.from('MyService.Orders').orderBy('createdAt desc')

      const wb = new ExcelBuilder()
      wb.addSheet('Orders').addEntity(srv.entities.Orders, {
        rows,
        labels:     true,    // column headers from @title / @Common.Label
        autoFormat: true,    // integer, decimal, date styles from CDS types
        exclude:    ['internalField', 'technicalKey'],
        order:      ['orderDate', 'customer', 'total'],
        overrides:  { total: { header: 'Total (USD)' } },
        theme:      'blue',
      })

      return wb
    })

CDS type to Excel style mapping:

Integer, Int16, Int32, Int64, UInt8 → Whole number
Decimal, Double → 2 decimal places
Date, DateTime, Timestamp → Short date
Everything else → General

If you prefer incremental configuration there is a fluent EntityBuilder that defers rendering until serialisation time:

const cfg = sheet.getEntity(srv.entities.Orders)
cfg
.setLabels(true)
.setAutoFormat(true)
.include(['orderDate', 'customer', 'amount', 'status'])
.setOrder(['orderDate', 'customer'])
.addRows(rows)

Pair it with the @Excel.export annotation and the download becomes fully declarative — annotate the action in your .cds model, return the ExcelBuilder instance from the handler, and the plugin intercepts the result and sends the binary response automatically:

    // in your .cds model
    @Excel.export: { filename: 'orders.xlsx' }
    action exportOrders();

    // in your handler
    srv.on('exportOrders', async (req) => {
      const wb = new ExcelBuilder()
      wb.addSheet('Orders').addEntity(srv.entities.Orders, {
        rows:       await SELECT.from('MyService.Orders'),
        labels:     true,
        autoFormat: true,
      })
      return wb  // plugin handles the binary response
    })
 

Tests:

This is not mocked or manually created — it is a direct output from the current implementation.

fabi2295_1-1776262749506.png


Other features

- Multi-sheet workbooks — add as many named sheets as you need per workbook
- 8 colour themes (blue, green, orange, red, purple, dark, light, default) with header rows and alternating stripes
- Formula support — SUM, PRODUCT, cell multiply, and arbitrary inline formulas
- Three download patterns — Base64 string for OData actions, direct binary via cds.excel.download(), or fully declarative via @Excel.export
- Worksheet protection — sheet.protect({ password }) to prevent accidental edits
- Sensitivity labels — embed classification metadata as custom document properties
- Structured logging — integrates with cds.log('excel'), falls back to console.* outside CAP
- Deterministic output — fixed timestamps produce byte-identical ZIP archives across builds
- Standalone — works without CAP via require('@cap-js/excel')


WHY NO DEPENDENCIES?

exceljs is around 200 KB minified and pulls sub-dependencies. xlsx has a dual license that can get complicated in corporate environments. Since .xlsx is just a ZIP of XML files with an open spec (OOXML / ECMA-376), implementing the essentials with Node's native zlib is doable — and it removes any supply chain, license, or bundle-size conversation entirely.


IS THIS USEFUL TO YOU?

A few questions before I decide whether to publish:

- Do you use CAP and need to generate Excel today? How are you solving it?
- Does the zero-dependency angle matter in your context — audits, license policies, bundle size?
- Is there a feature that would make this actually useful for you that isn't listed here?

Happy to share more details or a demo. Any feedback appreciated.

Accepted Solutions (0)

Answers (0)