NestboltNestbolt

@nestbolt/excel

Decorator Exports

Use @Exportable, @ExportColumn, and @ExportIgnore decorators to define exports directly on your NestJS entities and DTOs.

The decorator API is the recommended way to define exports when you have existing entity or DTO classes. Instead of creating separate export classes, you annotate your data classes directly with metadata that describes how they should appear in a spreadsheet.

@Exportable()

The @Exportable() class decorator marks a class as exportable and configures sheet-level options. Without this decorator, the entity cannot be passed to the *FromEntity methods on ExcelService.

import { Exportable } from "@nestbolt/excel";

@Exportable()
class SimpleEntity {
  // ...
}

ExportableOptions

Pass an options object to configure the worksheet:

@Exportable({
  title: "Users",
  autoFilter: "auto",
  autoSize: true,
  frozenRows: 1,
  frozenColumns: 1,
  columnWidths: { A: 10, B: 30, C: 20 },
})
class UserEntity {
  // ...
}
OptionTypeDefaultDescription
titlestring--Worksheet tab name. When omitted, ExcelJS uses its default sheet name.
autoFilterstring--Add auto-filter dropdowns. Use "auto" to detect the range from headings, or pass an explicit range like "A1:D1".
autoSizebooleanfalseAutomatically size all columns to fit their content.
frozenRowsnumber--Number of rows to freeze from the top (e.g., 1 to freeze the heading row).
frozenColumnsnumber--Number of columns to freeze from the left.
columnWidthsRecord<string, number>--Explicit column widths keyed by column letter ("A", "B", etc.), in character units.

Each option maps to an equivalent concern interface:

OptionEquivalent Concern
titleWithTitle
autoFilterWithAutoFilter
autoSizeShouldAutoSize
frozenRowsWithFrozenRows
frozenColumnsWithFrozenColumns
columnWidthsWithColumnWidths

@ExportColumn()

The @ExportColumn() property decorator marks a property for inclusion in the export and configures its column behavior. Only properties decorated with @ExportColumn() appear in the output.

import { Exportable, ExportColumn } from "@nestbolt/excel";

@Exportable()
class ProductEntity {
  @ExportColumn({ order: 1, header: "SKU", width: 15 })
  sku!: string;

  @ExportColumn({ order: 2, header: "Product Name" })
  name!: string;

  @ExportColumn({ order: 3, format: "#,##0.00" })
  price!: number;

  @ExportColumn({
    order: 4,
    header: "In Stock",
    map: (val) => (val ? "Yes" : "No"),
  })
  inStock!: boolean;

  @ExportColumn({
    order: 5,
    header: "Total Value",
    format: "$#,##0.00",
    map: (val, row) => row.price * row.quantity,
  })
  totalValue!: number;
}

ExportColumnOptions

OptionTypeDefaultDescription
ordernumberInsertion order + 1,000,000Column position. Lower numbers appear further left. Columns are sorted by order before writing.
headerstringTitle-cased property nameColumn heading text. When omitted, the property name is converted from camelCase to Title Case (e.g., firstName becomes "First Name").
formatstring--Excel number format string applied to the column. Examples: "#,##0.00", "yyyy-mm-dd", "0%", "$#,##0.00".
map(value, row) => any--Transform function called for each cell. Receives the raw property value and the full row object. The return value is written to the cell.
widthnumber--Column width in character units. Overrides any width set in @Exportable({ columnWidths }) for this column.

Calling @ExportColumn() Without Options

When called without arguments, the column uses default behavior:

@ExportColumn()
firstName!: string;

This creates a column with the header "First Name" (derived from the property name), no specific order (appended after ordered columns), no format, and no value transformation.

The map Function

The map function is powerful for transforming values before they are written to the spreadsheet. It receives two arguments: the raw property value and the entire row object.

@ExportColumn({
  order: 3,
  header: "Full Name",
  map: (val, row) => `${row.firstName} ${row.lastName}`,
})
fullName!: string;

@ExportColumn({
  order: 4,
  header: "Status",
  map: (val) => {
    switch (val) {
      case "active": return "Active";
      case "inactive": return "Inactive";
      case "pending": return "Pending Review";
      default: return "Unknown";
    }
  },
})
status!: string;

@ExportColumn({
  order: 5,
  header: "Created",
  map: (val) => val instanceof Date ? val.toISOString().split("T")[0] : val,
})
createdAt!: Date;

@ExportIgnore()

The @ExportIgnore() property decorator explicitly excludes a property from the export. This is especially useful in inheritance scenarios where a parent class marks a property with @ExportColumn() but a child class needs to hide it.

