Node.js
Le paquet honk-me est un client TypeScript sans dépendance d’exécution, pour Node 18 et plus récent, Bun et Deno. Utilisez-le dans du code back-end, des fonctions et des scripts.
- Paquet
-
honk-mePublié - Code source
- github.com/honk-me/honk-node · Licence MIT
- Prérequis
- Node 18 ou plus récent (
fetchglobal), Bun ou Deno.
Installation
Créez ensuite un projet et une clé d’ingestion dans l’app web Honk.
npm install honk-me Une clé d’ingestion (honk_…) permet à quiconque la détient d’envoyer des messages dans son projet. Gardez-la sur vos serveurs, dans vos tâches et dans les secrets de votre CI, jamais dans un navigateur ni dans une app mobile ou de bureau.
Envoyer un événement
Honk.fromEnv() lit HONK_URL, HONK_KEY et les valeurs par défaut facultatives HONK_SOURCE, HONK_ENVIRONMENT et HONK_CHANNEL. En CommonJS, require('honk-me') fonctionne aussi.
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'); Recette : une demande client sur votre téléphone
Un groupe par demande et une clé d’idempotence stable : deux clients ne se retrouvent jamais dans la même notification, et un webhook relancé ne fait jamais vibrer deux fois. La promesse n’est pas attendue : un réseau lent ne ralentit jamais la requête du 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
} L’échelle Honk et les incidents
Un helper par niveau. problem et recovery nécessitent une clé de groupe ; le rétablissement clôt l’épisode ouvert par le problème.
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 Erreurs
Chaque erreur est une HonkError avec un kind et retryable. Les erreurs de validation sont des bugs ; les erreurs temporaires peuvent retourner dans votre file d’attente avec la même clé.
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 et Deno
Bun : bun add honk-me, puis utilisez-le comme dans Node. Deno l’importe depuis npm :
import { Honk } from 'npm:honk-me'; // Deno
const honk = new Honk({ url: Deno.env.get('HONK_URL')!, key: Deno.env.get('HONK_KEY')! }); Des nouvelles tentatives sans doublon
- Chaque envoi porte une
Idempotency-Key: la vôtre, ou un UUIDv7 généré pour l’occasion. La même clé est réutilisée à chaque nouvelle tentative, et pendant 24 heures Honk répond à une requête rejouée avec l’identifiant d’origine etduplicate: true: une réponse perdue ne crée jamais de second message. - Une nouvelle tentative n’a lieu qu’en cas d’erreur réseau, de délai dépassé, de
429ou de5xx, avec un backoff exponentiel et un jitter complet, jamais avant leRetry-Afterdu serveur. Les autres réponses4xxne donnent lieu à aucune nouvelle tentative : corrigez plutôt la requête. - Chaque tentative expire au bout de 5 secondes, et tout s’arrête au bout de 30 secondes au total. Une attente qui dépasserait cette échéance, comme un quota quotidien remis à zéro à minuit, échoue immédiatement et indique quand réessayer.
- Les champs sont vérifiés localement avant l’envoi, et tous les champs invalides sont signalés en une seule fois.