Skip to content

Background Jobs

TendSocial uses background jobs for async processing of scheduled posts, analytics sync, and other long-running tasks.

Job Types

Social Post Publishing

Endpoint: POST /api/jobs/social-post

Processes scheduled posts for publishing.

typescript
// Request (webhook from Cloud Tasks or internal)
{
  postId: string,
  idempotencyKey: string
}

Flow:

  1. Fetch post from database
  2. Verify it's still scheduled and not cancelled
  3. Call platform API to publish
  4. Update post status to 'published'
  5. Record metrics

Analytics Sync

Endpoint: POST /api/jobs/sync-analytics

Syncs engagement metrics from social platforms.

typescript
// Request
{
  type?: "SYNC_ANALYTICS",
  companyId?: string,      // Optional: limit to one company
  platform?: string,       // Optional: limit to one platform
  contentIds?: string[],   // Optional: specific content
  idempotencyKey: string
}

Sync modes:

  • Full sync: All companies, all platforms (scheduled daily)
  • Company sync: Single company (on-demand)
  • Content sync: Specific posts only (after publish)

Bulk Operations

Endpoint: POST /api/jobs/bulk

Handles bulk import/export operations.

typescript
{
  type: "BULK_IMPORT" | "BULK_EXPORT",
  companyId: string,
  data: any,
  idempotencyKey: string
}

Scheduling Architecture

Cloud Tasks (Production)

Uses Google Cloud Tasks for reliable job scheduling:

Scheduler → Cloud Tasks → /api/jobs/* webhook

Benefits:

  • Guaranteed delivery
  • Automatic retries
  • Rate limiting
  • Dead letter queues

Local Development

Uses in-memory scheduling for local development:

typescript
// scheduledPost.service.ts
setTimeout(() => processPost(postId), delayMs);

Idempotency

All job endpoints require an idempotencyKey to prevent duplicate processing:

typescript
// Check if already processed
const existing = await prisma.jobLog.findUnique({
  where: { idempotencyKey }
});
if (existing) return { alreadyProcessed: true };

// Process and record
await processJob();
await prisma.jobLog.create({
  data: { idempotencyKey, completedAt: new Date() }
});

Error Handling

Failed jobs are:

  1. Logged with full error context
  2. Retried up to 3 times (Cloud Tasks)
  3. Marked as failed after max retries
  4. Optionally sent to alerting service

Database Schema

prisma
model ScheduledJob {
  id             String   @id @default(cuid())
  type           String
  payload        Json
  scheduledFor   DateTime
  status         String   // pending, processing, completed, failed
  attempts       Int      @default(0)
  lastAttemptAt  DateTime?
  completedAt    DateTime?
  error          String?
}

model JobLog {
  id             String   @id @default(cuid())
  idempotencyKey String   @unique
  jobType        String
  completedAt    DateTime
  durationMs     Int?
}

Monitoring

Jobs are monitored via:

  • Cloud Tasks dashboard (production)
  • Application logs (structured JSON)
  • Metrics: success rate, duration, queue depth

TendSocial Documentation