Awesome-omni-skill backend-development

Use this skill when the user asks about "async patterns", "Promise.all", "parallel execution", "Firebase functions", "webhook handlers", "cron jobs", "cold start", "timeout", "pubsub", "background processing", "message queue", "fan-out", "event-driven", or any Node.js/Firebase backend patterns. Provides async/await patterns, function configuration, Pub/Sub messaging, and scalable background processing.

install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/backend-development-trantuananh-17" ~/.claude/skills/diegosouzapw-awesome-omni-skill-backend-development-3664c3 && rm -rf "$T"
manifest: skills/development/backend-development-trantuananh-17/SKILL.md
source content

Node.js & Firebase Functions Patterns

Async/Await Patterns

Parallel Execution

// ❌ BAD: Sequential (3000ms)
const customers = await getCustomers();
const settings = await getSettings();
const tiers = await getTiers();

// ✅ GOOD: Parallel (1000ms)
const [customers, settings, tiers] = await Promise.all([
  getCustomers(),
  getSettings(),
  getTiers()
]);

Avoid Await in Loops

// ❌ BAD: Sequential loop
for (const customer of customers) {
  await updateCustomer(customer);
}

// ✅ GOOD: Parallel
await Promise.all(customers.map(c => updateCustomer(c)));

// ✅ BETTER: Chunked for rate limits
async function processInChunks(items, fn, chunkSize = 10) {
  for (let i = 0; i < items.length; i += chunkSize) {
    const chunk = items.slice(i, i + chunkSize);
    await Promise.all(chunk.map(fn));
  }
}

Firebase Functions Configuration

Right-Sizing Guide

Function TypeMemoryTimeout
Simple API handler256MB60s
Webhook handler256-512MB60s
Data sync (small)512MB120s
Data sync (large)1GB540s
Bulk operations1GB540s
High-traffic API512MB60s

Webhook Handlers (CRITICAL)

Shopify requires response within 5 seconds or it will retry.

// ❌ BAD: Heavy processing (may timeout)
app.post('/webhooks/orders/create', async (req, res) => {
  await calculatePoints(req.body);
  await updateCustomer(req.body);
  res.status(200).send('OK');
});

// ✅ GOOD: Queue and respond fast
app.post('/webhooks/orders/create', async (req, res) => {
  if (!verifyHmac(req)) {
    return res.status(401).send('Unauthorized');
  }

  await webhookQueueRef.add({
    type: 'orders/create',
    payload: req.body
  });

  res.status(200).send('OK');
});

Background Processing

MethodUse CaseVolumeLatency
Firestore triggerSimple queuingLow-MediumReal-time
Cloud TasksDelayed processing, rate limitsMediumConfigurable
Pub/SubHigh volume, fan-out, scalingHighReal-time

See

cloud-tasks-queue
skill for delayed/scheduled patterns.


Pub/Sub Patterns (Scalable Background Processing)

When to Use Pub/Sub

ScenarioRecommended
High-volume webhooks (100+ per minute)✅ Pub/Sub
Fan-out to multiple consumers✅ Pub/Sub
Decoupled microservices✅ Pub/Sub
At-least-once delivery needed✅ Pub/Sub
Delayed execution (specific time)❌ Use Cloud Tasks
Rate-limited API calls❌ Use Cloud Tasks

Publishing Messages

import {PubSub} from '@google-cloud/pubsub';

const pubsub = new PubSub();

// Publish single message
async function publishMessage(topicName, data) {
  const topic = pubsub.topic(topicName);
  const messageBuffer = Buffer.from(JSON.stringify(data));

  const messageId = await topic.publishMessage({data: messageBuffer});
  return messageId;
}

// Publish with attributes (for filtering)
async function publishWithAttributes(topicName, data, attributes) {
  const topic = pubsub.topic(topicName);
  const messageBuffer = Buffer.from(JSON.stringify(data));

  const messageId = await topic.publishMessage({
    data: messageBuffer,
    attributes: {
      shopId: data.shopId,
      eventType: attributes.eventType
    }
  });
  return messageId;
}

// Batch publish for high volume
async function publishBatch(topicName, items) {
  const topic = pubsub.topic(topicName, {
    batching: {
      maxMessages: 100,
      maxMilliseconds: 100
    }
  });

  const promises = items.map(item =>
    topic.publishMessage({data: Buffer.from(JSON.stringify(item))})
  );

  return Promise.all(promises);
}

Subscriber Functions (Firebase)

import * as functions from 'firebase-functions';

// Basic subscriber
exports.processOrderEvents = functions.pubsub
  .topic('order-events')
  .onPublish(async (message, context) => {
    const data = JSON.parse(Buffer.from(message.data, 'base64').toString());

    try {
      await processOrder(data);
    } catch (error) {
      console.error('Processing failed:', error);
      throw error; // Pub/Sub will retry
    }
  });

// With message attributes filtering (server-side)
exports.processVipOrders = functions.pubsub
  .topic('order-events')
  .onPublish(async (message, context) => {
    const attributes = message.attributes;

    // Skip non-VIP orders early
    if (attributes.tierLevel !== 'vip') {
      return;
    }

    const data = JSON.parse(Buffer.from(message.data, 'base64').toString());
    await processVipOrder(data);
  });

Fan-Out Pattern (One Event → Multiple Actions)

