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:
- Fetch post from database
- Verify it's still scheduled and not cancelled
- Call platform API to publish
- Update post status to 'published'
- 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/* webhookBenefits:
- 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:
- Logged with full error context
- Retried up to 3 times (Cloud Tasks)
- Marked as failed after max retries
- 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