Skip to main content

Export Service

The Ever Works Template provides an analytics export system that supports CSV and JSON output formats, with metadata embedding, date-range filtering, and a comprehensive report mode. The service runs server-side via the AnalyticsExportService class and is surfaced to administrators through the AdminDataExport component.

Architecture Overview

Source Files

FilePurpose
lib/services/analytics-export.service.tsCore export service with CSV/JSON generation
components/admin/admin-data-export.tsxAdmin UI for triggering exports

Export Service Class

The AnalyticsExportService is the central export engine. It pulls data from the AdminAnalyticsOptimizedRepository and converts it into downloadable formats.

Interfaces

const EXPORT_FORMATS = {
CSV: 'csv',
JSON: 'json',
EXCEL: 'xlsx',
} as const;

type ExportFormat = typeof EXPORT_FORMATS[keyof typeof EXPORT_FORMATS];

interface ExportOptions {
format: ExportFormat;
dateRange?: { start: Date; end: Date };
includeMetadata?: boolean;
compression?: boolean;
}

interface ExportResult {
data: string | Buffer;
filename: string;
contentType: string;
size: number;
timestamp: Date;
}

Available Export Methods

MethodData SourceDefault Range
exportUserGrowthTrends(months, options)User signup trends12 months
exportActivityTrends(days, options)Votes, comments, views7 days
exportTopItems(limit, options)Highest-engagement itemsTop 10
exportRecentActivity(limit, options)Latest user actionsLast 10
exportComprehensiveReport(options)All of the above combinedMixed

Basic Usage

import { AnalyticsExportService } from '@/lib/services/analytics-export.service';

const exporter = new AnalyticsExportService();

// Export user growth as CSV
const result = await exporter.exportUserGrowthTrends(12, {
format: 'csv',
});

// result.data -> CSV string
// result.filename -> "user-growth-trends-2025-01-15T10-30-00-000Z.csv"
// result.contentType -> "text/csv; charset=utf-8"
// result.size -> byte length

Comprehensive Report

The exportComprehensiveReport method fetches all data sources in parallel and combines them into a single export:

const report = await exporter.exportComprehensiveReport({
format: 'json',
includeMetadata: true,
});

Internally, it runs four queries concurrently via Promise.all:

const [userGrowth, activityTrends, topItems, recentActivity] = await Promise.all([
this.repository.getUserGrowthTrends(12),
this.repository.getActivityTrends(30),
this.repository.getTopItems(50),
this.repository.getRecentActivity(100),
]);

The output includes a summary object with aggregated totals for quick reference.

CSV Generation

Array Data

For array-shaped results (most single-metric exports), the CSV generator:

  1. Collects the union of all keys across every row to handle sparse data
  2. Writes a header row from those keys
  3. Escapes values containing commas, quotes, or newlines using RFC 4180 rules
// Escaping logic
const esc = (v: any) => {
if (v === null || v === undefined) return '';
const s = typeof v === 'string' ? v : String(v);
return /[",\n\r]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
};

Nested Object Data

The comprehensive report produces nested objects. The objectToCSV method handles this recursively:

  • Arrays of objects become labeled CSV sections with their own header rows
  • Simple arrays are flattened into a single row
  • Nested objects are prefixed with dot notation (e.g., summary.totalUsers)

JSON Generation

Without Metadata

[
{ "month": "2025-01", "active": 150 },
{ "month": "2025-02", "active": 175 }
]

With Metadata

When includeMetadata is true, the output wraps data in an envelope:

interface ExportMetadata {
generatedAt: string; // ISO timestamp
dateRange?: string; // "start to end" when date range is set
totalRecords: number; // Array length or 1 for objects
exportFormat: string; // "JSON"
version: string; // "1.0.0"
}
{
"metadata": {
"generatedAt": "2025-01-15T10:30:00.000Z",
"totalRecords": 12,
"exportFormat": "JSON",
"version": "1.0.0"
},
"data": [ ... ]
}

Validation

The validateExportOptions method checks:

  1. Format validity -- the format must be one of the defined EXPORT_FORMATS values
  2. Date range logic -- start must not be after end
exporter.validateExportOptions({
format: 'csv',
dateRange: { start: new Date('2025-01-01'), end: new Date('2024-12-01') },
});
// false -- start is after end

Every export method calls validateExportOptions before proceeding and throws an Error if validation fails.

Filename Generation

Filenames include a full ISO timestamp with colons and dots replaced by hyphens:

user-growth-trends-2025-01-15T10-30-00-000Z.csv
activity-trends-2025-01-15T10-30-00-000Z.csv

JSON exports use a simpler date-only format:

top-items-2025-01-15.json

Admin Data Export Component

The AdminDataExport component provides the admin interface for triggering exports.

Export Options

The component presents five export options, each with internationalized labels via next-intl:

IDData Type
user-growthUser growth trends
activity-trendsActivity trends
top-itemsTop performing items
recent-activityRecent activity feed
comprehensiveFull analytics report

UI State

const [isExporting, setIsExporting] = useState(false);
const [exportProgress, setExportProgress] = useState(0);
const [selectedFormat, setSelectedFormat] = useState<'csv' | 'json'>('csv');
const [includeMetadata, setIncludeMetadata] = useState(true);

Export Flow

The component uses the browser's native download mechanism:

const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);

Scheduled Reports

The component includes a placeholder section for database-backed scheduled reports. Each report displays:

  • Report name and schedule
  • Output format (CSV/JSON)
  • Status indicator (generated, failed, pending)
  • Last generated and next generation timestamps

Report Management

The admin interface also provides buttons for:

  • Creating new report templates
  • Managing existing templates
  • Viewing export history
  • Configuring export settings

Content Type Mapping

FormatContent-TypeExtension
CSVtext/csv; charset=utf-8.csv
JSONapplication/json.json

Best Practices

  1. Use the comprehensive report for full analytics snapshots rather than exporting each metric individually.
  2. Enable metadata for JSON exports so downstream consumers can verify freshness and record counts.
  3. Validate date ranges before calling export methods to avoid confusing error states.
  4. Clean up object URLs after triggering downloads to prevent memory leaks in the browser.
  5. Handle export errors gracefully -- show a toast notification rather than leaving the progress bar stuck.
  6. Use the progress indicator to give users feedback during longer exports.