@thinwrap/notifications
@thinwrap/notifications for TypeScript — unified facade over 35 notification providers across email, SMS, push, and chat. Stateless, zero runtime deps, BYO fetch.
TypeScript constraints
- Runtime dependencies
- Zero runtime dependencies
- BYO HTTP client
- injectable fetch parameter (Node 18+ native fetch)
- Provenance
- npm provenance + Sigstore
- Minimum runtime
- Node 18.20+
- Node 18 and 20 emit a one-time ExperimentalWarning on the first fetch() invocation: "ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time."
- The warning is informational only — fetch() in Node 18.20+ and Node 20.x is functionally identical to the stable fetch in Node 21+. Suppress with the NODE_NO_WARNINGS=1 env var or the --no-warnings flag if it clutters CI logs.
- Thinwrap does not patch around the warning, does not silence it on the consumer's behalf, and does not require a polyfill. It is surfaced exactly as Node's runtime surfaces it.
30-second install
npm install @thinwrap/notifications2-minute end-to-end example
Send an email through any supported provider with one normalized call. The example below uses SendGrid; switching providers means changing the provider ID and config — input and output shape stay identical.
import { Email } from '@thinwrap/notifications';
const email = new Email('sendgrid', {
apiKey: process.env.SENDGRID_API_KEY!,
from: 'you@example.com',
});
await email.send({
to: 'user@example.com',
subject: 'Hello',
text: 'Hello world',
});Same code works for all supported providers — change the provider ID and pass the matching config. The supported-providers list below is auto-sourced from the catalog and stays in lockstep with what the package actually exports.
See the universal email snippet for the full copy-paste pattern.
Supported providers
| Provider | Channel | Auth | README |
|---|---|---|---|
sendgrid SendGrid | bearer | docs | |
ses AWS SES | aws-sig-v4 | docs | |
mailgun Mailgun | basic | docs | |
postmark Postmark | header | docs | |
resend Resend | bearer | docs | |
brevo Brevo (Sendinblue) | header | docs | |
mailersend MailerSend | bearer | docs | |
mailtrap Mailtrap | bearer | docs | |
sparkpost SparkPost | header | docs | |
scaleway Scaleway | header | docs | |
twilio Twilio | sms | basic | docs |
vonage Vonage | sms | query | docs |
plivo Plivo | sms | basic | docs |
sns AWS SNS | sms | aws-sig-v4 | docs |
messagebird MessageBird (Bird) | sms | header | docs |
infobip Infobip | sms | header | docs |
telnyx Telnyx | sms | bearer | docs |
sinch Sinch | sms | bearer | docs |
textmagic Textmagic | sms | header | docs |
d7networks D7 Networks | sms | bearer | docs |
fcm Firebase Cloud Messaging | push | rs256-jwt | docs |
apns Apple Push Notifications | push | es256-jwt | docs |
expo Expo Push | push | bearer | docs |
one-signal OneSignal | push | header | docs |
pusher-beams Pusher Beams | push | bearer | docs |
wonderpush WonderPush | push | bearer | docs |
slack Slack | chat | webhook-url | docs |
discord Discord | chat | webhook-url | docs |
ms-teams Microsoft Teams | chat | webhook-url | docs |
telegram Telegram | chat | webhook-url | docs |
whatsapp-business WhatsApp Business | chat | bearer | docs |
line LINE | chat | bearer | docs |
google-chat Google Chat | chat | webhook-url | docs |
rocket-chat Rocket.Chat | chat | webhook-url | docs |
mattermost Mattermost | chat | webhook-url | docs |
Migration paths
From @novu/providers
Novu compatibility is design inspiration credit, not a critical path — the import change is usually one line:
- import { SendgridEmailProvider } from '@novu/providers';
+ import { Email } from '@thinwrap/notifications';
- const provider = new SendgridEmailProvider({ apiKey });
+ const provider = new Email('sendgrid', { apiKey, from });From @sendgrid/mail (or any vendor SDK)
Drop the vendor SDK; the wrapper produces the same outbound HTTP call with no transitive dependencies. Vendor-specific fields are accessible through the typed _passthrough escape valve.
- import sgMail from '@sendgrid/mail';
- sgMail.setApiKey(process.env.SENDGRID_API_KEY);
- await sgMail.send({ to, from, subject, text });
+ import { Email } from '@thinwrap/notifications';
+ const email = new Email('sendgrid', { apiKey: process.env.SENDGRID_API_KEY!, from });
+ await email.send({ to, subject, text });From hand-rolled fetch
Keep your retry/observability layer; replace the per-vendor request shape with a single uniform call. The wrapper performs no automatic retry — compose your own from ConnectorError.providerCodeand the raw Retry-After header on e.cause.