← All @molecule/* packages · App templates

@molecule/api-reporting

Core interface · reporting · API (Node) · v1.0.1 · Apache-2.0

Aggregate reporting core interface for molecule.dev

npm install @molecule/api-reporting

npm · Source on GitHub

How it works

@molecule/api-reporting is the reporting core interface on the API (Node) side: the API your app calls, with no vendor inside.

Choose the implementation by bonding one of its 1 provider: @molecule/api-reporting-database.

import { setProvider, aggregate, timeSeries } from '@molecule/api-reporting'
import { provider } from '@molecule/api-reporting-database'

// Wire the provider at startup
setProvider(provider)

// Run an aggregate query
const result = await aggregate({
  table: 'orders',
  measures: [{ field: 'amount', function: 'sum', alias: 'totalRevenue' }],
  dimensions: ['status'],
})

// Run a time-series query
const series = await timeSeries({
  table: 'orders',
  dateField: 'created_at',
  interval: 'day',
  measures: [{ field: 'id', function: 'count', alias: 'orderCount' }],
})

Providers (1): @molecule/api-reporting-database

Works with: @molecule/api-bond, @molecule/api-i18n

Reference

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Aggregate reporting core interface for molecule.dev.

Defines the abstract {@link ReportProvider} contract and convenience functions for executing aggregate queries, time-series analysis, data export, and report scheduling.

Quick Start

import { setProvider, aggregate, timeSeries } from '@molecule/api-reporting'
import { provider } from '@molecule/api-reporting-database'

// Wire the provider at startup
setProvider(provider)

// Run an aggregate query
const result = await aggregate({
  table: 'orders',
  measures: [{ field: 'amount', function: 'sum', alias: 'totalRevenue' }],
  dimensions: ['status'],
})

// Run a time-series query
const series = await timeSeries({
  table: 'orders',
  dateField: 'created_at',
  interval: 'day',
  measures: [{ field: 'id', function: 'count', alias: 'orderCount' }],
})

Type

core

Installation

npm install @molecule/api-reporting @molecule/api-bond @molecule/api-i18n

API

Interfaces

AggregateQuery

Query definition for aggregate reports.

interface AggregateQuery {
  /** Table or view to query. */
  table: string

  /** Measures (aggregations) to compute. */
  measures: Measure[]

  /** Columns to group by. */
  dimensions?: string[]

  /** WHERE clause filters. */
  filters?: Filter[]

  /** HAVING clause filters (applied after aggregation). */
  having?: Filter[]

  /** Result ordering. */
  orderBy?: OrderBy[]

  /** Maximum number of rows to return. */
  limit?: number
}

AggregateResult

Result of an aggregate query.

interface AggregateResult {
  /** Aggregated rows. */
  rows: Record<string, unknown>[]

  /** Total number of matching rows (before LIMIT). */
  total: number
}

Filter

A filter condition for queries.

interface Filter {
  /** Column to filter on. */
  field: string

  /** Comparison operator. */
  operator: FilterOperator

  /** Value or values to compare against. */
  value: unknown
}

Measure

A measure to compute during aggregation.

interface Measure {
  /** Column or expression to aggregate. */
  field: string

  /** Aggregate function to apply. */
  function: AggregateFunction

  /** Optional alias for the result column. */
  alias?: string
}

OrderBy

An ordering clause for query results.

interface OrderBy {
  /** Column to sort by. */
  field: string

  /** Sort direction. */
  direction: SortDirection
}

ReportProvider

Reporting provider interface.

All reporting providers must implement this interface.

interface ReportProvider {
  /**
   * Executes an aggregate query and returns grouped results.
   *
   * @param query - The aggregate query definition.
   * @returns Aggregated rows and total count.
   */
  aggregate(query: AggregateQuery): Promise<AggregateResult>

  /**
   * Executes a time-series query and returns bucketed data points.
   *
   * @param query - The time-series query definition.
   * @returns Ordered time-series points.
   */
  timeSeries(query: TimeSeriesQuery): Promise<TimeSeriesResult>

  /**
   * Exports query results in the specified format.
   *
   * @param query - The query to execute and export.
   * @param format - The desired output format.
   * @returns A Buffer containing the exported data.
   */
  export(query: AggregateQuery | TimeSeriesQuery, format: ExportFormat): Promise<Buffer>

  /**
   * Creates a scheduled report and returns its unique identifier.
   *
   * @param report - The scheduled report configuration.
   * @returns The schedule identifier.
   */
  schedule(report: ScheduledReport): Promise<string>

  /**
   * Cancels a previously scheduled report.
   *
   * @param scheduleId - The schedule identifier to cancel.
   */
  cancelSchedule(scheduleId: string): Promise<void>
}

ScheduledReport

Configuration for a scheduled report.

interface ScheduledReport {
  /** Human-readable report name. */
  name: string

  /** The query to execute on schedule. */
  query: AggregateQuery | TimeSeriesQuery

