Media Analytics API โ Developer Guide
Overview
The Media Analytics API provides platform administrators with real-time insights into media upload activity, storage consumption, file type distribution, and processing pipeline health across the Mallnline platform. Exposed through the media subgraph (apps/media), these admin-only queries use MongoDB aggregation pipelines to compute storage and volume metrics server-side.
| Component | Purpose | Backend |
|---|---|---|
| MediaAnalyticsResolver | 4 admin-only storage analytics queries | @Resolver() with dual-layer access |
| MediaAnalyticsService | MongoDB $group, $sum, $sort pipelines |
MediaFile model aggregation |
| MediaAnalyticsFilter | Date range presets + custom filters | MediaAnalyticsDateRange enum |
All operations use the same dual-layer access control pattern: @RequirePermission(Permission.MANAGE_ANALYTICS) guard + runtime PLATFORM_ADMIN role verification.
Architecture
graph TD
A["MediaAnalyticsResolver"] --> B["MediaAnalyticsService"]
B --> C["MediaFile Model (MongoDB)"]
A -- "admin header" --> D["@RequirePermission(MANAGE_ANALYTICS)"]
A -- "runtime" --> E["assertPlatformAdmin(actor)"]
subgraph "Aggregation Pipelines"
C -- "$group by date, $sum size" --> F["mediaUploadsByDay"]
C -- "$group by mimetype" --> G["mediaTypeDistribution"]
C -- "$group by maletId, $sort totalSize" --> H["mediaStorageByMalet"]
C -- "$group by status" --> I["mediaStatusBreakdown"]
end
style D fill:#dc2626,color:#fff
style E fill:#dc2626,color:#fff
style F fill:#06b6d4,color:#fff
style G fill:#06b6d4,color:#fff
style H fill:#06b6d4,color:#fff
style I fill:#06b6d4,color:#fff
Data Source
The MediaFile entity includes key fields consumed by the analytics pipelines:
| Field | Type | Aggregation Use |
|---|---|---|
createdAt |
Date |
Daily upload volume grouping |
size |
Int |
Storage bytes โ summed for total/average calculations |
mimetype |
String |
File type distribution (image/jpeg, video/mp4, etc.) |
status |
MediaStatus |
Processing health (PENDING, COMPLETE, FAILED) |
maletId |
String |
Per-Malet storage isolation |
GraphQL API
Uploads By Day
Daily upload volume with cumulative storage โ ideal for line or area charts:
query MediaUploadsByDay($filter: MediaAnalyticsFilter) {
mediaUploadsByDay(filter: $filter) {
days {
date
count
totalSizeBytes
}
totalUploads
totalStorageBytes
periodStart
periodEnd
}
}
Variables:
{
"filter": {
"dateRange": "LAST_30_DAYS",
"maletId": "malet-photo-studio"
}
}
The totalSizeBytes per day enables tracking storage growth trends over time.
Type Distribution
Media files grouped by MIME type with aggregate storage:
query MediaTypes($filter: MediaAnalyticsFilter) {
mediaTypeDistribution(filter: $filter) {
mimetype
count
totalSizeBytes
}
}
Returns entries sorted by count descending. Common types include:
| MIME Type | Typical Source |
|---|---|
image/jpeg |
Product photos, gallery images |
image/png |
Logos, graphics with transparency |
image/webp |
Optimized web images (auto-generated) |
video/mp4 |
Malet Owner product videos |
image/tiff |
High-res print-ready gallery exports |
Storage By Malet
Storage usage leaderboard ranked by total bytes:
query MediaStorage($limit: Int, $filter: MediaAnalyticsFilter) {
mediaStorageByMalet(limit: $limit, filter: $filter) {
maletId
fileCount
totalSizeBytes
averageFileSize
}
}
Variables:
{
"limit": 10,
"filter": {
"dateRange": "LAST_365_DAYS"
}
}
Results are sorted by totalSizeBytes descending. The limit parameter caps at 100. Use averageFileSize to identify Malets uploading unusually large files that may need attention.
Status Breakdown
Processing pipeline health check:
query {
mediaStatusBreakdown {
status
count
}
}
| Status | Meaning |
|---|---|
PENDING |
Uploaded but not yet processed (thumbnails, LQIP not generated) |
COMPLETE |
Fully processed with thumbnails and blur placeholders |
FAILED |
Processing failed โ may need manual intervention |
A high PENDING count may indicate processing queue backlogs. A rising FAILED count signals infrastructure issues.
Type Reference
MediaAnalyticsFilter (Input)
| Field | Type | Default | Description |
|---|---|---|---|
dateRange |
MediaAnalyticsDateRange |
LAST_30_DAYS |
Preset date range |
startDate |
DateTime |
โ | Custom start date (overrides preset) |
endDate |
DateTime |
โ | Custom end date (overrides preset) |
maletId |
ID |
โ | Filter to a specific Malet |
MediaAnalyticsDateRange (Enum)
enum MediaAnalyticsDateRange {
LAST_7_DAYS // Weekly view
LAST_30_DAYS // Monthly view (default)
LAST_90_DAYS // Quarterly view
LAST_365_DAYS // Annual view
CUSTOM // Use startDate/endDate
}
MediaUploadsSummary
| Field | Type | Description |
|---|---|---|
days |
[MediaUploadsByDay!]! |
Daily upload breakdown |
totalUploads |
Int! |
Total uploads in the period |
totalStorageBytes |
Int! |
Cumulative storage bytes |
periodStart |
DateTime! |
Query period start |
periodEnd |
DateTime! |
Query period end |
MediaUploadsByDay
| Field | Type | Description |
|---|---|---|
date |
DateTime! |
Calendar date |
count |
Int! |
Files uploaded on this day |
totalSizeBytes |
Int! |
Bytes uploaded on this day |
MediaTypeDistribution
| Field | Type | Description |
|---|---|---|
mimetype |
String! |
MIME type (e.g., image/jpeg) |
count |
Int! |
Number of files |
totalSizeBytes |
Int! |
Total bytes for this type |
MaletStorageUsage
| Field | Type | Description |
|---|---|---|
maletId |
ID! |
The Malet identifier |
fileCount |
Int! |
Total files uploaded by this Malet |
totalSizeBytes |
Int! |
Total storage consumed in bytes |
averageFileSize |
Float! |
Mean file size in bytes |
MediaStatusBreakdown
| Field | Type | Description |
|---|---|---|
status |
MediaStatus! |
PENDING, COMPLETE, or FAILED |
count |
Int! |
Files with this processing status |
Access Control
Identical to all analytics modules across the platform:
- Declarative guard โ
@RequirePermission(Permission.MANAGE_ANALYTICS) - Runtime check โ
assertPlatformAdmin(actor)verifiesrole === 'PLATFORM_ADMIN'
@Query(() => MediaUploadsSummary)
@RequirePermission(Permission.MANAGE_ANALYTICS)
@HandleErrors()
async mediaUploadsByDay(
@Args('filter', { nullable: true }) filter: MediaAnalyticsFilter,
@CurrentActor() actor: Actor,
): Promise<MediaUploadsSummary> {
this.assertPlatformAdmin(actor);
return this.analyticsService.getUploadsByDay(filter);
}
Module Structure
apps/media/src/analytics/
โโโ analytics.types.ts # GraphQL types, enums, filter input
โโโ analytics.service.ts # MongoDB aggregation pipelines
โโโ analytics.service.spec.ts # 10 unit tests
โโโ analytics.resolver.ts # Admin-only resolver (4 queries)
โโโ analytics.resolver.spec.ts # 8 unit tests
โโโ analytics.module.ts # NestJS module (MediaFile model import)
apps/media/test/
โโโ analytics.e2e-spec.ts # 6 E2E tests
Storage Monitoring Use Cases
Capacity Planning
# Total platform storage over the last year
query {
mediaUploadsByDay(filter: { dateRange: LAST_365_DAYS }) {
totalStorageBytes # Total bytes across all Malets
totalUploads # Total files uploaded
}
}
Malet Owner Storage Audits
# Which Malets consume the most storage?
query {
mediaStorageByMalet(limit: 20) {
maletId
fileCount
totalSizeBytes
averageFileSize # Spot Malets uploading raw TIFFs vs compressed JPEGs
}
}
Processing Health Alerts
# Monitor for stuck or failed processing
query {
mediaStatusBreakdown {
status
count
}
}
# Alert if FAILED count > threshold or PENDING count spikes
Testing
# Unit tests (18 tests across 2 suites)
npm run test -- apps/media --testPathPattern="analytics"
# E2E tests (6 tests)
npx jest --config apps/media/test/jest-e2e.json --testPathPattern="analytics" --detectOpenHandles
Key coverage: date range resolution, upload-by-day aggregation, Malet filtering, type distribution, storage ranking with limit cap, status breakdown, empty-result handling, admin access enforcement.
Related
- Analytics Aggregation API โ Revenue, Murchase volume, entity counts
- Blog Analytics API โ Post volume, status distribution, author rankings
- Media Analytics Dashboard โ Frontend Media sub-tab component guide
- Workspaces & The Tower โ Tower architecture and all 5 analytics sub-tabs
- Media Infrastructure โ Multi-size thumbnails, blur placeholders, and presigned upload flow
- Media Intelligence & Processing โ EXIF extraction, deduplication, and video transcoding