Real-time Notifications
Build instant notification systems that reach users in real-time.
Overview
NoLag makes it easy to deliver notifications instantly to users across all their devices. This guide covers setting up a notification system with one room per user.
App Setup
Rooms never exist implicitly, so create the app and its actors before any client connects. Per-user rooms are created at runtime, one for each user account, which needs config.autoProvisionRooms: true on the app. Use a service actor for the server that sends and a user actor for each user that reads: publishers never receive their own messages, so the two roles cannot share a token. The only bootstrap secret is a project-scoped API key (nlg_live_...), created once in the portal.
import { NoLagApi } from '@nolag/js-sdk'
const api = new NoLagApi(process.env.NOLAG_API_KEY) // nlg_live_...
// 1. Create the app, declare its topics, and allow runtime room creation.
// The slug that comes back has a random suffix, so read it from the response.
const app = await api.apps.create({
name: 'Notifications',
slug: 'notifications',
topics: ['notifications'],
config: { autoProvisionRooms: true },
})
console.log(app.slug) // e.g. "notifications-a3f9": this is your APP_SLUG
// 2. A service actor for the server that sends. Its access token is shown once; keep it.
const sender = await api.actors.create({ name: 'notification-service', actorType: 'service' })
console.log(sender.accessToken)One room per user
Create a user's room when their account is created, before their client first connects, and create a user actor for them at the same time. POST /v1/apps/{appId}/rooms/ensure is an idempotent create-if-missing; it is only allowed because the app has config.autoProvisionRooms: true (set at creation as above, or later with api.apps.update(app.appId, { config: { autoProvisionRooms: true } })), and is capped per app. Subscribing to user-123 before the room exists fails with unknown_topic (42940) and nothing is delivered.
// When a user account is created (userId = '123'). `app` and `api` are from App Setup.
const room = await api.rooms.ensure(app.appId, { name: `User ${userId}`, slug: `user-${userId}` })
const user = await api.actors.create({ name: `user-${userId}`, actorType: 'user', externalId: userId })
console.log(user.accessToken) // this user's token; shown once
// Optional: make the room private. The first grant closes the room to everyone
// else, so grant the user's actor read access and your service actors write access.
await api.rooms.grantActor(app.appId, room.roomId, { actorTokenId: user.actorTokenId, permission: 'subscribe' })
await api.rooms.grantActor(app.appId, room.roomId, { actorType: 'service', permission: 'publish' })A room with no grants is open to every actor in the app, so without the optional grants any actor could subscribe to user-123. See Access Control for how grants work.
Client Setup
Each user's client connects with that user's actor token and subscribes to its own room.
import { NoLag } from '@nolag/js-sdk'
const APP_SLUG = 'notifications-a3f9' // the slug returned when you created the app
interface AppNotification {
title: string
body: string
icon: string
action: string
}
const client = NoLag('user_123_access_token') // this user's actor token
client.on('error', (err) => console.error(err)) // unknown_topic etc. arrive here
await client.connect()
// Subscribe to user-specific notifications
const userRoom = client.setApp(APP_SLUG).setRoom(`user-${userId}`)
userRoom.subscribe('notifications')
// Handle incoming notifications. Payloads arrive as `unknown`; name the shape you expect.
userRoom.on<AppNotification>('notifications', (notification) => {
showNotification({
title: notification.title,
body: notification.body,
icon: notification.icon,
action: notification.action
})
})Sending Notifications (Server)
The server connects with the service actor and publishes into the user's room.
import { NoLag } from '@nolag/js-sdk'
const APP_SLUG = 'notifications-a3f9' // the slug returned when you created the app
// Server-side: connect with the service actor and send a notification to user 123
const client = NoLag('notification_service_access_token')
await client.connect()
const userRoom = client.setApp(APP_SLUG).setRoom('user-123')
userRoom.emit('notifications', {
title: 'New Message',
body: 'You have a new message from Alice',
icon: 'message',
action: '/messages/456'
})Push for Offline Users
A notification published while the user is disconnected is not held for them: an ordinary reconnect restores subscriptions and replays nothing (see Replay). To reach offline users, configure a trigger webhook. The broker POSTs { roomName, topicName, actorId, data, scope } to it on every publish, and your endpoint can send a push notification. triggerWebhook is the app-wide default; topicConfigs.notifications.webhooks.onPublish overrides it for the notifications topic alone and wins when both are set.
import { NoLagApi } from '@nolag/js-sdk'
const api = new NoLagApi(process.env.NOLAG_API_KEY) // nlg_live_...
// `app` is the App returned by api.apps.create in App Setup
await api.apps.update(app.appId, {
// App-wide default, used by every topic without its own override
triggerWebhook: {
url: 'https://api.example.com/trigger',
headers: { Authorization: 'Bearer xxx' }
},
// Per-topic override: wins over the app-wide default for `notifications`
topicConfigs: {
notifications: {
webhooks: { onPublish: { url: 'https://api.example.com/push' } }
}
}
})Notification Types
- User notifications - Personal notifications, one room per user as above
- Broadcast notifications - Announcements to all users: one shared room, such as
broadcast, that every client also subscribes to - Group notifications - Messages to specific user groups: one room per group, ensured when the group is created
Best Practices
- Use one room per user for private notifications, ensured when the account is created, and grant access to close it to other actors
- Include action URLs for clickable notifications
- Consider notification preferences and quiet hours
- Use a trigger webhook to also send push notifications to offline users