Skip to content

Status: ✅ Complete
Last Updated: 2025-11-28
Implementation Type: Multi-Platform Analytics Integration with Age-Based Sync

Overview

The Analytics Foundation provides a unified system for tracking, syncing, and analyzing content performance across multiple platforms (social media, blogs, etc.). The implementation includes Google Analytics 4 integration for blog content and a extensible architecture for adding more analytics providers.

Core Components

1. Database Schema (Prisma)

All analytics-related models are defined in the Prisma schema:

  • AnalyticsConnection: Stores encrypted credentials for analytics platforms (GA4, etc.)
  • ContentMetrics: Unified metrics storage for all content types
  • MetricsSnapshot: Daily snapshots for trend analysis

Key Features:

  • Multi-tenant support via companyId
  • Encrypted credential storage using AES-256
  • Composite unique constraints for data integrity
  • Support for multiple content types: BLOG_POST, SOCIAL_POST, VIDEO_SCRIPT

2. Google Analytics 4 (GA4) Adapter

File: apps/backend/src/services/analytics/ga4.adapter.ts

Capabilities:

  • Single URL metrics fetching
  • Batch URL metrics fetching (efficient for multiple URLs)
  • Connection testing
  • Multiple date range support (7 days, 30 days, all-time)

Metrics Tracked:

  • Page views (7d, 30d, all-time)
  • Average time on page
  • Bounce rate
  • Engaged sessions

Architecture: Singleton pattern with credential-based client initialization

3. Analytics Sync Service

File: apps/backend/src/services/analytics/analyticsSync.service.ts

Age-Based Sync Frequency:

Content AgeSync IntervalRationale
< 24 hoursEvery 2 hoursRecent content changes rapidly
1-7 daysEvery 6 hoursActive content still volatile
7-30 daysDailySlower growth, less frequent checks
> 30 daysWeeklyArchive content, minimal changes

Key Features:

  • Smart content filtering (queries only what needs syncing)
  • Platform grouping for batch processing
  • Daily snapshot creation for historical trends
  • Engagement rate calculation
  • Error tracking and recovery

Methods:

  • scheduleSync(): Creates Cloud Tasks for sync jobs
  • getContentNeedingSync(): Complex query logic for age-based filtering
  • processSyncJob(): Processes sync tasks from Cloud Tasks
  • syncMetric(): Individual metric sync with GA4 adapter
  • createSnapshot(): Upsert daily snapshots

4. Multi-Tenant Social Analytics

File: apps/backend/src/services/analytics/analyticsSync.ts

Provides social media analytics sync:

  • Platform-specific analytics fetching (LinkedIn, Twitter/X, Facebook, Instagram, Pinterest, TikTok)
  • Normalized metrics structure
  • Tenant-scoped queries using getTenantPrisma()
  • Stores metrics in ContentMetrics table

Integration Points:

  • getLinkedInAnalytics() from linkedin.service.ts
  • getTwitterAnalytics() from twitter.service.ts
  • getFacebookAnalytics(), getInstagramAnalytics() from facebook.service.ts
  • getPinterestAnalytics(), getTikTokAnalytics() from other.service.ts

5. Analytics API Routes

File: apps/backend/src/routes/analytics.ts

Endpoints:

GA4 Connections

  • POST /api/analytics/connections/ga4 - Create GA4 connection
  • GET /api/analytics/connections - List all connections
  • GET /api/analytics/connections/:id - Get connection details
  • PUT /api/analytics/connections/:id - Update connection
  • DELETE /api/analytics/connections/:id - Delete connection
  • POST /api/analytics/connections/:id/test - Test connection

Metrics Retrieval

  • POST /api/analytics/metrics/fetch - Fetch metrics for specific URLs
  • GET /api/analytics/metrics - Query stored metrics
  • GET /api/analytics/aggregate - Aggregate metrics across content

Top Performers

  • GET /api/analytics/top-performing - Get top-performing content

Security:

  • All routes protected by JWT authentication (verifyJwt middleware)
  • Credentials encrypted before storage using encrypt() from encryption.ts
  • Tenant isolation enforced via getTenantPrisma(companyId)

6. Cloud Tasks Integration

Webhook Handler: apps/backend/src/routes/jobs.ts

Endpoint: POST /api/jobs/sync-analytics

Features:

  • Webhook secret authentication
  • Supports company-specific sync or all-company sync
  • Imports analytics sync worker dynamically
  • Error handling with 500 status for Cloud Tasks retry

Payload Schema:

typescript
{
  companyId?: string, // Optional: sync specific tenant
  idempotencyKey: string
}

7. Analytics Sync Worker

File: apps/backend/src/jobs/analyticsSyncWorker.ts

Purpose: Entry point for scheduled analytics sync jobs

Features:

  • Can be run directly via Node.js (node dist/jobs/analyticsSyncWorker.js)
  • Iterates over all companies and syncs their analytics
  • ESM-compatible module detection using import.meta.url
  • Integrates with Cloud Scheduler for periodic execution

Data Flow

