Blog Analytics API โ Developer Guide
Overview
The Blog Analytics API provides platform administrators with real-time insights into content production, publishing status, author performance, and reader engagement across all Malets on the Mallnline platform. Exposed through the blogs subgraph (apps/blogs), these admin-only queries use MongoDB aggregation pipelines to compute metrics server-side.
| Component | Purpose | Backend |
|---|---|---|
| BlogAnalyticsResolver | 4 admin-only content analytics queries | @Resolver() with dual-layer access |
| BlogAnalyticsService | MongoDB $group, $dateToString, $sum pipelines |
Blog entity aggregation |
| BlogAnalyticsFilter | Date range presets + custom filters | BlogAnalyticsDateRange enum |
All operations are gated behind dual-layer access control: the @RequirePermission(Permission.MANAGE_ANALYTICS) decorator guard and a runtime PLATFORM_ADMIN role check.
Architecture
graph TD
A["BlogAnalyticsResolver"] --> B["BlogAnalyticsService"]
B --> C["Blog Model (MongoDB)"]
A -- "admin header" --> D["@RequirePermission(MANAGE_ANALYTICS)"]
A -- "runtime" --> E["assertPlatformAdmin(actor)"]
subgraph "Aggregation Pipelines"
C -- "$group by date + status" --> F["blogPostsByDay"]
C -- "$group by status" --> G["blogStatusBreakdown"]
C -- "$group by createdBy" --> H["topBlogAuthors"]
C -- "$sum viewCount/likeCount" --> I["blogEngagement"]
end
style D fill:#dc2626,color:#fff
style E fill:#dc2626,color:#fff
style F fill:#8b5cf6,color:#fff
style G fill:#8b5cf6,color:#fff
style H fill:#8b5cf6,color:#fff
style I fill:#8b5cf6,color:#fff
Data Source
The Blog entity includes key fields consumed by the analytics pipelines:
| Field | Type | Aggregation Use |
|---|---|---|
createdAt |
Date |
Daily post volume grouping |
status |
BlogStatus |
Status distribution breakdown |
createdBy |
String |
Author ranking |
viewCount |
Int |
Engagement โ total and per-post views |
likeCount |
Int |
Engagement โ total and per-post likes |
maletId |
String |
Per-Malet content filtering |
GraphQL API
Posts By Day
Daily post creation volume grouped by publishing status โ ideal for stacked area charts:
query BlogPostsByDay($filter: BlogAnalyticsFilter) {
blogPostsByDay(filter: $filter) {
days {
date
count
status
}
totalPosts
publishedCount
draftCount
periodStart
periodEnd
}
}
Variables:
{
"filter": {
"dateRange": "LAST_30_DAYS",
"maletId": "malet-ke-safari"
}
}
Each day entry includes the BlogStatus value (DRAFT, SCHEDULED, PUBLISHED, ARCHIVED), enabling visualization of content lifecycle distribution.
Status Breakdown
A platform-wide snapshot of all blog posts by publishing status:
query {
blogStatusBreakdown {
status
count
}
}
Returns counts across all statuses regardless of date range โ useful for real-time pipeline health monitoring.
Top Blog Authors
Authors ranked by post count with aggregate engagement totals:
query TopBlogAuthors($limit: Int, $filter: BlogAnalyticsFilter) {
topBlogAuthors(limit: $limit, filter: $filter) {
authorId
postCount
totalViews
totalLikes
}
}
Variables:
{
"limit": 10,
"filter": {
"dateRange": "LAST_90_DAYS"
}
}
Results are sorted by postCount descending. The limit parameter caps at 100. The authorId maps to a Visitor/Buyer user ID โ resolve to a full user profile via the nodes subgraph through federation.
Blog Engagement
Aggregate engagement metrics with the top-performing posts:
query BlogEngagement($filter: BlogAnalyticsFilter) {
blogEngagement(filter: $filter) {
totalViews
totalLikes
avgViewsPerPost
avgLikesPerPost
topPosts {
id
title
viewCount
likeCount
maletId
}
}
}
The topPosts array returns the top 10 blog posts by viewCount descending. Only posts with viewCount > 0 are included.
Type Reference
BlogAnalyticsFilter (Input)
| Field | Type | Default | Description |
|---|---|---|---|
dateRange |
BlogAnalyticsDateRange |
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 |
BlogAnalyticsDateRange (Enum)
enum BlogAnalyticsDateRange {
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
}
BlogPostsSummary
| Field | Type | Description |
|---|---|---|
days |
[BlogPostsByDay!]! |
Daily post breakdown |
totalPosts |
Int! |
Total posts in the period |
publishedCount |
Int! |
Posts with PUBLISHED status |
draftCount |
Int! |
Posts with DRAFT status |
periodStart |
DateTime! |
Query period start |
periodEnd |
DateTime! |
Query period end |
BlogPostsByDay
| Field | Type | Description |
|---|---|---|
date |
DateTime! |
Calendar date |
count |
Int! |
Posts created on this day |
status |
BlogStatus |
Publishing status for this group |
BlogStatusBreakdown
| Field | Type | Description |
|---|---|---|
status |
BlogStatus! |
DRAFT, SCHEDULED, PUBLISHED, ARCHIVED |
count |
Int! |
Total posts with this status |
TopBlogAuthor
| Field | Type | Description |
|---|---|---|
authorId |
ID! |
Author user identifier |
postCount |
Int! |
Total posts authored |
totalViews |
Int! |
Aggregate views across all posts |
totalLikes |
Int! |
Aggregate likes across all posts |
BlogEngagementSummary
| Field | Type | Description |
|---|---|---|
totalViews |
Int! |
Total views across all posts |
totalLikes |
Int! |
Total likes across all posts |
avgViewsPerPost |
Float! |
Mean views per post |
avgLikesPerPost |
Float! |
Mean likes per post |
topPosts |
[TopBlogPost!]! |
Top 10 posts by views |
TopBlogPost
| Field | Type | Description |
|---|---|---|
id |
ID! |
Blog post identifier |
title |
String! |
Post title |
viewCount |
Int! |
Number of views |
likeCount |
Int! |
Number of likes |
maletId |
ID! |
Owning Malet identifier |
Access Control
Identical to all analytics modules across the platform:
- Declarative guard โ
@RequirePermission(Permission.MANAGE_ANALYTICS) - Runtime check โ
assertPlatformAdmin(actor)verifiesrole === 'PLATFORM_ADMIN'
@Query(() => BlogPostsSummary)
@RequirePermission(Permission.MANAGE_ANALYTICS)
@HandleErrors()
async blogPostsByDay(
@Args('filter', { nullable: true }) filter: BlogAnalyticsFilter,
@CurrentActor() actor: Actor,
): Promise<BlogPostsSummary> {
this.assertPlatformAdmin(actor); // Defense-in-depth
return this.analyticsService.getPostsByDay(filter);
}
Module Structure
apps/blogs/src/analytics/
โโโ analytics.types.ts # GraphQL types, enums, filter input
โโโ analytics.service.ts # MongoDB aggregation pipelines
โโโ analytics.service.spec.ts # 12 unit tests
โโโ analytics.resolver.ts # Admin-only resolver (4 queries)
โโโ analytics.resolver.spec.ts # 8 unit tests
โโโ analytics.module.ts # NestJS module (Blog model import)
apps/blogs/test/
โโโ analytics.e2e-spec.ts # 6 E2E tests
Testing
# Unit tests (20 tests across 2 suites)
npm run test -- apps/blogs --testPathPattern="analytics"
# E2E tests (6 tests)
npx jest --config apps/blogs/test/jest-e2e.json --testPathPattern="analytics" --detectOpenHandles
Key coverage: date range resolution, post-by-day aggregation with status grouping, Malet filtering, status breakdown, author ranking with limit cap, engagement computation with empty-result handling, admin access enforcement.
Related
- Analytics Aggregation API โ Revenue, Murchase volume, entity counts
- Media Analytics API โ Upload volume, storage usage, type distribution
- Workspaces & The Tower โ Tower architecture and all 5 analytics sub-tabs
- Blogs Connectivity & SEO โ RSS feeds, sitemap generation, and canonical URL computation
- Edit History Audit Trail โ Blog post field-level change tracking via
blogPostEditHistory - Admin Analytics Dashboards โ Frontend Blog Analytics component that consumes these APIs