import { Exportable, ExportColumn, ExportIgnore } from "@nestbolt/excel";

@Exportable({ title: "Users" })
class UserEntity {
  @ExportColumn({ order: 1 }) id!: number;
  @ExportColumn({ order: 2 }) name!: string;
  @ExportColumn({ order: 3 }) email!: string;

  @ExportIgnore()
  password!: string;

  @ExportIgnore()
  refreshToken!: string;
}

Properties without any export decorator are also excluded from the output. @ExportIgnore() serves two purposes:

  1. Documentation -- it makes the exclusion explicit and visible to other developers.
  2. Inheritance override -- it removes a column that was included by a parent class.

Inheritance

Export decorators support class inheritance. Child classes inherit all @ExportColumn() definitions from their parent and can extend, override, or remove them.

Inheriting Columns

@Exportable({ title: "Base" })
class BaseEntity {
  @ExportColumn({ order: 1, header: "ID" })
  id!: number;

  @ExportColumn({ order: 2 })
  createdAt!: Date;
}

@Exportable({ title: "Products" })
class ProductEntity extends BaseEntity {
  @ExportColumn({ order: 3 })
  name!: string;

  @ExportColumn({ order: 4, format: "#,##0.00" })
  price!: number;
}
// Exported columns: ID, Created At, Name, Price

Overriding a Column

A child class can redefine a column inherited from the parent:

@Exportable({ title: "Users" })
class UserEntity extends BaseEntity {
  @ExportColumn({ order: 1, header: "User ID", width: 10 })
  id!: number;  // overrides BaseEntity's @ExportColumn for id

  @ExportColumn({ order: 3 })
  email!: string;
}
// Exported columns: User ID, Created At, Email

Removing a Column

Use @ExportIgnore() in a child class to remove a column that was included by the parent:

@Exportable({ title: "Employees" })
class EmployeeEntity extends BaseEntity {
  @ExportColumn({ order: 3 })
  department!: string;

  @ExportIgnore()
  createdAt!: Date;  // remove from export
}
// Exported columns: ID, Department

ExcelService Entity Methods

ExcelService provides four methods for working with decorated entities:

downloadFromEntity

Returns an ExcelDownloadResult object containing the file buffer, filename, and content type:

const result = await this.excelService.downloadFromEntity(
  UserEntity,
  users,
  "users.xlsx",
);

// result.buffer     -- Buffer containing the file
// result.filename   -- "users.xlsx"
// result.contentType -- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

You can use this to build a custom response:

@Get("export")
async exportUsers(@Res() res: Response) {
  const users = await this.usersService.findAll();
  const result = await this.excelService.downloadFromEntity(
    UserEntity,
    users,
    "users.xlsx",
  );

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

downloadFromEntityAsStream

Returns a NestJS StreamableFile that can be returned directly from a controller method:

@Get("export")
async exportUsers() {
  const users = await this.usersService.findAll();
  return this.excelService.downloadFromEntityAsStream(
    UserEntity,
    users,
    "users.xlsx",
  );
}

This is the most convenient method for HTTP downloads. The StreamableFile sets the Content-Type and Content-Disposition headers automatically.

storeFromEntity

Writes the export to a storage backend:

await this.excelService.storeFromEntity(
  UserEntity,
  users,
  "reports/users.xlsx",
);

// Write to a specific named disk
await this.excelService.storeFromEntity(
  UserEntity,
  users,
  "reports/users.xlsx",
  undefined,  // writerType (auto-detect from extension)
  "s3",       // disk name
);

rawFromEntity

Returns the raw file buffer:

import { ExcelType } from "@nestbolt/excel";

const buffer = await this.excelService.rawFromEntity(
  UserEntity,
  users,
  ExcelType.XLSX,
);

// Use the buffer for email attachments, S3 uploads, etc.

Method Signatures

MethodReturnsDescription
downloadFromEntity(entityClass, data, filename, type?)ExcelDownloadResultBuffer + filename + content type
downloadFromEntityAsStream(entityClass, data, filename, type?)StreamableFileNestJS StreamableFile for direct controller return
storeFromEntity(entityClass, data, filePath, type?, disk?)voidWrite to storage (default or named disk)
rawFromEntity(entityClass, data, type)BufferRaw file buffer

All methods accept a generic type parameter T that matches the entity class. The data argument must be an array of instances of that class (or plain objects matching its shape). The type argument is optional and auto-detected from the filename extension when omitted.