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