node-pushnotifications sends one API call to FCM, APN, WNS, ADM or Web Push
Push notifications for GCM, APNS, MPNS, AMZ (automatic detection from device token)
At a glance
- What is it?
- node-pushnotifications is a Node.js wrapper that detects which push service a device token belongs to and routes the message accordingly, so one send call covers Android, iOS, Windows, Amazon and browsers. It works on ES6 with a Babel build for ES5 targets, requires Node 20 or newer, and the token detection that makes the routing automatic is also its most fragile part.
- Who is it for?
- Adopt node-pushnotifications if your backend already serves iOS, Android and browser clients and you want one call site instead of five, since the object form of the registration id carries its own type and removes the guesswork. Do not adopt it if you only push to a single platform, because the underlying libraries are separately maintained and documented, and each one gives you more control than this wrapper exposes.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 80 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem is five push protocols behind one send call
node-pushnotifications is a Node.js module for interfacing with Apple Push Notification, Google Cloud Messaging, Windows Push Notification, Web Push and Amazon Device Messaging services. The README's own description of it in the package metadata is shorter: a cross-platform push service for node.js.
The duplication it removes is real. A service with an iOS app, an Android app and a web app would otherwise depend on a separate client library for each platform, each with its own credential handling, its own payload format and its own error shapes. This module wraps all of them behind one constructor and one send method, and the routing decision is made per message.
That routing is the headline feature and the README states it plainly: automatically detects destination device type. How it manages that is described as well, since the send method detects device type and therefore the push method used, based on the id structure, and the README points at the method PN.getPushMethodByRegId for the details.
There is also a manual override. The isAlwaysUseFCM setting, documented as sending all notifications through the FCM API instead of platform-specific services, exists because Firebase Cloud Messaging now reaches devices beyond Android. The README notes FCM options are used for Android and as a fallback for other platforms, which is the reason that override is more than a convenience.
The dependency list shows the shape of the wrapper. It is five libraries, one per platform: @parse/node-apn 8.1.0 for Apple, firebase-admin 13.10.0 for Google, wns 0.5.4 for Windows, node-adm 0.9.1 for Amazon and web-push 3.6.7 for the browser. The module does not implement a push protocol itself, and it does not abstract the payloads, which are still the ones each platform expects.
Settings are a single object with one block per platform
Setup is a constructor call with one settings object, and the README's advice is to include only the settings for the devices you expect to have. If an app is Android only, include fcm; if it is iOS only, include apn.
The FCM block is the most detailed, because Firebase has the most options:
fcm: {
appName: 'localFcmAppName',
serviceAccountKey: require('../firebase-project-service-account-key.json'), // firebase service-account-file.json,
credential: null, // 'firebase-admin' Credential interface
// Optional Firebase Admin SDK AppOptions
projectId: 'your-project-id', // Explicitly set the Google Cloud project ID
databaseURL: 'https://your-database.firebaseio.com', // Realtime Database URL (optional)
storageBucket: 'your-bucket.appspot.com', // Cloud Storage bucket (optional)
serviceAccountId: '[email protected]', // Service account email (optional)
httpAgent: undefined, // HTTP Agent for proxy support (optional)
httpsAgent: undefined, // HTTPS Agent for proxy support (optional)
},The comments in that block are the documentation. The serviceAccountKey is a Firebase service account file loaded with require, credential is a firebase-admin Credential interface or null, and projectId, databaseURL, storageBucket and serviceAccountId are the optional Firebase Admin SDK AppOptions. The httpAgent and httpsAgent entries are there for proxy support.
The APN block takes a token object with a key path, a keyId and a teamId, plus a production boolean that is false for the APN sandbox environment and true for production. The README recommends provider authentication tokens and the .p8 certificate from an Apple account membership, and notes that certificates can be used instead by preparing cert.pem and key.pem.
The WNS block carries client_id, client_secret and a notificationMethod, whose default in the example is sendTileSquareBlock, which is a Windows tile notification style rather than a raw toast. The ADM block has client_id and client_secret.
Web Push is the odd one out because it uses a different shape entirely, with vapidDetails holding a subject that is a mailto address or a URL plus a URL-safe Base64 public and private key, then a TTL of 2419200 seconds, a contentEncoding of aes128gcm and a headers object. The keys are optional but if you supply one it should be the whole set.
And the FCM override sits alongside them:
isAlwaysUseFCM: false, // true all messages will be sent through FCM API
};
const push = new PushNotifications(settings);Each platform block also carries an ellipsis in the README, meaning the upstream library's own options are accepted and passed through. The README links each one: firebase-admin for FCM options, node-apn for APN, node-adm for ADM, wns for Windows and web-push for the browser. That is where the full option surface lives, and it is where the wrapper's value drops off, because at that point you are configuring the underlying library through a pass-through.
The registration id is the routing key, and its object form matters
The second step in the README's usage guide is defining the destination device id, and the documentation spends real care on the format.
The recommended form is an object with an id and a type, where type is one of 'apn', 'fcm', 'adm', 'wns' or 'webPush'. The types are exported as constants:
import { WEB, WNS, ADM, FCM, APN } from 'node-pushnotifications';
const regId = {
id: 'INSERT_YOUR_DEVICE_ID',
type: APN,
};js import { WEB, WNS, ADM, FCM, APN } from 'node-pushnotifications';
const regId = { id: 'INSERT_YOUR_DEVICE_ID', type: APN,
{
"endpoint": "< Push Subscription URL >",
"keys": {
"p256dh": "< User Public Encryption Key >",
"auth": "< User Auth Secret >"
}
}json
The endpoint is the Push Subscription URL and the keys object holds the p256dh user public encryption key and the auth user auth secret, which is the structure web-push expects.
Sending to several devices is just an array of these ids, and the README states you can send to multiple devices independently of platform, creating an array with different destination device ids.
Installing is one npm command on Node 20 or newer
Installation is the standard npm install, and the only requirement worth calling out is the Node version:
npm install node-pushnotifications --saveNode version 20.x.x or newer is required. That is a recent floor for a library whose own version line is at 6, and it reflects that the underlying firebase-admin and node-apn releases it depends on are current.
The package metadata also shows how the module is built and tested, which matters if you are reading its source. The main entry is index.js, the build script runs Babel from src to lib, and there is a .babelrc plus @babel/preset-env in the dev dependencies, which is what the README's feature list refers to when it says the code is written in ES6 and compatible with ES5 through babel transpilation. The source lives in src/ and the tests in test/.
The scripts show a standard Node project workflow. The test command runs mocha against the test directory with @babel/register, the build script is a direct call to the Babel binary, lint runs eslint over src and test, and format runs prettier over JavaScript, JSON, Markdown and YAML. There is a ci script that builds, runs the coverage script and lints, and a prepublishOnly script that lints, tests and builds before publish. Releases go through release-it, with a .release.json configuration.
The proxy support deserves a mention because it is easy to miss in a push library. Both httpAgent and httpsAgent are accepted in the FCM block and documented as being for proxy support, which is the difference between a module that works in a locked down network and one that does not.
Where the wrapper is the wrong tool
The first limitation is that the routing logic is a heuristic dressed as a feature. The string registration id is the supported but not recommended fallback, kept for backwards compatibility, and it works by inspecting length and prefix. If a platform changes its token format, or if a token from one platform happens to look like another, messages go to the wrong service and the failure appears as a delivery error at the provider rather than as a routing error here.
The second limitation is the pass-through design. Every platform block ends in an ellipsis, meaning the upstream options go through untouched. That is the right call for coverage and the wrong call for control. When you need a specific FCM option or a specific APN payload field, you are reading the upstream library's documentation anyway, and at that point the wrapper saves you one import and one constructor call.
The third is that the abstraction stops at the transport. The module does not normalise payloads. An APN payload has an aps dictionary with a badge, a sound and an alert, a WNS payload has its own XML or JSON shape for tiles, and a Web Push payload is a JSON body with its own encryption settings. You still write one payload per platform, so a single send call does not mean a single message body.
The fourth is the platform coverage drift. The topics list in the repository metadata includes mpns, the Windows Phone push service, and the WNS section says Windows Push Notification. WNS is the current service and the dependency list contains wns 0.5.4, but a module that has carried a dead platform's name in its metadata for years is a small signal about how quickly the package vocabulary updates.
The alternative is to depend on the underlying libraries directly, and the README points at each of them by name. The difference in approach is explicit: firebase-admin, node-apn, wns, node-adm and web-push are the client libraries for their services, each with its own release cadence, its own documentation and its own error handling, while this module adds one thing on top, which is automatic routing from a device id. If your service pushes to exactly one platform, that one thing is worth nothing and you should take the upstream library directly, because you get its documentation, its type definitions and its error objects without a wrapper in between.
MIT licensed, actively released, with a six point zero major
The licence is MIT, recorded in the package metadata and with a LICENSE file at the repository root. That is a permissive grant with no copyleft obligation on code you build with it, so shipping node-pushnotifications inside a commercial service carries no licensing friction. That is a licensing note rather than legal advice.
The maintenance picture is current. The last push was on 2026-08-04 and the repository is not archived, and the release history shows 6.0.0 on 2026-05-05, 5.0.4 on 2026-04-18 and 5.0.3 on 2026-03-19. Three releases in a short window, with a major version landing in May, is a project that is being used and maintained.
The major version bump is worth understanding, because a 6.0.0 in a wrapper is usually a breaking change in the pass-through contract. The dependencies are pinned to exact versions rather than ranges, which is unusual and worth noting: @parse/node-apn at 8.1.0, firebase-admin at 13.10.0, node-adm at 0.9.1, web-push at 3.6.7 and wns at 0.5.4. Pinning means you get exactly the tested versions, and it also means you do not automatically receive upstream fixes until this module releases a new version.
That is the upgrade cost in one sentence. Your security and bug fixes for firebase-admin or web-push arrive on the wrapper's schedule, not your own, and because the pins are exact you cannot opt into a newer upstream without overriding the dependency yourself.
The project layout is conventional, with src/, test/, an index.js entry point, a .babelrc for the transpilation, eslint and prettier configuration, a .github directory for the workflows, and a .release.json for the release tooling. The CI badge and the coveralls badge are both in the README header, so the test and coverage story is visible without reading the source.
Editorial conclusion
Adopt node-pushnotifications if your backend already serves iOS, Android and browser clients and you want one call site instead of five, since the object form of the registration id carries its own type and removes the guesswork. Do not adopt it if you only push to a single platform, because the underlying libraries are separately maintained and documented, and each one gives you more control than this wrapper exposes. Verify first which registration id form your clients are actually storing, since the string form is kept only for backwards compatibility, that your FCM service account key or APN p8 file is in place, and that your Node runtime is 20 or newer.
Frequently asked questions
How do I install node-pushnotifications and send my first push?
Run npm install node-pushnotifications --save, then create a PushNotifications instance with a settings object containing the blocks for the platforms you support, and call send with a registration id and a payload. Node version 20.x.x or newer is required.
How does node-pushnotifications decide which push service to use?
The send method inspects the registration id structure and picks the platform service, with the logic in PN.getPushMethodByRegId. The recommended form is an object with an id and an explicit type of apn, fcm, adm, wns or webPush, exported as constants, while plain strings are detected by prefix and length and are not recommended.
Can I send every push through Firebase Cloud Messaging?
Yes. The isAlwaysUseFCM setting sends all notifications through the FCM API instead of the platform specific services, which is useful now that FCM reaches devices beyond Android. The README notes FCM options are used for Android and as a fallback for other platforms.
What credentials does node-pushnotifications need for each platform?
FCM takes a Firebase service account key file or a firebase-admin credential. APN takes a .p8 key path with a keyId and teamId, and the production flag chooses the sandbox or production environment. WNS and ADM take client_id and client_secret, and Web Push takes vapidDetails with a subject, a public key and a private key.
How do I send a browser push notification with node-pushnotifications?
Use a webPush registration id, which is a push subscription object rather than a token, with an endpoint and a keys object holding p256dh and auth. The web settings block also takes vapidDetails, a TTL, a contentEncoding of aes128gcm and optional headers.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/appfeel-node-pushnotifications)Was this useful?