@videodock/push-service
v0.4.0
Published
Framework-agnostic push service helpers for typed subscriptions, storage, and delivery.
Readme
@videodock/push-service
Framework-agnostic helpers for typed push subscriptions, optional subscription storage, and provider-backed delivery.
Status
This package currently focuses on:
- subscribing a device token to typed subscriptions
- unsubscribing a device token from typed subscriptions
- optionally storing subscription state in a separate adapter
- sending push messages through a provider that maps subscriptions to its own delivery model
Install
npm install @videodock/push-service firebase-adminfirebase-admin is a peer dependency of this package.
Quick Start
import { initializeApp, applicationDefault } from "firebase-admin/app";
import {
FirebaseAdminPushProvider,
InMemoryNotificationsStore,
PushService
} from "@videodock/push-service";
const app = initializeApp({
credential: applicationDefault()
});
const pushService = new PushService({
provider: FirebaseAdminPushProvider.fromApp(app),
store: new InMemoryNotificationsStore()
});
await pushService.subscribe("device-token", [
{
type: "content"
},
{
type: "category",
subject: "announcements"
}
]);
await pushService.sendToSubscription(
{
type: "category",
subject: "announcements"
},
{
message: {
title: "Live now",
body: "A new update is available",
deepLink: "my-app://content/item-123"
}
}
);Subscription Model
Subscriptions are plain objects:
{
"subscriptions": [
{
"type": "content"
},
{
"type": "category",
"subject": "announcements"
}
]
}Rules are intentionally small:
typemust be a non-empty stringsubjectis optional and must be a non-empty string when present
The public API is provider-agnostic. FirebaseAdminPushProvider maps each subscription object to a Firebase topic internally.
Adapters
This package deliberately separates push delivery from subscription storage.
PushProvider: subscribe, unsubscribe, and send through FCM or another push backendNotificationsStore: read and persist current subscription state for a device token
That lets you combine:
FirebaseAdminPushProviderfor actual push subscription and deliveryInMemoryNotificationsStorefor tests or local stateMySqlNotificationsStorefor relational persistence
API
new PushService(options)
Creates a service around a provider implementation.
If you configure store, the service can also reconcile a full device state through syncDevice(...).
options:
provider: PushProviderstore?: NotificationsStore
pushService.subscribe(deviceToken, subscriptions)
Subscribes a device token to one or more subscriptions.
pushService.unsubscribe(deviceToken, subscriptions)
Unsubscribes a device token from one or more subscriptions.
pushService.sendToSubscription(subscription, { message })
Sends a push message to a single subscription.
pushService.trigger({ subscriptions, message })
Sends the same push message to one or more subscriptions.
pushService.getSubscriptions(deviceToken)
This method delegates to store.getSubscriptionsForDevice(deviceToken).
If no store is configured, this method throws.
pushService.syncDevice({ deviceToken, subscriptions, previousDeviceToken? })
Reconciles the provider state for a device token to the full desired subscription set.
- subscribes missing subscriptions for
deviceToken - unsubscribes stale subscriptions for
deviceToken - replaces the stored subscriptions for
deviceToken - if
previousDeviceTokenis provided and differs, unsubscribes that old token and removes its stored rows
This is the recommended method for app startup and token rotation.
The bundled FirebaseAdminPushProvider only handles push writes and delivery. Use a separate NotificationsStore if your application needs reads.
MySQL Store
MySqlNotificationsStore stores subscriptions in a relational table through a minimal MySQL client interface.
Recommended schema:
CREATE TABLE push_subscriptions (
device_token VARCHAR(255) NOT NULL,
subscription_type VARCHAR(191) NOT NULL,
subscription_subject VARCHAR(191) NULL
);Example:
import { createPool } from "mysql2/promise";
import { initializeApp, applicationDefault } from "firebase-admin/app";
import {
FirebaseAdminPushProvider,
MySqlNotificationsStore,
PushService
} from "@videodock/push-service";
const pool = createPool({
uri: process.env.DATABASE_URL
});
const app = initializeApp({
credential: applicationDefault()
});
const pushService = new PushService({
provider: FirebaseAdminPushProvider.fromApp(app),
store: new MySqlNotificationsStore({
client: pool
})
});Example Route Integration
const pushService = new PushService({
provider: FirebaseAdminPushProvider.fromApp(app),
store: new InMemoryNotificationsStore()
});
app.get("/notifications/:deviceToken", async (request) => {
return pushService.getSubscriptions(request.params.deviceToken);
});
app.post("/notifications/subscribe", async (request) => {
const { deviceToken, subscriptions } = request.body;
return pushService.subscribe(deviceToken, subscriptions);
});
app.post("/notifications/sync", async (request) => {
const { deviceToken, previousDeviceToken, subscriptions } = request.body;
return pushService.syncDevice({
deviceToken,
previousDeviceToken,
subscriptions
});
});