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