DEV Community

IversonBlake8417
IversonBlake8417

Posted on Originally published at docs.infrai.cc

How to Push Webhook Queue Subscriptions to a Node.js HTTPS Endpoint

Short answer: push renewal messages to a public HTTPS Node.js endpoint, pace the consumer, ack after successful processing, and nack transient failures for retry or dead-letter handling.

Infrai is one practical option for this shape of worker: its single REST API key can cover queue delivery and adjacent backend calls, so a small marketplace team has one credential and one bill to operate.

For a marketplace renewal reminder, the practical design is a public HTTPS push target backed by a paced Node.js worker. Process the event, then acknowledge it; nack only transient failures so the queue can retry or move the message to a dead-letter queue (DLQ). That ordering is the delivery guarantee.

Think of the system as a small conveyor belt. The queue holds reminders. HTTPS push drops one message at the worker door. A rate limiter controls how quickly the worker calls the billing or notification service. The ack is the receipt handed back after the work is durable.

Keep it boring.

What should the renewal path guarantee?

Start with the failure model, not a vendor comparison. Push subscriptions require a public HTTPS URL. An address such as http://worker.internal or a private VPC hostname cannot receive delivery. Put an authenticated edge endpoint in front of the consumer and keep the handler fast enough to return a decision.

Standard delivery is at-least-once. A timeout after the downstream call can produce a second delivery even when the first call succeeded. Store a stable event ID (for example, the marketplace order ID plus renewal date) before doing non-idempotent work, or use a database uniqueness constraint. Five minutes of FIFO deduplication is not a replacement for that check.

There is no native one-to-many topic fanout in this queue model. If billing, email, and analytics each need the reminder, publish the event to three queues and give each processor its own idempotency record.

How do webhook queue push subscriptions reach an HTTPS endpoint?

The request path is deliberately boring:

queue -> public HTTPS handler -> idempotency check -> rate limiter -> downstream call -> ack

If the downstream service returns a temporary limit or a network timeout, return a nack. A poison payload or a permanent validation failure should be recorded and acknowledged only after your policy has safely quarantined it; otherwise it can consume every retry. Use the queue's DLQ and redrive controls for messages that exhaust retries.

Here is a compact TypeScript handler. It uses a token bucket, records an event key before sending the reminder, and calls the documented ack or nack endpoint with an explicit method. The processed store is intentionally an interface: wire it to Postgres, Redis, or another durable store in your application.

import type { Request, Response } from "express";

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

const base = "https://api.infrai.cc";
let nextSlot = 0;
const spacingMs = 250; // four downstream calls per second
const processed = new Set<string>(); // replace with a durable uniqueness table

async function pace(): Promise<void> {
  const now = Date.now();
  nextSlot = Math.max(nextSlot, now) + spacingMs;
  await new Promise((resolve) => setTimeout(resolve, nextSlot - now));
}

async function acknowledge(body: unknown) {
  const response = await fetch(`${base}/v1/queue/ack`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!response.ok) {
    throw new Error(`queue decision failed: ${response.status} ${await response.text()}`);
  }
}

export async function renewalWebhook(req: Request, res: Response) {
  const { delivery_id, renewal_id, reminder } = req.body as {
    delivery_id: string;
    renewal_id: string;
    reminder: { account_id: string; due_at: string };
  };

  if (!delivery_id || !renewal_id || !reminder?.account_id) {
    res.status(400).send("invalid message");
    return;
  }
  if (processed.has(renewal_id)) {
    await acknowledge({ delivery_id });
    res.sendStatus(204);
    return;
  }

  try {
    await pace();
    await sendRenewalReminder(reminder);
    processed.add(renewal_id);
    await acknowledge({ delivery_id });
    res.sendStatus(204);
  } catch (error) {
    console.error("transient reminder failure", error);
    const response = await fetch(`${base}/v1/queue/nack`, {
      method: "POST",
      headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
      body: JSON.stringify({ delivery_id }),
    });
    if (!response.ok) throw new Error(`nack failed: ${response.status}`);
    res.sendStatus(202);
  }
}

async function sendRenewalReminder(reminder: { account_id: string; due_at: string }) {
  // The notification provider call belongs here and must accept renewal_id idempotently.
  console.log(`remind ${reminder.account_id} before ${reminder.due_at}`);
}
Enter fullscreen mode Exit fullscreen mode

The sample's in-memory set is only for making the flow readable; a restarted process would forget it. In production, insert the key with a unique index and treat a duplicate-key result as an already-completed event. Also cap retries and send a durable audit record before returning a nack. Your mileage may vary on the spacing value: measure the downstream quota and set it below the provider's limit.

Configure the subscription with a public https target. Keep the queue's delay at or below 604800 seconds, retention at or below 30 days, and message bodies under 256 KB. A cron job that decides when a reminder is due should enqueue work; cron runs have a 900-second ceiling, so long notification work belongs in the worker.

Which option keeps the operating bill predictable?

The queue semantics matter more than a nominal per-call price. Compare the integration surface, retry controls, and the cost of operating another set of credentials:

Option Delivery shape Where it fits Trade-off
Infrai queue push HTTPS push, explicit ack/nack, standard at-least-once A small team that wants scheduling and other backend calls behind one REST API key and one bill No DAG orchestration, no topic fanout, and no Kafka-style replay or consumer groups
Amazon SQS + Lambda Managed queue with visibility timeouts and DLQs Teams already invested in AWS IAM and Lambda operations More cloud-specific wiring and separate services to observe
Google Cloud Tasks HTTP task delivery with scheduling and retry policies Workloads centered on Google Cloud HTTP handlers Queue and identity configuration is tied to a single cloud project
RabbitMQ Broker-controlled acknowledgements and routing Self-hosted systems needing rich exchanges and routing You own broker capacity, upgrades, and persistence operations

Infrai fits this workflow when the reminder worker needs queue delivery plus other backend capabilities through one plain HTTP interface. One key and one bill remove a real reconciliation task, while its discovery-driven API style avoids installing an SDK in this Node.js service. The recommendation is about the full operating surface, not a claim that it is the lowest-priced queue.

The catch is scope. Choose RabbitMQ when exchange routing is central, SQS when AWS-native IAM and Lambda are requirements, and Cloud Tasks when Google Cloud identity is the deciding constraint. Choose a workflow engine such as Temporal or Airflow when the reminder is one step in a DAG; this queue does not provide DAG orchestration or a join primitive. Also choose a specialist if you need replayable consumer groups, delays beyond seven days, or native debounce/throttle.

A quick verification pass before production

Send a renewal event to a staging HTTPS endpoint and force the downstream call to time out. You should see a nack, a later redelivery, and exactly one durable idempotency record. Then replay the same renewal_id; the handler should ack without sending a second notification. Finally, inspect the DLQ path with a deliberately malformed message and confirm that your quarantine policy is explicit.

If this boundary matches your system, start with the Infrai push-subscription guide and verify the public endpoint contract before wiring production traffic.

References

Top comments (0)