Node.js
The honk-me package is a TypeScript client with no runtime dependencies, for Node 18 and later, Bun and Deno. Use it in back-end code, functions and scripts.
- Package
-
honk-mePublished - Source
- github.com/honk-me/honk-node · MIT License
- Requirements
- Node 18 or later (global
fetch), Bun or Deno.
Install
Then create a project and an ingestion key in the Honk web app.
npm install honk-me Anyone with an ingestion key (honk_…) can post to its project. Keep it on servers, in jobs and in CI secrets, never in a browser, mobile or desktop app.
Send an event
Honk.fromEnv() reads HONK_URL, HONK_KEY and the optional HONK_SOURCE, HONK_ENVIRONMENT and HONK_CHANNEL defaults. In CommonJS, require('honk-me') works too.
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'); Recipe: a customer request on your phone
One group per request and a stable idempotency key: two customers never fold into one notification, and a retried webhook never buzzes twice. The promise is not awaited, so a slow network never slows the customer’s request.
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
} The Honk scale and incidents
One helper per level. problem and recovery need a group key; the recovery closes the episode the problem opened.
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 Errors
Every error is a HonkError with a kind and retryable. Validation errors are bugs; retryable ones can go back to your queue with the same key.
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 and Deno
Bun: bun add honk-me and use it as in Node. Deno imports it from 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 that never send twice
- Every send carries an
Idempotency-Key: yours, or a fresh UUIDv7. The same key is reused on every retry, and within 24 hours Honk answers a replay with the original id andduplicate: true, so a lost response never turns into a second message. - Only network errors, timeouts,
429and5xxare retried, with exponential backoff and full jitter, never sooner than the server’sRetry-After. Other4xxresponses are never retried; fix the request instead. - Each attempt times out after 5 seconds and everything stops after 30. A wait that would cross that deadline, like a daily quota that resets at midnight, fails right away and tells you when to retry.
- Fields are validated before sending, and all invalid fields are reported together.