// Webhook receives order → Publish once → Multiple subscribers process
app.post('/webhooks/orders/create', async (req, res) => {
  if (!verifyHmac(req)) {
    return res.status(401).send('Unauthorized');
  }

  // Single publish, multiple consumers handle different aspects
  await pubsub.topic('order-created').publishMessage({
    data: Buffer.from(JSON.stringify(req.body)),
    attributes: {
      shopId: req.body.shopId,
      orderValue: String(req.body.total_price)
    }
  });

  res.status(200).send('OK');
});

// Consumer 1: Calculate points
exports.calculatePoints = functions.pubsub
  .topic('order-created')
  .onPublish(async (message) => {
    const order = JSON.parse(Buffer.from(message.data, 'base64').toString());
    await pointsService.calculateAndAward(order);
  });

// Consumer 2: Update VIP tier
exports.updateTier = functions.pubsub
  .topic('order-created')
  .onPublish(async (message) => {
    const order = JSON.parse(Buffer.from(message.data, 'base64').toString());
    await tierService.recalculate(order.customerId);
  });

// Consumer 3: Send notification
exports.sendNotification = functions.pubsub
  .topic('order-created')
  .onPublish(async (message) => {
    const order = JSON.parse(Buffer.from(message.data, 'base64').toString());
    await notificationService.sendPointsEarned(order);
  });

Error Handling & Dead Letter Queue

// Configure subscription with DLQ in GCP Console or Terraform
// subscription: order-events-sub
// dead_letter_topic: order-events-dlq
// max_delivery_attempts: 5

// Process dead letter messages
exports.handleDeadLetters = functions.pubsub
  .topic('order-events-dlq')
  .onPublish(async (message) => {
    const data = JSON.parse(Buffer.from(message.data, 'base64').toString());

    // Log for investigation
    console.error('Dead letter received:', {
      data,
      attributes: message.attributes,
      deliveryAttempt: message.attributes?.deliveryAttempt
    });

    // Store for manual review
    await firestore.collection('failedEvents').add({
      data,
      attributes: message.attributes,
      timestamp: new Date()
    });
  });

Ordering Messages (When Order Matters)

// Use ordering key for messages that must be processed in sequence
async function publishOrderedMessage(topicName, data, orderingKey) {
  const topic = pubsub.topic(topicName, {
    messageOrdering: true
  });

  await topic.publishMessage({
    data: Buffer.from(JSON.stringify(data)),
    orderingKey // e.g., customerId - ensures all messages for same customer processed in order
  });
}

Decision: Pub/Sub vs Cloud Tasks

NeedSolution
Process NOW, scale automaticallyPub/Sub
Process LATER (delay)Cloud Tasks
Multiple consumers for same eventPub/Sub (fan-out)
Rate-limited external APICloud Tasks
At-least-once deliveryBoth work
Exactly-once processingImplement idempotency

Cron Jobs (Scheduled Functions)

import * as functions from 'firebase-functions';

// Daily cleanup at midnight UTC
exports.dailyCleanup = functions.pubsub
  .schedule('0 0 * * *')
  .timeZone('UTC')
  .onRun(async (context) => {
    await cleanupExpiredRewards();
    await archiveOldActivities();
  });

// Every 5 minutes - sync pending updates
exports.syncPendingUpdates = functions.pubsub
  .schedule('every 5 minutes')
  .onRun(async (context) => {
    const pending = await getPendingUpdates();
    await processBatch(pending);
  });

// Weekly tier recalculation (Sunday 2am)
exports.weeklyTierRecalc = functions.pubsub
  .schedule('0 2 * * 0')
  .timeZone('America/New_York')
  .onRun(async (context) => {
    await tierService.recalculateAllTiers();
  });

Queue System (Firestore-based)

For simple queuing without Cloud Tasks/Pub/Sub overhead:

// Add to queue
async function enqueue(type, payload, shopId) {
  await firestore.collection('queues').add({
    type,
    payload,
    shopId,
    status: 'pending',
    createdAt: new Date(),
    attempts: 0
  });
}

// Process queue with Firestore trigger
exports.processQueue = functions.firestore
  .document('queues/{docId}')
  .onCreate(async (snap, context) => {
    const job = snap.data();
    const docRef = snap.ref;

    try {
      await docRef.update({status: 'processing'});

      switch (job.type) {
        case 'sync_customer':
          await syncCustomer(job.payload);
          break;
        case 'send_email':
          await sendEmail(job.payload);
          break;
      }

      await docRef.update({status: 'completed', completedAt: new Date()});
    } catch (error) {
      const attempts = job.attempts + 1;
      if (attempts >= 3) {
        await docRef.update({status: 'failed', error: error.message});
      } else {
        await docRef.update({status: 'pending', attempts});
      }
    }
  });

Queue vs Cloud Tasks vs Pub/Sub

FeatureFirestore QueueCloud TasksPub/Sub
Setup complexityLowMediumMedium
Delayed execution❌ Manual✅ Built-in❌ No
Rate limiting❌ Manual✅ Built-in❌ No
Fan-out❌ No❌ No✅ Yes
High volume⚠️ Limited✅ Good✅ Best
CostFirestore readsTask feesMessage fees
RetriesManualAutomaticAutomatic

Checklist

□ Independent async operations use Promise.all
□ No await inside loops
□ Functions right-sized (memory, timeout)
□ Webhooks respond < 5 seconds
□ Heavy processing in background queue
□ Proper error handling with try/catch
□ High-volume events use Pub/Sub fan-out
□ Delayed tasks use Cloud Tasks
□ Simple queuing uses Firestore triggers
□ Scheduled jobs use cron functions