easy-pingv0.10.0

Get in touch

Questions, bug reports, or anything about easy-ping. Either of these reaches me.

Emailteklumo.jembere@gmail.comTelegram@teklumt

For anything others would benefit from, a GitHub issue is better than a DM, because it's searchable.

GitHub

Self-hosted notifications for TypeScript

Own your notifications.

In-app inbox, transactional email, web push and Telegram, running inside your app, stored in your database, with no per-notification bill. One config file, one send(), one hook.

$pnpm add easy-ping
  • v0.10.0
  • MIT
  • 1 runtime dep
  • no services to run
app/api/comments/route.ts
await notify.send("commentReply", {
  to: threadOwnerId,
  payload: { authorName: "Dana", commentId: "c_123" },
  // optional, makes a retry safe
  dedupeKey: `commentReply:c_123:${threadOwnerId}`,
});
delivery log
inAppwritten to your notification tablesent
emailresend · notifications@acme.devsent
pushweb-push · 2 devices · aes128gcmsent
telegram@acme_bot · 1 linked chatsent
send() returned in 6 msproviders ran after the response

Built for how you actually work

Your AI doesn't know this library yet.

easy-ping shipped after every current model's training cutoff. Ask Cursor or Claude to wire it up cold and you'll get a confident, fluent, completely invented API.

So we ship the context. Grab a prompt below, paste it in, and your assistant writes against the real surface instead of guessing at it.

without the context
notify.createNotification({ … })
// plausible. fluent. does not exist.
with it
await notify.send("commentReply", { to, payload })
// typed against your own definition

Why it exists

What you don't have to build

The parts everyone hand-rolls, gets subtly wrong, and then maintains forever.

Database

Your database, your schema

Postgres with any driver or Drizzle, MySQL, SQLite or MongoDB. One conformance suite, 87 cases, runs against every one of them.

Types

Payloads typed from the definition

send() won't compile with the wrong shape. No payload of type any, rotting for two years.

Latency

send() returns before the provider does

It writes its rows and returns. A 300 ms provider round trip has no business on a comment POST. Unless you pick 'inline', which awaits it on purpose.

Delivery

At-least-once delivery

Atomic claim, so two concurrent sweeps can't send the same email twice. Backoff with jitter, five attempts.

Push

Web push and native push

VAPID and aes128gcm on Web Crypto for browsers, Expo's push service for iOS and Android, with receipts that prune uninstalled devices.

Telegram

A bot the user connects with one tap

POST /telegram/link returns a t.me link; the user taps it and every send lands in their chat. Blocked bots prune themselves.

Realtime

A live bell without a socket server

One event stream per browser, other tabs mirror it, polling only as the fallback. Ten open tabs cost one connection.

Plugins

Preferences and digests

A per-type, per-channel preference center and timezone-aware digests, both as opt-in plugins.

How it compares

A library, not another service to run

The alternatives are good, and most of them are a platform. This one is a dependency, and that difference decides almost everything else.

easy-pinga libraryNovuself-hostedKnock · Courierhosted SaaS
What you deployNothing. It runs inside the app you already have.Four services. API, worker, dashboard and websocket.Nothing. It runs in their cloud.
Infrastructure it addsNoneMongoDB, Redis and S3, on top of your ownNone
Where notifications are storedYour Postgres, MySQL, SQLite or MongoDB, in tables you can queryNovu's own MongoDBTheir database, reachable by API
Cost per notificationNoneNone, but you pay for the serversMetered per send
LicenseMIT, all of itMIT core, commercial enterprise modulesProprietary
How you integrateOne config file, one send(), one hookHTTP API plus an embeddable inboxHTTP API plus an embeddable inbox
Visual workflow editorNo. A notification is code, reviewed like codeYesYes
Channels out of the boxIn-app, email, web push, mobile push, TelegramDozens of providers across every channelDozens of providers across every channel

Checked 22 September 2026 against Novu's repository and self-hosting guide, and the Knock and Courier pricing pages. They are good tools solving a bigger problem. If you need a workflow editor that a non-developer can edit, use one of them.

Delivery

Four ways to deliver, one durability floor

Modes are additive, not alternatives. The rows are committed before any of them runs, so a missing platform primitive or a crashed process costs latency, never a notification.

ModeDelivery happensLatencyNeeds cron
inlineBefore send() resolvesin-requestrecommended
deferredAfter the response, via waitUntil~1 syes
workerIn-process loop, woken by send()instantoptional
cronWhen the sweep runsup to the intervalyes

Ship it this afternoon.

Schema, config, one send(), and the bell. The quickstart is the whole thing, in order, with the gotchas called out.