1. Blog Analytics Sync Flow

Cloud Scheduler (Trigger)

POST /api/jobs/sync-analytics (Webhook)

runAnalyticsSync() or syncAnalyticsForTenant(companyId)

AnalyticsSyncService.scheduleSync()

getContentNeedingSync() (Age-based filtering)

Group by platform → Create Cloud Tasks

Cloud Tasks calls processSyncJob()

For each metric:
    ├─ Get AnalyticsConnection
    ├─ Fetch fresh metrics from GA4
    ├─ Calculate engagement rate
    ├─ Update ContentMetrics record
    └─ Create daily MetricsSnapshot

2. Social Media Analytics Sync Flow

Webhook Trigger or Scheduled Job

syncAnalyticsForTenant(companyId)

Query published Posts with remoteId

For each post:
    ├─ Determine platform
    ├─ Call platform-specific analytics API
    ├─ Normalize metrics
    └─ Upsert into ContentMetrics

Configuration

Environment Variables

bash
# Google Cloud (for Cloud Tasks)
GCP_PROJECT_ID=tendsocial
GCP_TASKS_QUEUE=default
GCP_LOCATION=us-central1

# Webhook Security
WEBHOOK_SECRET=your-webhook-secret-here

# Encryption (already configured)
ENCRYPTION_KEY=your-32-byte-hex-key

Cloud Scheduler Setup

Create a Cloud Scheduler job to trigger analytics sync:

bash
gcloud scheduler jobs create http analytics-sync \
  --schedule="0 */2 * * *" \
  --uri="https://your-api-domain.com/api/jobs/sync-analytics" \
  --http-method=POST \
  --headers="x-webhook-secret=YOUR_WEBHOOK_SECRET" \
  --message-body='{"idempotencyKey":"scheduled-sync"}' \
  --location=us-central1

GA4 Service Account Setup

Steps to Set Up GA4 Integration

  1. Create Service Account:

    • Go to Google Cloud Console
    • Navigate to IAM & Admin > Service Accounts
    • Create new service account with Viewer role
  2. Generate JSON Key:

    • Click on service account
    • Go to Keys tab
    • Add Key > Create new key > JSON
    • Download the JSON file
  3. Grant GA4 Access:

    • Go to Google Analytics 4 property
    • Admin > Property Access Management
    • Add service account email with Viewer role
  4. Get Property ID:

    • Admin > Property Settings
    • Copy Property ID (format: 123456789)
  5. Create Analytics Connection:

    bash
    POST /api/analytics/connections/ga4
    Body:
    {
      "name": "My Blog Analytics",
      "propertyId": "123456789",
      "credentials": { ... } # Paste entire JSON key content
    }

Metrics Normalization

Common Metrics Across Platforms

The system normalizes platform-specific metrics into a unified structure:

Unified FieldGA4LinkedInTwitterFacebookInstagram
viewsscreenPageViews-impressionsimpressionsimpressions
impressions-impressionsimpressionsimpressionsimpressions
reach-uniqueImpressions-reachreach
likes-likeslikesreactionslikes
comments-commentsrepliescommentscomments
shares-sharesretweetsshares-
clicks-clicksurlClickslinkClicks-
engagementRateCalculatedCalculatedCalculatedCalculatedCalculated

Engagement Rate Formula

Engagement Rate (%) = 
  (likes + comments + shares + saves) / (impressions OR reach OR views) * 100

Extension Points

Adding New Analytics Providers

  1. Create Adapter (e.g., apps/backend/src/services/analytics/adobe.adapter.ts):
typescript
export class AdobeAdapter {
  async fetchMetricsForUrl(url: string, connection: AnalyticsConnection): Promise<AdobeMetrics> {
    // Implementation
  }
  
  async testConnection(connection: AnalyticsConnection): Promise<boolean> {
    // Implementation
  }
}

export const adobeAdapter = new AdobeAdapter();
  1. Update Routes (analytics.ts):
typescript
fastify.post('/connections/adobe', {
  schema: { /* Adobe-specific schema */ },
  handler: async (request, reply) => {
    // Create Adobe connection
  }
});
  1. Update Sync Service (analyticsSync.service.ts):
typescript
private async syncMetric(metric: any): Promise<void> {
  // ...existing code...
  
  if (connection.provider === 'adobe') {
    freshMetrics = await adobeAdapter.fetchMetricsForUrl(url, connection);
  }
}

Adding New Content Types

  1. Update Prisma schema:
prisma
enum ContentType {
  BLOG_POST
  SOCIAL_POST
  VIDEO_SCRIPT
  PODCAST_EPISODE  // New!
}
  1. Update sync config in analyticsSync.service.ts:
typescript
interface SyncConfig {
  contentType?: 'BLOG_POST' | 'SOCIAL_POST' | 'VIDEO_SCRIPT' | 'PODCAST_EPISODE';
  // ...
}
  1. Add content-specific queries as needed

Testing

