Web Push and Expo Push
The app supports web push (browser) and Expo push (mobile) so users receive chat and call notifications when the tab or app is in the background. Both flows use the same backend pipeline: the API publishes chat/call events to RabbitMQ, and dedicated workers consume and send push to the recipient.
Overview
| Channel | Worker | Purpose |
|---|---|---|
| Web Push (browser) | WebPushNotificationWorker | Sends push to browser (Chrome, Firefox, etc.) via Web Push API; user subscribes in Settings → Notification. |
| Expo Push (mobile) | ExpoPushNotificationWorker | Sends push to mobile app via Expo Push Service; app registers Expo token with the API after login. |
Both workers consume the same messages from RabbitMQ (ChatMessage, IncomingCallPushMessage, and NotificationMessage for likes/comments/friends/etc.), so when someone sends a chat message, starts a call, or a social notification is created, the appropriate worker sends a push to the recipient’s device(s).
Parity notes (native): Expo chat DMs use the same sorted guid_guid conversation id as web push; system_event chat messages are skipped on both channels; Expo payloads include deepLink (bellamybook://…) for native tap routing. Invalid Expo tokens (DeviceNotRegistered) are deactivated. When the recipient is in Redis presence:online_users (SignalR connected), Expo skips chat/social push (calls still fire); set ExpoPush__SkipWhenUserOnline=false to disable. Social pushes may include Expo richContent.image when imageUrl is set.
What Must Be Running
For chat/call push to be delivered:
| Service | Purpose |
|---|---|
| RabbitMQ | Message broker; API publishes chat/call events here; workers consume from here. |
| API | When a user sends a message (or starts a call), API publishes to RabbitMQ; also stores push subscriptions (web) and Expo tokens (mobile). |
| MongoDB | API and workers use it (subscriptions and tokens). |
| WebPushNotificationWorker | Consumes from RabbitMQ and sends web push to recipient’s browser. |
| ExpoPushNotificationWorker | Consumes from RabbitMQ and sends push to recipient’s mobile app via Expo. |
If a worker shows “Bus started” but no push is received, ensure the API is running when the message or call is sent (the API publishes to RabbitMQ).
Web Push (browser)
User flow
- User logs in and goes to Settings → Notification.
- User clicks Enable notifications and allows the browser permission.
- The frontend registers the push subscription with the API (
POST /api/push-subscriptions). - When someone sends a chat message or starts a call, the API publishes to RabbitMQ; WebPushNotificationWorker consumes and sends a web push to the recipient’s browser.
- The recipient sees an OS/browser notification (e.g. “Someone: message preview” or incoming call).
Test notification (no worker)
The API can send a test push directly (no RabbitMQ/worker):
- In the app: Settings → Notification → Send test notification.
- Or call
POST /api/push-subscriptions/testwith a valid JWT.
This verifies subscription and permission; real chat/call push still requires the worker and RabbitMQ.
API endpoints (web push)
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/push-subscriptions | Register a push subscription (endpoint, p256dh, auth). |
| DELETE | /api/push-subscriptions | Unregister by endpoint. |
| POST | /api/push-subscriptions/renew | Renew subscription (e.g. after pushsubscriptionchange). |
| GET | /api/push-subscriptions | List current user’s subscriptions. |
| POST | /api/push-subscriptions/test | Send a test push (API sends directly). |
Run WebPushNotificationWorker
cd Src/backend/WebPushNotificationWorker
dotnet run
Use the same RabbitMQ and MongoDB (and WebPush VAPID keys) as in the API’s appsettings.Development.json.
Expo Push (mobile)
After members install Bellamy Book and sign in, the app registers an Expo push token with your API. Keep the Expo push worker running. See Mobile App.
User flow
- User installs the app, enters your API URL (
API_PUBLIC_URL— not a frontend-only host), and signs in. - The app requests notification permission and obtains an Expo push token.
- The app registers the token (
POST /api/push-subscriptions/expo). - Chat / call / social events → RabbitMQ → ExpoPushNotificationWorker → Expo Push Service.
- Tap opens the matching screen in the app.
API endpoints (Expo push)
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/push-subscriptions/expo | Register an Expo push token for the current user. |
| DELETE | /api/push-subscriptions/expo | Unregister Expo token (e.g. by token or device). |
Run ExpoPushNotificationWorker
cd Src/backend/ExpoPushNotificationWorker
dotnet run
Use the same RabbitMQ and MongoDB as the API.
Message flow (chat / call push)
- User A sends a message to User B (or starts a call to User B).
- API saves the message/call and publishes
ChatMessage(orIncomingCallPushMessage) to RabbitMQ. - WebPushNotificationWorker consumes the message and, if User B has web push subscriptions, sends a web push to each.
- ExpoPushNotificationWorker consumes the same message and, if User B has Expo tokens, sends push via Expo to each device.
- User B sees an OS notification (browser and/or mobile) when the tab/app is in the background.
Worker logs to look for:
- Web:
WebPush: Consuming chat message ...andWebPush: Sent chat push for message ... - Expo:
ExpoPush: Consuming chat message ...andExpoPush: Sent chat push for message ...
Configuration
Web Push (VAPID)
Configure in API and WebPushNotificationWorker (e.g. appsettings.json or appsettings.Development.json):
{
"WebPush": {
"PublicKey": "your-vapid-public-key",
"PrivateKey": "your-vapid-private-key"
}
}
The frontend needs the same PublicKey (e.g. VITE_VAPID_PUBLIC_KEY).
RabbitMQ and MongoDB
Workers must use the same RabbitMQ (host, vhost, credentials) and MongoDB (connection string, database name) as the API so they consume the same messages and read the same subscription/token data.
Troubleshooting
- Test notification works but chat/call push does not: Ensure RabbitMQ and the relevant worker (WebPushNotificationWorker and/or ExpoPushNotificationWorker) are running when the message or call is sent.
- Worker shows “Bus started” but no “Consuming chat message”: The API must be running when the sender submits the message; the API publishes to RabbitMQ.
- Web: no notification on Mac/Windows: Check OS notification settings for the browser (e.g. System Settings → Notifications → your browser → Allow). Allow notifications for the site in the browser (e.g. site settings for
localhostor your domain). Try sending the test with the tab in the background. - Expo: no notification on device: Ensure the app registered the Expo token with the API after login and that the device allows notifications for the app.
Related
- Mobile App – how members connect the native app to your instance; Expo push registration.
- Notifications Feature – In-app and real-time notifications (SignalR).
- Messaging Feature – Chat and call APIs and real-time behavior.
- Workers Architecture – WebPushNotificationWorker and ExpoPushNotificationWorker.