Node.js

Das Paket honk-me ist ein TypeScript-Client ohne Laufzeitabhängigkeiten, für Node 18 und neuer, Bun und Deno. Nutze es in Backend-Code, Functions und Skripten.

Paket
honk-me Veröffentlicht
Quellcode
github.com/honk-me/honk-node · MIT-Lizenz
Voraussetzungen
Node 18 oder neuer (globales fetch), Bun oder Deno.

Installation

Erstelle danach in der Honk-Web-App ein Projekt und einen Ingest-Schlüssel.

Terminal
npm install honk-me

Mit einem Ingest-Schlüssel (honk_…) kann jeder an sein Projekt senden. Er gehört auf Server, in Jobs und in CI-Secrets, nie in einen Browser, eine Mobil- oder Desktop-App.

Ein Ereignis senden

Honk.fromEnv() liest HONK_URL, HONK_KEY und die optionalen Standardwerte HONK_SOURCE, HONK_ENVIRONMENT und HONK_CHANNEL. In CommonJS funktioniert auch require('honk-me').

import { Honk } from 'honk-me';

const honk = new Honk({ url: process.env.HONK_URL, key: process.env.HONK_KEY });
await honk.beep('Backup finished', 'nightly pg_dump took 42 s');

Rezept: eine Kundenanfrage auf deinem Handy

Eine Gruppe pro Anfrage und ein stabiler Idempotency-Key: Zwei Kunden landen nie in derselben Mitteilung, und ein wiederholter Webhook lässt dein Handy nie zweimal vibrieren. Auf das Promise wird nicht gewartet, sodass ein langsames Netzwerk die Anfrage des Kunden nie bremst.

import { Honk } from 'honk-me';

const honk = Honk.fromEnv(); // create once, reuse (keep-alive)

export async function onCustomerRequest(req) {
  // ...save the request first, then notify without blocking the response:
  honk
    .send(
      {
        title: `New request: ${req.subject}`.slice(0, 160),
        message: `${req.name} (${req.company}) asked: ${req.body}`.slice(0, 2000),
        priority: 'high', // push right away
        category: 'customers',
        channel: 'requests',
        groupKey: `requests/${req.id}`, // one group per request
        url: `https://shop.example.com/admin/requests/${req.id}`, // https only, shown as "Open link"
        metadata: { request_id: String(req.id) },
      },
      { idempotencyKey: `request-${req.id}` }, // same request, same key: never a duplicate
    )
    .catch((err) => console.warn('honk:', err.message)); // never fail the customer's request
}

Die Honk-Skala und Vorfälle

Ein Helfer pro Stufe. problem und recovery brauchen einen Gruppenschlüssel; die Entwarnung schließt die Episode, die das Problem eröffnet hat.

await honk.loud('Disk 91%', '/var on app-01', { groupKey: 'disk/app-01/var' });
await honk.light('Deploy started', 'v4.2.0 to production', { channel: 'deploys' });
await honk.problem('db/backup', 'Backup failed', 'pg_dump exited with 1');      // a long honk by default
await honk.recovery('db/backup', 'Backup OK', 'pg_dump finished in 41 s');      // a beep by default

Fehler

Jeder Fehler ist ein HonkError mit kind und retryable. Validierungsfehler sind Bugs; wiederholbare Fehler können mit demselben Schlüssel zurück in deine Queue.

import { HonkError, HonkValidationError } from 'honk-me';

try {
  await honk.send(msg, { idempotencyKey: `order-${order.id}-failed` });
} catch (err) {
  if (err instanceof HonkValidationError) logger.error(err.fields);   // a bug: don't retry
  else if (err instanceof HonkError && err.retryable) queue.retryLater(err.idempotencyKey, err.retryAfter);
  else throw err;
}

Bun und Deno

Bun: bun add honk-me, dann wie in Node verwenden. Deno importiert es von npm:

import { Honk } from 'npm:honk-me'; // Deno
const honk = new Honk({ url: Deno.env.get('HONK_URL')!, key: Deno.env.get('HONK_KEY')! });

Retries ohne Doppelversand

  • Jedes Senden hat einen Idempotency-Key: deinen oder eine neue UUIDv7. Jeder Retry nutzt denselben Schlüssel, und innerhalb von 24 Stunden beantwortet Honk eine erneut gesendete Anfrage mit der ursprünglichen ID und duplicate: true. Geht eine Antwort verloren, entsteht also nie eine zweite Nachricht.
  • Wiederholt werden nur Netzwerkfehler, Timeouts, 429 und 5xx, mit exponentiellem Backoff und vollem Jitter, nie früher als das Retry-After des Servers. Andere 4xx-Antworten werden nie wiederholt: Korrigiere stattdessen die Anfrage.
  • Jeder Versuch bricht nach 5 Sekunden ab, und nach insgesamt 30 Sekunden ist Schluss. Wäre die nötige Wartezeit länger, etwa bis ein Tageskontingent um Mitternacht zurückgesetzt wird, schlägt der Aufruf sofort fehl und sagt dir, wann du es erneut versuchen kannst.
  • Felder werden vor dem Senden geprüft, und alle ungültigen Felder werden auf einmal gemeldet.