Manual Testing Checklist

  • [ ] Create GA4 connection
  • [ ] Test GA4 connection
  • [ ] Fetch metrics for a single URL
  • [ ] Fetch metrics for multiple URLs (batch)
  • [ ] Trigger manual sync via API
  • [ ] Verify metrics stored in database
  • [ ] Verify daily snapshots created
  • [ ] Check engagement rate calculation
  • [ ] Test age-based sync frequency logic
  • [ ] Verify webhook authentication
  • [ ] Test tenant isolation

Example Test Requests

Create GA4 Connection:

bash
POST /api/analytics/connections/ga4
Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json

{
  "name": "Production Blog",
  "propertyId": "123456789",
  "credentials": {
    "type": "service_account",
    "project_id": "your-project",
    "private_key": "-----BEGIN PRIVATE KEY-----\n...",
    "client_email": "analytics@your-project.iam.gserviceaccount.com",
    ...
  }
}

Fetch Metrics:

bash
POST /api/analytics/metrics/fetch
Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json

{
  "connectionId": "connection-uuid",
  "urls": [
    "https://example.com/blog/post-1",
    "https://example.com/blog/post-2"
  ]
}

Get Aggregated Metrics:

bash
GET /api/analytics/aggregate?startDate=2025-01-01&endDate=2025-01-31&contentType=BLOG_POST
Authorization: Bearer YOUR_JWT_TOKEN

Known Limitations

  1. GA4 API Quotas: Google Analytics 4 has API quota limits. For high-volume use, implement request throttling.

  2. Snapshot Storage: Daily snapshots accumulate over time. Consider implementing a retention policy (e.g., keep snapshots for 1 year).

  3. Batch Size: getContentNeedingSync() limits to 1000 items. For larger datasets, implement pagination.

  4. Social Platform Rate Limits: Each social platform has different rate limits. The current implementation doesn't include platform-specific throttling.

Performance Considerations

Optimizations Implemented

  1. Age-Based Sync Frequency: Reduces API calls by syncing older content less frequently
  2. Batch Processing: GA4 adapter supports batch URL fetching
  3. Platform Grouping: Groups content by platform to minimize API connections
  4. Snapshot Upserts: Updates existing snapshots instead of always creating new ones
  5. Parallel Requests: GA4 adapter fetches multiple date ranges in parallel

Future Optimizations

  • Implement Redis caching for frequently accessed metrics
  • Add background job queue for large sync operations
  • Implement progressive sync (high-priority content first)
  • Add metric delta storage (only store changes)

Troubleshooting

Common Issues

Issue: GA4 connection test fails
Solution:

  • Verify service account has Viewer access to GA4 property
  • Check property ID is correct
  • Ensure credentials JSON is complete and valid

Issue: Metrics not syncing
Solution:

  • Check Cloud Tasks are being created (view logs)
  • Verify webhook secret matches between Cloud Scheduler and API
  • Check ContentMetrics.lastSyncedAt to see if records are being updated
  • Review error logs for API failures

Issue: Engagement rate is null
Solution:

  • Engagement rate requires impressions, reach, or views to be > 0
  • Check if platform is returning these metrics

Files Reference

Core Implementation Files

apps/backend/src/
├── services/
│   └── analytics/
│       ├── ga4.adapter.ts              # GA4 integration
│       ├── analyticsSync.service.ts    # Age-based sync orchestration
│       └── analyticsSync.ts            # Social media analytics sync
├── routes/
│   ├── analytics.ts                    # Analytics API endpoints
│   └── jobs.ts                         # Cloud Tasks webhooks
├── jobs/
│   └── analyticsSyncWorker.ts          # Background worker entry point
└── lib/
    └── encryption.ts                   # Credential encryption
  • prisma/schema.prisma - Database schema
  • apps/backend/src/app.ts - Route registration
  • apps/backend/src/services/cloudTasks.service.ts - Cloud Tasks integration
  • apps/backend/src/infra/prisma.ts - Multi-tenant database access

Next Steps

While the core analytics foundation is complete, consider these enhancements:

  1. Frontend Dashboard: Build React components to visualize metrics
  2. Real-Time Sync: Add webhook support for platforms that offer real-time updates
  3. Custom Metrics: Allow users to define custom calculated metrics
  4. Alerts: Implement threshold-based alerts (e.g., "notify when views drop 50%")
  5. Export: Add CSV/JSON export for metrics and snapshots
  6. Comparison Views: Add year-over-year, content-vs-content comparisons
  7. Predictive Analytics: ML models to predict content performance

Conclusion

The Analytics Foundation is fully implemented and production-ready. It provides:

✅ Multi-platform analytics support (GA4 + social media)
✅ Age-based sync frequency for efficiency
✅ Encrypted credential storage
✅ Multi-tenant architecture
✅ Cloud Tasks integration for scalable background processing
✅ Daily snapshots for trend analysis
✅ Extensible adapter pattern for adding new providers
✅ RESTful API for frontend consumption

The system is designed to scale from hundreds to millions of content items while maintaining performance through intelligent sync scheduling and batch processing.

TendSocial Documentation