Node.js

Pachetul honk-me este un client TypeScript fără dependențe la rulare, pentru Node 18 sau mai nou, Bun și Deno. Folosește-l în codul de backend, în funcții și în scripturi.

Pachet
honk-me Publicat
Cod sursă
github.com/honk-me/honk-node · Licență MIT
Cerințe
Node 18 sau mai nou (cu fetch global), Bun sau Deno.

Instalare

Apoi creează un proiect și o cheie API în aplicația web Honk.

Terminal
npm install honk-me

Oricine are o cheie API (honk_…) poate trimite mesaje în proiectul ei. Ține-o pe servere, în joburi și în secretele din CI, niciodată într-o aplicație de browser, de mobil sau de desktop.

Trimite un eveniment

Honk.fromEnv() citește HONK_URL, HONK_KEY și valorile implicite opționale HONK_SOURCE, HONK_ENVIRONMENT și HONK_CHANNEL. În CommonJS merge și 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');

Rețetă: cererea unui client, direct pe telefon

Un grup pentru fiecare cerere și o cheie de idempotență stabilă: doi clienți nu ajung niciodată în aceeași notificare, iar un webhook retrimis nu te anunță de două ori. Nu aștepți rezultatul trimiterii, așa că o rețea lentă nu întârzie niciodată răspunsul pentru client.

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
}

Scala Honk și incidentele

Câte un helper pentru fiecare nivel. problem și recovery au nevoie de o cheie de grup; revenirea închide episodul deschis de problemă.

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

Erori

Fiecare eroare este un HonkError cu kind și retryable. Erorile de validare sunt buguri; cele care se pot reîncerca pot reveni în coada ta cu aceeași cheie.

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 și Deno

Bun: bun add honk-me și folosește-l ca în Node. Deno îl importă din npm:

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

Reîncercări care nu trimit nimic de două ori

  • Fiecare trimitere are un Idempotency-Key: al tău sau un UUIDv7 nou. Aceeași cheie e refolosită la fiecare reîncercare, iar timp de 24 de ore Honk răspunde la o repetare cu id-ul original și duplicate: true, așa că un răspuns pierdut nu creează niciodată un al doilea mesaj.
  • Se reîncearcă doar erorile de rețea, cererile care expiră, 429 și 5xx, cu backoff exponențial și jitter complet, niciodată mai devreme decât Retry-After de la server. Celelalte răspunsuri 4xx nu se reîncearcă niciodată: corectează cererea.
  • Fiecare încercare expiră după 5 secunde, iar totul se oprește după 30. Dacă ar trebui să aștepte mai mult, de exemplu până se resetează cota zilnică la miezul nopții, eșuează imediat și îți spune când să reîncerci.
  • Câmpurile sunt verificate înainte de trimitere, iar toate câmpurile invalide sunt raportate deodată.