  /** Output format for the scheduled report. */
  format: ExportFormat

  /** Cron expression defining the schedule. */
  schedule: string

  /** Email addresses to deliver the report to. */
  recipients?: string[]
}

TimeSeriesPoint

A single data point in a time series.

interface TimeSeriesPoint {
  /** ISO 8601 date string for the bucket start. */
  date: string

  /** Aggregated values keyed by measure alias or field. */
  values: Record<string, number>
}

TimeSeriesQuery

Query definition for time-series reports.

interface TimeSeriesQuery {
  /** Table or view to query. */
  table: string

  /** Date/timestamp column to bucket by. */
  dateField: string

  /** Time bucket granularity. */
  interval: TimeInterval

  /** Measures (aggregations) to compute per bucket. */
  measures: Measure[]

  /** WHERE clause filters. */
  filters?: Filter[]

  /** Start of the date range (inclusive). */
  startDate?: Date

  /** End of the date range (inclusive). */
  endDate?: Date
}

TimeSeriesResult

Result of a time-series query.

interface TimeSeriesResult {
  /** Ordered data points. */
  points: TimeSeriesPoint[]

  /** The interval granularity used. */
  interval: string
}

Types

AggregateFunction

Aggregate function applied to a measure field.

type AggregateFunction = 'count' | 'sum' | 'avg' | 'min' | 'max' | 'countDistinct'

ExportFormat

Supported export formats.

type ExportFormat = 'csv' | 'json' | 'xlsx'

FilterOperator

Filter operator for WHERE and HAVING clauses.

type FilterOperator =
  'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'notIn' | 'between' | 'like'

SortDirection

Sort direction for ORDER BY clauses.

type SortDirection = 'asc' | 'desc'

TimeInterval

Time interval granularity for time-series queries.

type TimeInterval = 'hour' | 'day' | 'week' | 'month' | 'year'

Functions

aggregate(query)

Executes an aggregate query and returns grouped results.

function aggregate(query: AggregateQuery): Promise<AggregateResult>
  • query — The aggregate query definition.

Returns: Aggregated rows and total count.

cancelSchedule(scheduleId)

Cancels a previously scheduled report.

function cancelSchedule(scheduleId: string): Promise<void>
  • scheduleId — The schedule identifier to cancel.

Returns: A promise that resolves when the schedule has been cancelled.

exportReport(query, format)

Exports query results in the specified format.

function exportReport(
  query: AggregateQuery | TimeSeriesQuery,
  format: ExportFormat,
): Promise<Buffer<ArrayBufferLike>>
  • query — The query to execute and export.
  • format — The desired output format.

Returns: A Buffer containing the exported data.

getProvider()

Retrieves the bonded reporting provider, throwing if none is configured.

function getProvider(): ReportProvider

Returns: The bonded reporting provider.

hasProvider()

Checks whether a reporting provider is currently bonded.

function hasProvider(): boolean

Returns: true if a reporting provider is bonded.

scheduleReport(report)

Creates a scheduled report and returns its unique identifier.

function scheduleReport(report: ScheduledReport): Promise<string>
  • report — The scheduled report configuration.

Returns: The schedule identifier.

setProvider(provider)

Registers a reporting provider as the active singleton. Called by bond packages during application startup.

function setProvider(provider: ReportProvider): void
  • provider — The reporting provider implementation to bond.

timeSeries(query)

Executes a time-series query and returns bucketed data points.

function timeSeries(query: TimeSeriesQuery): Promise<TimeSeriesResult>
  • query — The time-series query definition.

Returns: Ordered time-series points.

Available Providers

ProviderPackage
Reporting@molecule/api-reporting-database

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-bond ^1.0.1
  • @molecule/api-i18n ^1.0.1

Runtime Dependencies

  • @molecule/api-bond

  • @molecule/api-i18n

  • table/field/dateField are PHYSICAL storage names — the table/column names your migrations created (typically snake_case, e.g. created_at), not model property names. A camelCase field that "looks right" returns empty or errored results.

  • Nothing is scoped automatically. Every user-facing report MUST carry a filters entry on the owning column (e.g. { field: 'user_id', operator: '=', value: userId }) or one user sees another's numbers.

  • The reference bond (@molecule/api-reporting-database) executes through the bonded database — the database bond must be wired before any reporting call runs.

  • Filter VALUES are parameterized and identifiers sanitized by the reference bond, but never pass user input as a table/field/alias name — only as filter values.

E2E Tests

Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:

  • Report/dashboard screens render aggregates that MATCH the seeded data — spot-check at least one total against rows you can count in the UI.
  • Changing the date range (and interval, if exposed) visibly updates the series and totals.
  • A dimension breakdown (e.g. by status/category) renders one segment or series per group present in the data.
  • A range with no data shows zeros or an empty state — not NaN, undefined, or a crashed chart.
  • If export is surfaced, the downloaded file's rows match what the report displays.
  • Reports are scoped to the signed-in user/tenant — never another user's numbers.