Multi-format export utilities for Rich Domain repositories (CSV, JSON, and more)
npm install @woltz/rich-domain-exportMulti-format export utilities for rich-domain repositories. Export your domain entities to CSV, JSON, and more!
This package extends rich-domain repositories with powerful multi-format export capabilities, supporting both in-memory and streaming exports for large datasets.
- ✅ Multiple formats - CSV, JSON, and extensible for custom formats
- ✅ Type-safe exports - Full TypeScript support with discriminated unions
- ✅ Streaming support - Memory-efficient exports for large datasets
- ✅ Criteria integration - Uses rich-domain Criteria API for filtering
- ✅ Custom formatters/transformers - Transform field values during export
- ✅ Validation - Built-in validation for export options
- ✅ Progress tracking - Monitor export progress for large datasets
- ✅ Two approaches - Repository extension or standalone service
- ✅ JSON Lines support - Stream-friendly JSONL format
``bash`
npm install @woltz/rich-domain-export
Note: This is a backend-only package (Node.js). For frontend exports, use API endpoints.
`typescript
import { ExportableRepository } from "@woltz/rich-domain-export";
import { Criteria } from "@woltz/rich-domain";
class UserRepository extends ExportableRepository
// Your repository implementation
}
const userRepository = new UserRepository();
// Export as CSV
const { data, stats } = await userRepository.export(
Criteria.create
{
format: "csv",
columns: ["name", "email", "createdAt"],
headers: {
name: "Full Name",
email: "Email Address",
createdAt: "Registration Date",
},
}
);
// Export as JSON
const { data, stats } = await userRepository.export(criteria, {
format: "json",
pretty: true,
fields: ["name", "email"],
});
console.log(Exported ${stats.totalRecords} records in ${stats.durationMs}ms);`
`typescript
import { ExportService } from "@woltz/rich-domain-export";
const exportService = new ExportService();
// Export from any repository
const { data, stats } = await exportService.export(userRepository, criteria, {
format: "csv",
columns: ["name", "email"],
});
`
`typescript${value} years old
const { data } = await repository.export(criteria, {
format: "csv",
columns: ["name", "email", "age"],
headers: { name: "Full Name", email: "Email Address", age: "Age" },
delimiter: ",",
includeHeaders: true,
formatters: {
age: (value) => ,`
},
});
CSV Options:
- columns? - Fields to include (default: all fields)headers?
- - Custom header labelsdelimiter?
- - Delimiter character (default: ,)includeHeaders?
- - Include header row (default: true)formatters?
- - Custom formatters (returns string)
`typescript
// Standard JSON
const { data } = await repository.export(criteria, {
format: "json",
pretty: true,
indent: 2,
fields: ["name", "email"],
rootKey: "users",
transformers: {
email: (email) => email.toLowerCase(),
},
});
// JSON Lines (streaming-friendly)
const { data } = await repository.export(criteria, {
format: "json",
jsonLines: true,
fields: ["name", "email"],
});
`
JSON Options:
- pretty? - Pretty print with indentation (default: false)indent?
- - Number of spaces for indentation (default: 2)jsonLines?
- - Use JSON Lines format (default: false)fields?
- - Fields to include (default: all fields)transformers?
- - Custom transformers (returns any type)rootKey?
- - Wrap output in root key
For large datasets, use streaming to avoid loading everything into memory:
`typescript
// CSV stream
const stream = await repository.exportStream(criteria, {
format: "csv",
batchSize: 1000,
});
stream.pipe(fs.createWriteStream("users.csv"));
// JSON Lines stream (recommended for large JSON exports)
const stream = await repository.exportStream(criteria, {
format: "json",
jsonLines: true,
batchSize: 500,
});
stream.pipe(fs.createWriteStream("users.jsonl"));
`
`typescript
const stream = await repository.exportStream(criteria, {
format: "csv",
columns: ["name", "email"],
});
reply
.header("Content-Type", "text/csv")
.header("Content-Disposition", 'attachment; filename="users.csv"')
.send(stream);
`
`typescriptExport progress: ${percentage.toFixed(1)}%
const { data, stats } = await repository.export(
criteria,
{ format: "csv", columns: ["name", "email"] },
(processed, total) => {
const percentage = (processed / total) * 100;
console.log();`
}
);
Extend the library with custom formats using the Strategy Pattern:
`typescript
import {
ExportFormatStrategy,
FormatRegistry
} from "@woltz/rich-domain-export";
class ExcelFormatStrategy implements ExportFormatStrategy<...> {
async export(records, options) {
// Your Excel export logic
}
async exportStream(recordsIterator, options) {
// Your streaming logic
}
validateOptions(options) { / ... / }
getMimeType() { return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; }
getFileExtension() { return "xlsx"; }
getFormatName() { return "excel"; }
}
// Register custom format
FormatRegistry.register("excel", ExcelFormatStrategy);
// Use it
const { data } = await repository.export(criteria, {
format: "excel",
sheetName: "Users"
});
`
CSV includes pre-built formatters:
`typescript
import { commonFormatters } from "@woltz/rich-domain-export";
const { data } = await repository.export(criteria, {
format: "csv",
columns: ["name", "amount", "createdAt", "active"],
formatters: {
amount: commonFormatters.currencyUSD,
createdAt: commonFormatters.isoDate,
active: commonFormatters.yesNo,
},
});
`
Available formatters:
- Dates: isoDate, localeDate, localeDateTimedecimal2
- Numbers: , currencyUSDyesNo
- Booleans: , trueFalsearray
- Collections: , jsonuppercase
- Text: , lowercase, trim
`typescript
import {
ValidationError,
FormatterError,
ExportOperationError,
} from "@woltz/rich-domain-export";
try {
const { data } = await repository.export(criteria, {
format: "csv",
columns: ["name", "email"],
});
} catch (error) {
if (error instanceof ValidationError) {
console.error("Invalid options:", error.validationErrors);
} else if (error instanceof FormatterError) {
console.error(Formatter failed for field: ${error.field});Export failed at phase: ${error.phase}
} else if (error instanceof ExportOperationError) {
console.error();`
}
}
`typescript
abstract class ExportableRepository
export(
criteria?: Criteria
options: ExportOptions
onProgress?: ExportProgressCallback
): Promise
exportStream(
criteria?: Criteria
options: ExportOptions
): Promise
}
`
`typescript
class ExportService {
export
repository: Repository
criteria: Criteria
options: ExportOptions
onProgress?: ExportProgressCallback
): Promise
exportStream
repository: Repository
criteria: Criteria
options: ExportOptions
): Promise
getMimeType(format: string): string;
getFileExtension(format: string): string;
}
`
`typescript`
class FormatRegistry {
static register(
format: string,
strategyClass: new () => ExportFormatStrategy
): void;
static getStrategy(format: string): ExportFormatStrategy;
static hasFormat(format: string): boolean;
static getRegisteredFormats(): string[];
}
| Dataset Size | Recommended Method | Memory Usage |
| ---------------- | ------------------ | -------------------- |
| < 1,000 records | export() | ~1-5 MB |export()
| 1,000 - 10,000 | | ~5-50 MB |exportStream()
| 10,000 - 100,000 | | ~10-20 MB (constant) |exportStream()
| > 100,000 | | ~10-20 MB (constant) |
Tips:
- Use exportStream() for datasets > 10,000 recordsjsonLines: true
- Use JSON Lines () for streaming large JSON exportsbatchSize
- Adjust option to control memory usage (default: 1000)
Full TypeScript support with discriminated unions for type-safe format selection:
`typescript``
// TypeScript enforces valid options for each format
const result = await repository.export(criteria, {
format: "csv",
columns: ["name"], // ✓ Valid for CSV
delimiter: ",", // ✓ Valid for CSV
// pretty: true // ✗ Error: 'pretty' doesn't exist on CSV options
});
MIT
Contributions are welcome! Please feel free to submit a Pull Request.
Tarcisio Andrade