[{"data":1,"prerenderedAt":1842},["ShallowReactive",2],{"docs:/docs/guides/notifications":3},{"id":4,"title":5,"body":6,"description":1835,"extension":1836,"meta":1837,"navigation":93,"path":1838,"seo":1839,"stem":1840,"__hash__":1841},"docs/docs/guides/notifications.md","Real-time Notifications",{"type":7,"value":8,"toc":1823},"minimark",[9,13,17,22,25,29,55,287,292,317,570,581,585,588,1214,1218,1224,1578,1582,1611,1749,1753,1780,1784,1798,1802,1819],[10,11,5],"h1",{"id":12},"real-time-notifications",[14,15,16],"p",{},"Build instant notification systems that reach users in real-time.",[18,19,21],"h2",{"id":20},"overview","Overview",[14,23,24],{},"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.",[18,26,28],{"id":27},"app-setup","App Setup",[14,30,31,32,36,37,40,41,44,45,50,51,54],{},"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 ",[33,34,35],"code",{},"config.autoProvisionRooms: true"," on the app. Use a ",[33,38,39],{},"service"," actor for the server that sends and a ",[33,42,43],{},"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 ",[46,47,49],"a",{"href":48},"/docs/authentication#api-keys","API key"," (",[33,52,53],{},"nlg_live_...","), created once in the portal.",[56,57,58],"code-tabs",{},[59,60,66],"pre",{"className":61,"code":62,"filename":63,"language":64,"meta":65,"style":65},"language-typescript shiki shiki-themes github-light github-dark","import { NoLagApi } from '@nolag/js-sdk'\n\nconst api = new NoLagApi(process.env.NOLAG_API_KEY) // nlg_live_...\n\n// 1. Create the app, declare its topics, and allow runtime room creation.\n//    The slug that comes back has a random suffix, so read it from the response.\nconst app = await api.apps.create({\n  name: 'Notifications',\n  slug: 'notifications',\n  topics: ['notifications'],\n  config: { autoProvisionRooms: true },\n})\nconsole.log(app.slug) // e.g. \"notifications-a3f9\": this is your APP_SLUG\n\n// 2. A service actor for the server that sends. Its access token is shown once; keep it.\nconst sender = await api.actors.create({ name: 'notification-service', actorType: 'service' })\nconsole.log(sender.accessToken)\n","Setup (server side, once)","typescript","",[33,67,68,88,95,128,133,139,145,167,179,190,201,213,219,234,239,245,277],{"__ignoreMap":65},[69,70,73,77,81,84],"span",{"class":71,"line":72},"line",1,[69,74,76],{"class":75},"szBVR","import",[69,78,80],{"class":79},"sVt8B"," { NoLagApi } ",[69,82,83],{"class":75},"from",[69,85,87],{"class":86},"sZZnC"," '@nolag/js-sdk'\n",[69,89,91],{"class":71,"line":90},2,[69,92,94],{"emptyLinePlaceholder":93},true,"\n",[69,96,98,101,105,108,111,115,118,121,124],{"class":71,"line":97},3,[69,99,100],{"class":75},"const",[69,102,104],{"class":103},"sj4cs"," api",[69,106,107],{"class":75}," =",[69,109,110],{"class":75}," new",[69,112,114],{"class":113},"sScJk"," NoLagApi",[69,116,117],{"class":79},"(process.env.",[69,119,120],{"class":103},"NOLAG_API_KEY",[69,122,123],{"class":79},") ",[69,125,127],{"class":126},"sJ8bj","// nlg_live_...\n",[69,129,131],{"class":71,"line":130},4,[69,132,94],{"emptyLinePlaceholder":93},[69,134,136],{"class":71,"line":135},5,[69,137,138],{"class":126},"// 1. Create the app, declare its topics, and allow runtime room creation.\n",[69,140,142],{"class":71,"line":141},6,[69,143,144],{"class":126},"//    The slug that comes back has a random suffix, so read it from the response.\n",[69,146,148,150,153,155,158,161,164],{"class":71,"line":147},7,[69,149,100],{"class":75},[69,151,152],{"class":103}," app",[69,154,107],{"class":75},[69,156,157],{"class":75}," await",[69,159,160],{"class":79}," api.apps.",[69,162,163],{"class":113},"create",[69,165,166],{"class":79},"({\n",[69,168,170,173,176],{"class":71,"line":169},8,[69,171,172],{"class":79},"  name: ",[69,174,175],{"class":86},"'Notifications'",[69,177,178],{"class":79},",\n",[69,180,182,185,188],{"class":71,"line":181},9,[69,183,184],{"class":79},"  slug: ",[69,186,187],{"class":86},"'notifications'",[69,189,178],{"class":79},[69,191,193,196,198],{"class":71,"line":192},10,[69,194,195],{"class":79},"  topics: [",[69,197,187],{"class":86},[69,199,200],{"class":79},"],\n",[69,202,204,207,210],{"class":71,"line":203},11,[69,205,206],{"class":79},"  config: { autoProvisionRooms: ",[69,208,209],{"class":103},"true",[69,211,212],{"class":79}," },\n",[69,214,216],{"class":71,"line":215},12,[69,217,218],{"class":79},"})\n",[69,220,222,225,228,231],{"class":71,"line":221},13,[69,223,224],{"class":79},"console.",[69,226,227],{"class":113},"log",[69,229,230],{"class":79},"(app.slug) ",[69,232,233],{"class":126},"// e.g. \"notifications-a3f9\": this is your APP_SLUG\n",[69,235,237],{"class":71,"line":236},14,[69,238,94],{"emptyLinePlaceholder":93},[69,240,242],{"class":71,"line":241},15,[69,243,244],{"class":126},"// 2. A service actor for the server that sends. Its access token is shown once; keep it.\n",[69,246,248,250,253,255,257,260,262,265,268,271,274],{"class":71,"line":247},16,[69,249,100],{"class":75},[69,251,252],{"class":103}," sender",[69,254,107],{"class":75},[69,256,157],{"class":75},[69,258,259],{"class":79}," api.actors.",[69,261,163],{"class":113},[69,263,264],{"class":79},"({ name: ",[69,266,267],{"class":86},"'notification-service'",[69,269,270],{"class":79},", actorType: ",[69,272,273],{"class":86},"'service'",[69,275,276],{"class":79}," })\n",[69,278,280,282,284],{"class":71,"line":279},17,[69,281,224],{"class":79},[69,283,227],{"class":113},[69,285,286],{"class":79},"(sender.accessToken)\n",[288,289,291],"h3",{"id":290},"one-room-per-user","One room per user",[14,293,294,295,297,298,301,302,304,305,308,309,312,313,316],{},"Create a user's room when their account is created, before their client first connects, and create a ",[33,296,43],{}," actor for them at the same time. ",[33,299,300],{},"POST /v1/apps/{appId}/rooms/ensure"," is an idempotent create-if-missing; it is only allowed because the app has ",[33,303,35],{}," (set at creation as above, or later with ",[33,306,307],{},"api.apps.update(app.appId, { config: { autoProvisionRooms: true } })","), and is capped per app. Subscribing to ",[33,310,311],{},"user-123"," before the room exists fails with ",[33,314,315],{},"unknown_topic"," (42940) and nothing is delivered.",[56,318,319,467],{},[59,320,323],{"className":61,"code":321,"filename":322,"language":64,"meta":65,"style":65},"// When a user account is created (userId = '123'). `app` and `api` are from App Setup.\nconst room = await api.rooms.ensure(app.appId, { name: `User ${userId}`, slug: `user-${userId}` })\nconst user = await api.actors.create({ name: `user-${userId}`, actorType: 'user', externalId: userId })\nconsole.log(user.accessToken) // this user's token; shown once\n\n// Optional: make the room private. The first grant closes the room to everyone\n// else, so grant the user's actor read access and your service actors write access.\nawait api.rooms.grantActor(app.appId, room.roomId, { actorTokenId: user.actorTokenId, permission: 'subscribe' })\nawait api.rooms.grantActor(app.appId, room.roomId, { actorType: 'service', permission: 'publish' })\n","TypeScript",[33,324,325,330,371,402,414,418,423,428,446],{"__ignoreMap":65},[69,326,327],{"class":71,"line":72},[69,328,329],{"class":126},"// When a user account is created (userId = '123'). `app` and `api` are from App Setup.\n",[69,331,332,334,337,339,341,344,347,350,353,356,359,362,365,367,369],{"class":71,"line":90},[69,333,100],{"class":75},[69,335,336],{"class":103}," room",[69,338,107],{"class":75},[69,340,157],{"class":75},[69,342,343],{"class":79}," api.rooms.",[69,345,346],{"class":113},"ensure",[69,348,349],{"class":79},"(app.appId, { name: ",[69,351,352],{"class":86},"`User ${",[69,354,355],{"class":79},"userId",[69,357,358],{"class":86},"}`",[69,360,361],{"class":79},", slug: ",[69,363,364],{"class":86},"`user-${",[69,366,355],{"class":79},[69,368,358],{"class":86},[69,370,276],{"class":79},[69,372,373,375,378,380,382,384,386,388,390,392,394,396,399],{"class":71,"line":97},[69,374,100],{"class":75},[69,376,377],{"class":103}," user",[69,379,107],{"class":75},[69,381,157],{"class":75},[69,383,259],{"class":79},[69,385,163],{"class":113},[69,387,264],{"class":79},[69,389,364],{"class":86},[69,391,355],{"class":79},[69,393,358],{"class":86},[69,395,270],{"class":79},[69,397,398],{"class":86},"'user'",[69,400,401],{"class":79},", externalId: userId })\n",[69,403,404,406,408,411],{"class":71,"line":130},[69,405,224],{"class":79},[69,407,227],{"class":113},[69,409,410],{"class":79},"(user.accessToken) ",[69,412,413],{"class":126},"// this user's token; shown once\n",[69,415,416],{"class":71,"line":135},[69,417,94],{"emptyLinePlaceholder":93},[69,419,420],{"class":71,"line":141},[69,421,422],{"class":126},"// Optional: make the room private. The first grant closes the room to everyone\n",[69,424,425],{"class":71,"line":147},[69,426,427],{"class":126},"// else, so grant the user's actor read access and your service actors write access.\n",[69,429,430,433,435,438,441,444],{"class":71,"line":169},[69,431,432],{"class":75},"await",[69,434,343],{"class":79},[69,436,437],{"class":113},"grantActor",[69,439,440],{"class":79},"(app.appId, room.roomId, { actorTokenId: user.actorTokenId, permission: ",[69,442,443],{"class":86},"'subscribe'",[69,445,276],{"class":79},[69,447,448,450,452,454,457,459,462,465],{"class":71,"line":181},[69,449,432],{"class":75},[69,451,343],{"class":79},[69,453,437],{"class":113},[69,455,456],{"class":79},"(app.appId, room.roomId, { actorType: ",[69,458,273],{"class":86},[69,460,461],{"class":79},", permission: ",[69,463,464],{"class":86},"'publish'",[69,466,276],{"class":79},[59,468,473],{"className":469,"code":470,"filename":471,"language":472,"meta":65,"style":65},"language-bash shiki shiki-themes github-light github-dark","# When a user account is created. Idempotent: returns the existing room on a slug match.\ncurl -X POST https://api.nolag.app/v1/apps/$APP_ID/rooms/ensure \\\n  -H \"Authorization: Bearer nlg_live_xxx.secret\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"User 123\",\"slug\":\"user-123\"}'\n\ncurl -X POST https://api.nolag.app/v1/actors \\\n  -H \"Authorization: Bearer nlg_live_xxx.secret\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"user-123\",\"actorType\":\"user\",\"externalId\":\"123\"}'\n","Terminal","bash",[33,474,475,480,503,513,522,530,534,547,555,563],{"__ignoreMap":65},[69,476,477],{"class":71,"line":72},[69,478,479],{"class":126},"# When a user account is created. Idempotent: returns the existing room on a slug match.\n",[69,481,482,485,488,491,494,497,500],{"class":71,"line":90},[69,483,484],{"class":113},"curl",[69,486,487],{"class":103}," -X",[69,489,490],{"class":86}," POST",[69,492,493],{"class":86}," https://api.nolag.app/v1/apps/",[69,495,496],{"class":79},"$APP_ID",[69,498,499],{"class":86},"/rooms/ensure",[69,501,502],{"class":103}," \\\n",[69,504,505,508,511],{"class":71,"line":97},[69,506,507],{"class":103},"  -H",[69,509,510],{"class":86}," \"Authorization: Bearer nlg_live_xxx.secret\"",[69,512,502],{"class":103},[69,514,515,517,520],{"class":71,"line":130},[69,516,507],{"class":103},[69,518,519],{"class":86}," \"Content-Type: application/json\"",[69,521,502],{"class":103},[69,523,524,527],{"class":71,"line":135},[69,525,526],{"class":103},"  -d",[69,528,529],{"class":86}," '{\"name\":\"User 123\",\"slug\":\"user-123\"}'\n",[69,531,532],{"class":71,"line":141},[69,533,94],{"emptyLinePlaceholder":93},[69,535,536,538,540,542,545],{"class":71,"line":147},[69,537,484],{"class":113},[69,539,487],{"class":103},[69,541,490],{"class":86},[69,543,544],{"class":86}," https://api.nolag.app/v1/actors",[69,546,502],{"class":103},[69,548,549,551,553],{"class":71,"line":169},[69,550,507],{"class":103},[69,552,510],{"class":86},[69,554,502],{"class":103},[69,556,557,559,561],{"class":71,"line":181},[69,558,507],{"class":103},[69,560,519],{"class":86},[69,562,502],{"class":103},[69,564,565,567],{"class":71,"line":192},[69,566,526],{"class":103},[69,568,569],{"class":86}," '{\"name\":\"user-123\",\"actorType\":\"user\",\"externalId\":\"123\"}'\n",[14,571,572,573,575,576,580],{},"A room with no grants is open to every actor in the app, so without the optional grants any actor could subscribe to ",[33,574,311],{},". See ",[46,577,579],{"href":578},"/docs/concepts/acl","Access Control"," for how grants work.",[18,582,584],{"id":583},"client-setup","Client Setup",[14,586,587],{},"Each user's client connects with that user's actor token and subscribes to its own room.",[56,589,590,903,1016],{},[59,591,593],{"className":61,"code":592,"filename":322,"language":64,"meta":65,"style":65},"import { NoLag } from '@nolag/js-sdk'\n\nconst APP_SLUG = 'notifications-a3f9' // the slug returned when you created the app\n\ninterface AppNotification {\n  title: string\n  body: string\n  icon: string\n  action: string\n}\n\nconst client = NoLag('user_123_access_token') // this user's actor token\nclient.on('error', (err) => console.error(err)) // unknown_topic etc. arrive here\nawait client.connect()\n\n// Subscribe to user-specific notifications\nconst userRoom = client.setApp(APP_SLUG).setRoom(`user-${userId}`)\nuserRoom.subscribe('notifications')\n\n// Handle incoming notifications. Payloads arrive as `unknown`; name the shape you expect.\nuserRoom.on\u003CAppNotification>('notifications', (notification) => {\n  showNotification({\n    title: notification.title,\n    body: notification.body,\n    icon: notification.icon,\n    action: notification.action\n  })\n})\n",[33,594,595,606,610,625,629,640,652,661,670,679,684,688,711,747,760,764,769,805,820,825,831,860,868,874,880,886,892,898],{"__ignoreMap":65},[69,596,597,599,602,604],{"class":71,"line":72},[69,598,76],{"class":75},[69,600,601],{"class":79}," { NoLag } ",[69,603,83],{"class":75},[69,605,87],{"class":86},[69,607,608],{"class":71,"line":90},[69,609,94],{"emptyLinePlaceholder":93},[69,611,612,614,617,619,622],{"class":71,"line":97},[69,613,100],{"class":75},[69,615,616],{"class":103}," APP_SLUG",[69,618,107],{"class":75},[69,620,621],{"class":86}," 'notifications-a3f9'",[69,623,624],{"class":126}," // the slug returned when you created the app\n",[69,626,627],{"class":71,"line":130},[69,628,94],{"emptyLinePlaceholder":93},[69,630,631,634,637],{"class":71,"line":135},[69,632,633],{"class":75},"interface",[69,635,636],{"class":113}," AppNotification",[69,638,639],{"class":79}," {\n",[69,641,642,646,649],{"class":71,"line":141},[69,643,645],{"class":644},"s4XuR","  title",[69,647,648],{"class":75},":",[69,650,651],{"class":103}," string\n",[69,653,654,657,659],{"class":71,"line":147},[69,655,656],{"class":644},"  body",[69,658,648],{"class":75},[69,660,651],{"class":103},[69,662,663,666,668],{"class":71,"line":169},[69,664,665],{"class":644},"  icon",[69,667,648],{"class":75},[69,669,651],{"class":103},[69,671,672,675,677],{"class":71,"line":181},[69,673,674],{"class":644},"  action",[69,676,648],{"class":75},[69,678,651],{"class":103},[69,680,681],{"class":71,"line":192},[69,682,683],{"class":79},"}\n",[69,685,686],{"class":71,"line":203},[69,687,94],{"emptyLinePlaceholder":93},[69,689,690,692,695,697,700,703,706,708],{"class":71,"line":215},[69,691,100],{"class":75},[69,693,694],{"class":103}," client",[69,696,107],{"class":75},[69,698,699],{"class":113}," NoLag",[69,701,702],{"class":79},"(",[69,704,705],{"class":86},"'user_123_access_token'",[69,707,123],{"class":79},[69,709,710],{"class":126},"// this user's actor token\n",[69,712,713,716,719,721,724,727,730,732,735,738,741,744],{"class":71,"line":221},[69,714,715],{"class":79},"client.",[69,717,718],{"class":113},"on",[69,720,702],{"class":79},[69,722,723],{"class":86},"'error'",[69,725,726],{"class":79},", (",[69,728,729],{"class":644},"err",[69,731,123],{"class":79},[69,733,734],{"class":75},"=>",[69,736,737],{"class":79}," console.",[69,739,740],{"class":113},"error",[69,742,743],{"class":79},"(err)) ",[69,745,746],{"class":126},"// unknown_topic etc. arrive here\n",[69,748,749,751,754,757],{"class":71,"line":236},[69,750,432],{"class":75},[69,752,753],{"class":79}," client.",[69,755,756],{"class":113},"connect",[69,758,759],{"class":79},"()\n",[69,761,762],{"class":71,"line":241},[69,763,94],{"emptyLinePlaceholder":93},[69,765,766],{"class":71,"line":247},[69,767,768],{"class":126},"// Subscribe to user-specific notifications\n",[69,770,771,773,776,778,780,783,785,788,791,794,796,798,800,802],{"class":71,"line":279},[69,772,100],{"class":75},[69,774,775],{"class":103}," userRoom",[69,777,107],{"class":75},[69,779,753],{"class":79},[69,781,782],{"class":113},"setApp",[69,784,702],{"class":79},[69,786,787],{"class":103},"APP_SLUG",[69,789,790],{"class":79},").",[69,792,793],{"class":113},"setRoom",[69,795,702],{"class":79},[69,797,364],{"class":86},[69,799,355],{"class":79},[69,801,358],{"class":86},[69,803,804],{"class":79},")\n",[69,806,808,811,814,816,818],{"class":71,"line":807},18,[69,809,810],{"class":79},"userRoom.",[69,812,813],{"class":113},"subscribe",[69,815,702],{"class":79},[69,817,187],{"class":86},[69,819,804],{"class":79},[69,821,823],{"class":71,"line":822},19,[69,824,94],{"emptyLinePlaceholder":93},[69,826,828],{"class":71,"line":827},20,[69,829,830],{"class":126},"// Handle incoming notifications. Payloads arrive as `unknown`; name the shape you expect.\n",[69,832,834,836,838,841,844,847,849,851,854,856,858],{"class":71,"line":833},21,[69,835,810],{"class":79},[69,837,718],{"class":113},[69,839,840],{"class":79},"\u003C",[69,842,843],{"class":113},"AppNotification",[69,845,846],{"class":79},">(",[69,848,187],{"class":86},[69,850,726],{"class":79},[69,852,853],{"class":644},"notification",[69,855,123],{"class":79},[69,857,734],{"class":75},[69,859,639],{"class":79},[69,861,863,866],{"class":71,"line":862},22,[69,864,865],{"class":113},"  showNotification",[69,867,166],{"class":79},[69,869,871],{"class":71,"line":870},23,[69,872,873],{"class":79},"    title: notification.title,\n",[69,875,877],{"class":71,"line":876},24,[69,878,879],{"class":79},"    body: notification.body,\n",[69,881,883],{"class":71,"line":882},25,[69,884,885],{"class":79},"    icon: notification.icon,\n",[69,887,889],{"class":71,"line":888},26,[69,890,891],{"class":79},"    action: notification.action\n",[69,893,895],{"class":71,"line":894},27,[69,896,897],{"class":79},"  })\n",[69,899,901],{"class":71,"line":900},28,[69,902,218],{"class":79},[59,904,909],{"className":905,"code":906,"filename":907,"language":908,"meta":65,"style":65},"language-python shiki shiki-themes github-light github-dark","from nolag import NoLag\n\nAPP_SLUG = 'notifications-a3f9'  # the slug returned when you created the app\n\nclient = NoLag('user_123_access_token')  # this user's actor token\nclient.on('error', lambda err: print(err))  # unknown_topic etc. arrive here\nawait client.connect()\n\n# Subscribe to user-specific notifications\nuser_room = client.set_app(APP_SLUG).set_room(f'user-{user_id}')\nawait user_room.subscribe('notifications')\n\n# Handle incoming notifications\ndef on_notification(notification, meta):\n    show_notification({\n        'title': notification['title'],\n        'body': notification['body'],\n        'icon': notification['icon'],\n        'action': notification['action']\n    })\n\nuser_room.on('notifications', on_notification)\n","Python","python",[33,910,911,916,920,925,929,934,939,944,948,953,958,963,967,972,977,982,987,992,997,1002,1007,1011],{"__ignoreMap":65},[69,912,913],{"class":71,"line":72},[69,914,915],{},"from nolag import NoLag\n",[69,917,918],{"class":71,"line":90},[69,919,94],{"emptyLinePlaceholder":93},[69,921,922],{"class":71,"line":97},[69,923,924],{},"APP_SLUG = 'notifications-a3f9'  # the slug returned when you created the app\n",[69,926,927],{"class":71,"line":130},[69,928,94],{"emptyLinePlaceholder":93},[69,930,931],{"class":71,"line":135},[69,932,933],{},"client = NoLag('user_123_access_token')  # this user's actor token\n",[69,935,936],{"class":71,"line":141},[69,937,938],{},"client.on('error', lambda err: print(err))  # unknown_topic etc. arrive here\n",[69,940,941],{"class":71,"line":147},[69,942,943],{},"await client.connect()\n",[69,945,946],{"class":71,"line":169},[69,947,94],{"emptyLinePlaceholder":93},[69,949,950],{"class":71,"line":181},[69,951,952],{},"# Subscribe to user-specific notifications\n",[69,954,955],{"class":71,"line":192},[69,956,957],{},"user_room = client.set_app(APP_SLUG).set_room(f'user-{user_id}')\n",[69,959,960],{"class":71,"line":203},[69,961,962],{},"await user_room.subscribe('notifications')\n",[69,964,965],{"class":71,"line":215},[69,966,94],{"emptyLinePlaceholder":93},[69,968,969],{"class":71,"line":221},[69,970,971],{},"# Handle incoming notifications\n",[69,973,974],{"class":71,"line":236},[69,975,976],{},"def on_notification(notification, meta):\n",[69,978,979],{"class":71,"line":241},[69,980,981],{},"    show_notification({\n",[69,983,984],{"class":71,"line":247},[69,985,986],{},"        'title': notification['title'],\n",[69,988,989],{"class":71,"line":279},[69,990,991],{},"        'body': notification['body'],\n",[69,993,994],{"class":71,"line":807},[69,995,996],{},"        'icon': notification['icon'],\n",[69,998,999],{"class":71,"line":822},[69,1000,1001],{},"        'action': notification['action']\n",[69,1003,1004],{"class":71,"line":827},[69,1005,1006],{},"    })\n",[69,1008,1009],{"class":71,"line":833},[69,1010,94],{"emptyLinePlaceholder":93},[69,1012,1013],{"class":71,"line":862},[69,1014,1015],{},"user_room.on('notifications', on_notification)\n",[59,1017,1022],{"className":1018,"code":1019,"filename":1020,"language":1021,"meta":65,"style":65},"language-go shiki shiki-themes github-light github-dark","package main\n\nimport (\n    \"fmt\"\n    \"log\"\n\n    nolag \"github.com/NoLagApp/go-sdk\"\n)\n\nconst appSlug = \"notifications-a3f9\" // the slug returned when you created the app\n\nfunc main() {\n    client := nolag.New(\"user_123_access_token\") // this user's actor token\n    client.OnError(func(err *nolag.ServerError) {\n        log.Println(err) // unknown_topic etc. arrive here\n    })\n    if err := client.Connect(); err != nil {\n        log.Fatal(err)\n    }\n\n    // Subscribe to user-specific notifications\n    userRoom := client.SetApp(appSlug).SetRoom(fmt.Sprintf(\"user-%s\", userID))\n    userRoom.Subscribe(\"notifications\", func(data any, meta nolag.MessageMeta) {\n        notification, _ := data.(map[string]any)\n        title, _ := notification[\"title\"].(string)\n        body, _ := notification[\"body\"].(string)\n        icon, _ := notification[\"icon\"].(string)\n        action, _ := notification[\"action\"].(string)\n        showNotification(Notification{\n            Title:  title,\n            Body:   body,\n            Icon:   icon,\n            Action: action,\n        })\n    })\n\n    select {} // keep the process alive\n}\n","Go","go",[33,1023,1024,1029,1033,1038,1043,1048,1052,1057,1061,1065,1070,1074,1079,1084,1089,1094,1098,1103,1108,1113,1117,1122,1127,1132,1137,1142,1147,1152,1157,1163,1169,1175,1181,1187,1193,1198,1203,1209],{"__ignoreMap":65},[69,1025,1026],{"class":71,"line":72},[69,1027,1028],{},"package main\n",[69,1030,1031],{"class":71,"line":90},[69,1032,94],{"emptyLinePlaceholder":93},[69,1034,1035],{"class":71,"line":97},[69,1036,1037],{},"import (\n",[69,1039,1040],{"class":71,"line":130},[69,1041,1042],{},"    \"fmt\"\n",[69,1044,1045],{"class":71,"line":135},[69,1046,1047],{},"    \"log\"\n",[69,1049,1050],{"class":71,"line":141},[69,1051,94],{"emptyLinePlaceholder":93},[69,1053,1054],{"class":71,"line":147},[69,1055,1056],{},"    nolag \"github.com/NoLagApp/go-sdk\"\n",[69,1058,1059],{"class":71,"line":169},[69,1060,804],{},[69,1062,1063],{"class":71,"line":181},[69,1064,94],{"emptyLinePlaceholder":93},[69,1066,1067],{"class":71,"line":192},[69,1068,1069],{},"const appSlug = \"notifications-a3f9\" // the slug returned when you created the app\n",[69,1071,1072],{"class":71,"line":203},[69,1073,94],{"emptyLinePlaceholder":93},[69,1075,1076],{"class":71,"line":215},[69,1077,1078],{},"func main() {\n",[69,1080,1081],{"class":71,"line":221},[69,1082,1083],{},"    client := nolag.New(\"user_123_access_token\") // this user's actor token\n",[69,1085,1086],{"class":71,"line":236},[69,1087,1088],{},"    client.OnError(func(err *nolag.ServerError) {\n",[69,1090,1091],{"class":71,"line":241},[69,1092,1093],{},"        log.Println(err) // unknown_topic etc. arrive here\n",[69,1095,1096],{"class":71,"line":247},[69,1097,1006],{},[69,1099,1100],{"class":71,"line":279},[69,1101,1102],{},"    if err := client.Connect(); err != nil {\n",[69,1104,1105],{"class":71,"line":807},[69,1106,1107],{},"        log.Fatal(err)\n",[69,1109,1110],{"class":71,"line":822},[69,1111,1112],{},"    }\n",[69,1114,1115],{"class":71,"line":827},[69,1116,94],{"emptyLinePlaceholder":93},[69,1118,1119],{"class":71,"line":833},[69,1120,1121],{},"    // Subscribe to user-specific notifications\n",[69,1123,1124],{"class":71,"line":862},[69,1125,1126],{},"    userRoom := client.SetApp(appSlug).SetRoom(fmt.Sprintf(\"user-%s\", userID))\n",[69,1128,1129],{"class":71,"line":870},[69,1130,1131],{},"    userRoom.Subscribe(\"notifications\", func(data any, meta nolag.MessageMeta) {\n",[69,1133,1134],{"class":71,"line":876},[69,1135,1136],{},"        notification, _ := data.(map[string]any)\n",[69,1138,1139],{"class":71,"line":882},[69,1140,1141],{},"        title, _ := notification[\"title\"].(string)\n",[69,1143,1144],{"class":71,"line":888},[69,1145,1146],{},"        body, _ := notification[\"body\"].(string)\n",[69,1148,1149],{"class":71,"line":894},[69,1150,1151],{},"        icon, _ := notification[\"icon\"].(string)\n",[69,1153,1154],{"class":71,"line":900},[69,1155,1156],{},"        action, _ := notification[\"action\"].(string)\n",[69,1158,1160],{"class":71,"line":1159},29,[69,1161,1162],{},"        showNotification(Notification{\n",[69,1164,1166],{"class":71,"line":1165},30,[69,1167,1168],{},"            Title:  title,\n",[69,1170,1172],{"class":71,"line":1171},31,[69,1173,1174],{},"            Body:   body,\n",[69,1176,1178],{"class":71,"line":1177},32,[69,1179,1180],{},"            Icon:   icon,\n",[69,1182,1184],{"class":71,"line":1183},33,[69,1185,1186],{},"            Action: action,\n",[69,1188,1190],{"class":71,"line":1189},34,[69,1191,1192],{},"        })\n",[69,1194,1196],{"class":71,"line":1195},35,[69,1197,1006],{},[69,1199,1201],{"class":71,"line":1200},36,[69,1202,94],{"emptyLinePlaceholder":93},[69,1204,1206],{"class":71,"line":1205},37,[69,1207,1208],{},"    select {} // keep the process alive\n",[69,1210,1212],{"class":71,"line":1211},38,[69,1213,683],{},[18,1215,1217],{"id":1216},"sending-notifications-server","Sending Notifications (Server)",[14,1219,1220,1221,1223],{},"The server connects with the ",[33,1222,39],{}," actor and publishes into the user's room.",[56,1225,1226,1384,1461],{},[59,1227,1229],{"className":61,"code":1228,"filename":322,"language":64,"meta":65,"style":65},"import { NoLag } from '@nolag/js-sdk'\n\nconst APP_SLUG = 'notifications-a3f9' // the slug returned when you created the app\n\n// Server-side: connect with the service actor and send a notification to user 123\nconst client = NoLag('notification_service_access_token')\nawait client.connect()\n\nconst userRoom = client.setApp(APP_SLUG).setRoom('user-123')\n\nuserRoom.emit('notifications', {\n  title: 'New Message',\n  body: 'You have a new message from Alice',\n  icon: 'message',\n  action: '/messages/456'\n})\n",[33,1230,1231,1241,1245,1257,1261,1266,1283,1293,1297,1324,1328,1342,1352,1362,1372,1380],{"__ignoreMap":65},[69,1232,1233,1235,1237,1239],{"class":71,"line":72},[69,1234,76],{"class":75},[69,1236,601],{"class":79},[69,1238,83],{"class":75},[69,1240,87],{"class":86},[69,1242,1243],{"class":71,"line":90},[69,1244,94],{"emptyLinePlaceholder":93},[69,1246,1247,1249,1251,1253,1255],{"class":71,"line":97},[69,1248,100],{"class":75},[69,1250,616],{"class":103},[69,1252,107],{"class":75},[69,1254,621],{"class":86},[69,1256,624],{"class":126},[69,1258,1259],{"class":71,"line":130},[69,1260,94],{"emptyLinePlaceholder":93},[69,1262,1263],{"class":71,"line":135},[69,1264,1265],{"class":126},"// Server-side: connect with the service actor and send a notification to user 123\n",[69,1267,1268,1270,1272,1274,1276,1278,1281],{"class":71,"line":141},[69,1269,100],{"class":75},[69,1271,694],{"class":103},[69,1273,107],{"class":75},[69,1275,699],{"class":113},[69,1277,702],{"class":79},[69,1279,1280],{"class":86},"'notification_service_access_token'",[69,1282,804],{"class":79},[69,1284,1285,1287,1289,1291],{"class":71,"line":147},[69,1286,432],{"class":75},[69,1288,753],{"class":79},[69,1290,756],{"class":113},[69,1292,759],{"class":79},[69,1294,1295],{"class":71,"line":169},[69,1296,94],{"emptyLinePlaceholder":93},[69,1298,1299,1301,1303,1305,1307,1309,1311,1313,1315,1317,1319,1322],{"class":71,"line":181},[69,1300,100],{"class":75},[69,1302,775],{"class":103},[69,1304,107],{"class":75},[69,1306,753],{"class":79},[69,1308,782],{"class":113},[69,1310,702],{"class":79},[69,1312,787],{"class":103},[69,1314,790],{"class":79},[69,1316,793],{"class":113},[69,1318,702],{"class":79},[69,1320,1321],{"class":86},"'user-123'",[69,1323,804],{"class":79},[69,1325,1326],{"class":71,"line":192},[69,1327,94],{"emptyLinePlaceholder":93},[69,1329,1330,1332,1335,1337,1339],{"class":71,"line":203},[69,1331,810],{"class":79},[69,1333,1334],{"class":113},"emit",[69,1336,702],{"class":79},[69,1338,187],{"class":86},[69,1340,1341],{"class":79},", {\n",[69,1343,1344,1347,1350],{"class":71,"line":215},[69,1345,1346],{"class":79},"  title: ",[69,1348,1349],{"class":86},"'New Message'",[69,1351,178],{"class":79},[69,1353,1354,1357,1360],{"class":71,"line":221},[69,1355,1356],{"class":79},"  body: ",[69,1358,1359],{"class":86},"'You have a new message from Alice'",[69,1361,178],{"class":79},[69,1363,1364,1367,1370],{"class":71,"line":236},[69,1365,1366],{"class":79},"  icon: ",[69,1368,1369],{"class":86},"'message'",[69,1371,178],{"class":79},[69,1373,1374,1377],{"class":71,"line":241},[69,1375,1376],{"class":79},"  action: ",[69,1378,1379],{"class":86},"'/messages/456'\n",[69,1381,1382],{"class":71,"line":247},[69,1383,218],{"class":79},[59,1385,1387],{"className":905,"code":1386,"filename":907,"language":908,"meta":65,"style":65},"from nolag import NoLag\n\nAPP_SLUG = 'notifications-a3f9'  # the slug returned when you created the app\n\n# Server-side: connect with the service actor and send a notification to user 123\nclient = NoLag('notification_service_access_token')\nawait client.connect()\n\nuser_room = client.set_app(APP_SLUG).set_room('user-123')\n\nawait user_room.emit('notifications', {\n    'title': 'New Message',\n    'body': 'You have a new message from Alice',\n    'icon': 'message',\n    'action': '/messages/456'\n})\n",[33,1388,1389,1393,1397,1401,1405,1410,1415,1419,1423,1428,1432,1437,1442,1447,1452,1457],{"__ignoreMap":65},[69,1390,1391],{"class":71,"line":72},[69,1392,915],{},[69,1394,1395],{"class":71,"line":90},[69,1396,94],{"emptyLinePlaceholder":93},[69,1398,1399],{"class":71,"line":97},[69,1400,924],{},[69,1402,1403],{"class":71,"line":130},[69,1404,94],{"emptyLinePlaceholder":93},[69,1406,1407],{"class":71,"line":135},[69,1408,1409],{},"# Server-side: connect with the service actor and send a notification to user 123\n",[69,1411,1412],{"class":71,"line":141},[69,1413,1414],{},"client = NoLag('notification_service_access_token')\n",[69,1416,1417],{"class":71,"line":147},[69,1418,943],{},[69,1420,1421],{"class":71,"line":169},[69,1422,94],{"emptyLinePlaceholder":93},[69,1424,1425],{"class":71,"line":181},[69,1426,1427],{},"user_room = client.set_app(APP_SLUG).set_room('user-123')\n",[69,1429,1430],{"class":71,"line":192},[69,1431,94],{"emptyLinePlaceholder":93},[69,1433,1434],{"class":71,"line":203},[69,1435,1436],{},"await user_room.emit('notifications', {\n",[69,1438,1439],{"class":71,"line":215},[69,1440,1441],{},"    'title': 'New Message',\n",[69,1443,1444],{"class":71,"line":221},[69,1445,1446],{},"    'body': 'You have a new message from Alice',\n",[69,1448,1449],{"class":71,"line":236},[69,1450,1451],{},"    'icon': 'message',\n",[69,1453,1454],{"class":71,"line":241},[69,1455,1456],{},"    'action': '/messages/456'\n",[69,1458,1459],{"class":71,"line":247},[69,1460,218],{},[59,1462,1464],{"className":1018,"code":1463,"filename":1020,"language":1021,"meta":65,"style":65},"package main\n\nimport (\n    \"log\"\n\n    nolag \"github.com/NoLagApp/go-sdk\"\n)\n\nconst appSlug = \"notifications-a3f9\" // the slug returned when you created the app\n\nfunc main() {\n    // Server-side: connect with the service actor and send a notification to user 123\n    client := nolag.New(\"notification_service_access_token\")\n    if err := client.Connect(); err != nil {\n        log.Fatal(err)\n    }\n\n    userRoom := client.SetApp(appSlug).SetRoom(\"user-123\")\n\n    userRoom.Emit(\"notifications\", map[string]any{\n        \"title\":  \"New Message\",\n        \"body\":   \"You have a new message from Alice\",\n        \"icon\":   \"message\",\n        \"action\": \"/messages/456\",\n    })\n}\n",[33,1465,1466,1470,1474,1478,1482,1486,1490,1494,1498,1502,1506,1510,1515,1520,1524,1528,1532,1536,1541,1545,1550,1555,1560,1565,1570,1574],{"__ignoreMap":65},[69,1467,1468],{"class":71,"line":72},[69,1469,1028],{},[69,1471,1472],{"class":71,"line":90},[69,1473,94],{"emptyLinePlaceholder":93},[69,1475,1476],{"class":71,"line":97},[69,1477,1037],{},[69,1479,1480],{"class":71,"line":130},[69,1481,1047],{},[69,1483,1484],{"class":71,"line":135},[69,1485,94],{"emptyLinePlaceholder":93},[69,1487,1488],{"class":71,"line":141},[69,1489,1056],{},[69,1491,1492],{"class":71,"line":147},[69,1493,804],{},[69,1495,1496],{"class":71,"line":169},[69,1497,94],{"emptyLinePlaceholder":93},[69,1499,1500],{"class":71,"line":181},[69,1501,1069],{},[69,1503,1504],{"class":71,"line":192},[69,1505,94],{"emptyLinePlaceholder":93},[69,1507,1508],{"class":71,"line":203},[69,1509,1078],{},[69,1511,1512],{"class":71,"line":215},[69,1513,1514],{},"    // Server-side: connect with the service actor and send a notification to user 123\n",[69,1516,1517],{"class":71,"line":221},[69,1518,1519],{},"    client := nolag.New(\"notification_service_access_token\")\n",[69,1521,1522],{"class":71,"line":236},[69,1523,1102],{},[69,1525,1526],{"class":71,"line":241},[69,1527,1107],{},[69,1529,1530],{"class":71,"line":247},[69,1531,1112],{},[69,1533,1534],{"class":71,"line":279},[69,1535,94],{"emptyLinePlaceholder":93},[69,1537,1538],{"class":71,"line":807},[69,1539,1540],{},"    userRoom := client.SetApp(appSlug).SetRoom(\"user-123\")\n",[69,1542,1543],{"class":71,"line":822},[69,1544,94],{"emptyLinePlaceholder":93},[69,1546,1547],{"class":71,"line":827},[69,1548,1549],{},"    userRoom.Emit(\"notifications\", map[string]any{\n",[69,1551,1552],{"class":71,"line":833},[69,1553,1554],{},"        \"title\":  \"New Message\",\n",[69,1556,1557],{"class":71,"line":862},[69,1558,1559],{},"        \"body\":   \"You have a new message from Alice\",\n",[69,1561,1562],{"class":71,"line":870},[69,1563,1564],{},"        \"icon\":   \"message\",\n",[69,1566,1567],{"class":71,"line":876},[69,1568,1569],{},"        \"action\": \"/messages/456\",\n",[69,1571,1572],{"class":71,"line":882},[69,1573,1006],{},[69,1575,1576],{"class":71,"line":888},[69,1577,683],{},[18,1579,1581],{"id":1580},"push-for-offline-users","Push for Offline Users",[14,1583,1584,1585,1589,1590,1594,1595,1598,1599,1602,1603,1606,1607,1610],{},"A notification published while the user is disconnected is not held for them: an ordinary reconnect restores subscriptions and replays nothing (see ",[46,1586,1588],{"href":1587},"/docs/concepts/replay","Replay","). To reach offline users, configure a ",[46,1591,1593],{"href":1592},"/docs/concepts/webhooks","trigger webhook",". The broker POSTs ",[33,1596,1597],{},"{ roomName, topicName, actorId, data, scope }"," to it on every publish, and your endpoint can send a push notification. ",[33,1600,1601],{},"triggerWebhook"," is the app-wide default; ",[33,1604,1605],{},"topicConfigs.notifications.webhooks.onPublish"," overrides it for the ",[33,1608,1609],{},"notifications"," topic alone and wins when both are set.",[56,1612,1613],{},[59,1614,1617],{"className":61,"code":1615,"filename":1616,"language":64,"meta":65,"style":65},"import { NoLagApi } from '@nolag/js-sdk'\n\nconst api = new NoLagApi(process.env.NOLAG_API_KEY) // nlg_live_...\n\n// `app` is the App returned by api.apps.create in App Setup\nawait api.apps.update(app.appId, {\n  // App-wide default, used by every topic without its own override\n  triggerWebhook: {\n    url: 'https://api.example.com/trigger',\n    headers: { Authorization: 'Bearer xxx' }\n  },\n  // Per-topic override: wins over the app-wide default for `notifications`\n  topicConfigs: {\n    notifications: {\n      webhooks: { onPublish: { url: 'https://api.example.com/push' } }\n    }\n  }\n})\n","Configure (server side, once)",[33,1618,1619,1629,1633,1653,1657,1662,1674,1679,1684,1694,1705,1710,1715,1720,1725,1736,1740,1745],{"__ignoreMap":65},[69,1620,1621,1623,1625,1627],{"class":71,"line":72},[69,1622,76],{"class":75},[69,1624,80],{"class":79},[69,1626,83],{"class":75},[69,1628,87],{"class":86},[69,1630,1631],{"class":71,"line":90},[69,1632,94],{"emptyLinePlaceholder":93},[69,1634,1635,1637,1639,1641,1643,1645,1647,1649,1651],{"class":71,"line":97},[69,1636,100],{"class":75},[69,1638,104],{"class":103},[69,1640,107],{"class":75},[69,1642,110],{"class":75},[69,1644,114],{"class":113},[69,1646,117],{"class":79},[69,1648,120],{"class":103},[69,1650,123],{"class":79},[69,1652,127],{"class":126},[69,1654,1655],{"class":71,"line":130},[69,1656,94],{"emptyLinePlaceholder":93},[69,1658,1659],{"class":71,"line":135},[69,1660,1661],{"class":126},"// `app` is the App returned by api.apps.create in App Setup\n",[69,1663,1664,1666,1668,1671],{"class":71,"line":141},[69,1665,432],{"class":75},[69,1667,160],{"class":79},[69,1669,1670],{"class":113},"update",[69,1672,1673],{"class":79},"(app.appId, {\n",[69,1675,1676],{"class":71,"line":147},[69,1677,1678],{"class":126},"  // App-wide default, used by every topic without its own override\n",[69,1680,1681],{"class":71,"line":169},[69,1682,1683],{"class":79},"  triggerWebhook: {\n",[69,1685,1686,1689,1692],{"class":71,"line":181},[69,1687,1688],{"class":79},"    url: ",[69,1690,1691],{"class":86},"'https://api.example.com/trigger'",[69,1693,178],{"class":79},[69,1695,1696,1699,1702],{"class":71,"line":192},[69,1697,1698],{"class":79},"    headers: { Authorization: ",[69,1700,1701],{"class":86},"'Bearer xxx'",[69,1703,1704],{"class":79}," }\n",[69,1706,1707],{"class":71,"line":203},[69,1708,1709],{"class":79},"  },\n",[69,1711,1712],{"class":71,"line":215},[69,1713,1714],{"class":126},"  // Per-topic override: wins over the app-wide default for `notifications`\n",[69,1716,1717],{"class":71,"line":221},[69,1718,1719],{"class":79},"  topicConfigs: {\n",[69,1721,1722],{"class":71,"line":236},[69,1723,1724],{"class":79},"    notifications: {\n",[69,1726,1727,1730,1733],{"class":71,"line":241},[69,1728,1729],{"class":79},"      webhooks: { onPublish: { url: ",[69,1731,1732],{"class":86},"'https://api.example.com/push'",[69,1734,1735],{"class":79}," } }\n",[69,1737,1738],{"class":71,"line":247},[69,1739,1112],{"class":79},[69,1741,1742],{"class":71,"line":279},[69,1743,1744],{"class":79},"  }\n",[69,1746,1747],{"class":71,"line":807},[69,1748,218],{"class":79},[18,1750,1752],{"id":1751},"notification-types","Notification Types",[1754,1755,1756,1764,1774],"ul",{},[1757,1758,1759,1763],"li",{},[1760,1761,1762],"strong",{},"User notifications"," - Personal notifications, one room per user as above",[1757,1765,1766,1769,1770,1773],{},[1760,1767,1768],{},"Broadcast notifications"," - Announcements to all users: one shared room, such as ",[33,1771,1772],{},"broadcast",", that every client also subscribes to",[1757,1775,1776,1779],{},[1760,1777,1778],{},"Group notifications"," - Messages to specific user groups: one room per group, ensured when the group is created",[18,1781,1783],{"id":1782},"best-practices","Best Practices",[1754,1785,1786,1789,1792,1795],{},[1757,1787,1788],{},"Use one room per user for private notifications, ensured when the account is created, and grant access to close it to other actors",[1757,1790,1791],{},"Include action URLs for clickable notifications",[1757,1793,1794],{},"Consider notification preferences and quiet hours",[1757,1796,1797],{},"Use a trigger webhook to also send push notifications to offline users",[18,1799,1801],{"id":1800},"next-steps","Next Steps",[1754,1803,1804,1809,1813],{},[1757,1805,1806],{},[46,1807,1808],{"href":1592},"Webhooks for push notifications",[1757,1810,1811],{},[46,1812,579],{"href":578},[1757,1814,1815],{},[46,1816,1818],{"href":1817},"/docs/concepts/rooms","Rooms",[1820,1821,1822],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":65,"searchDepth":90,"depth":90,"links":1824},[1825,1826,1829,1830,1831,1832,1833,1834],{"id":20,"depth":90,"text":21},{"id":27,"depth":90,"text":28,"children":1827},[1828],{"id":290,"depth":97,"text":291},{"id":583,"depth":90,"text":584},{"id":1216,"depth":90,"text":1217},{"id":1580,"depth":90,"text":1581},{"id":1751,"depth":90,"text":1752},{"id":1782,"depth":90,"text":1783},{"id":1800,"depth":90,"text":1801},"Build real-time notification systems with NoLag.","md",{},"/docs/guides/notifications",{"title":5,"description":1835},"docs/guides/notifications","h9nQffFG1lW-N8Ur-U_2wF9T8yUjjAmMotgF2xVMEZk",1789869420070]