[{"data":1,"prerenderedAt":2753},["ShallowReactive",2],{"docs:/docs/guides/dashboards":3},{"id":4,"title":5,"body":6,"description":2746,"extension":2747,"meta":2748,"navigation":87,"path":2749,"seo":2750,"stem":2751,"__hash__":2752},"docs/docs/guides/dashboards.md","Live Dashboards",{"type":7,"value":8,"toc":2734},"minimark",[9,13,17,22,25,29,49,371,375,403,1340,1344,1347,1354,2100,2105,2115,2582,2612,2616,2644,2648,2663,2667,2707,2711,2730],[10,11,5],"h1",{"id":12},"live-dashboards",[14,15,16],"p",{},"Build real-time dashboards that update instantly as data changes.",[18,19,21],"h2",{"id":20},"overview","Overview",[14,23,24],{},"Real-time dashboards provide immediate visibility into your business metrics. With NoLag, you can push updates to dashboards the moment data changes, without polling or page refreshes.",[18,26,28],{"id":27},"app-setup","App Setup",[14,30,31,32,36,37,40,41,44,45,48],{},"Rooms never exist implicitly, so create the app, its rooms, and its actors before any client connects. This guide uses one app with two rooms: ",[33,34,35],"code",{},"live"," for metrics and ",[33,38,39],{},"ops"," for filtered booking updates. It also uses two actors: a ",[33,42,43],{},"service"," actor for the backend that publishes and a ",[33,46,47],{},"user"," actor for the dashboard that subscribes. Publishers never receive their own messages, so the two roles cannot share a token.",[50,51,52],"code-tabs",{},[53,54,60],"pre",{"className":55,"code":56,"filename":57,"language":58,"meta":59,"style":59},"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({\n  name: 'Analytics',\n  slug: 'analytics',\n  topics: ['page-views', 'active-users', 'sales', 'bookings'],\n})\nconsole.log(app.slug) // e.g. \"analytics-a3f9\": this is your APP_SLUG\n\n// 2. Create the rooms. Room slugs are kept exactly as you supply them.\nawait api.rooms.create(app.appId, { name: 'Live metrics', slug: 'live' })\nawait api.rooms.create(app.appId, { name: 'Operations', slug: 'ops' })\n\n// 3. One actor per role. Each access token is shown once; keep both.\nconst publisher = await api.actors.create({ name: 'metrics-service', actorType: 'service' })\nconst dashboard = await api.actors.create({ name: 'dashboard', actorType: 'user' })\nconsole.log(publisher.accessToken, dashboard.accessToken)\n","Setup (server side, once)","typescript","",[33,61,62,82,89,122,127,133,139,161,173,184,212,218,233,238,244,270,291,296,302,333,361],{"__ignoreMap":59},[63,64,67,71,75,78],"span",{"class":65,"line":66},"line",1,[63,68,70],{"class":69},"szBVR","import",[63,72,74],{"class":73},"sVt8B"," { NoLagApi } ",[63,76,77],{"class":69},"from",[63,79,81],{"class":80},"sZZnC"," '@nolag/js-sdk'\n",[63,83,85],{"class":65,"line":84},2,[63,86,88],{"emptyLinePlaceholder":87},true,"\n",[63,90,92,95,99,102,105,109,112,115,118],{"class":65,"line":91},3,[63,93,94],{"class":69},"const",[63,96,98],{"class":97},"sj4cs"," api",[63,100,101],{"class":69}," =",[63,103,104],{"class":69}," new",[63,106,108],{"class":107},"sScJk"," NoLagApi",[63,110,111],{"class":73},"(process.env.",[63,113,114],{"class":97},"NOLAG_API_KEY",[63,116,117],{"class":73},") ",[63,119,121],{"class":120},"sJ8bj","// nlg_live_...\n",[63,123,125],{"class":65,"line":124},4,[63,126,88],{"emptyLinePlaceholder":87},[63,128,130],{"class":65,"line":129},5,[63,131,132],{"class":120},"// 1. Create the app and declare the topics it allows. The slug that comes back\n",[63,134,136],{"class":65,"line":135},6,[63,137,138],{"class":120},"//    has a random suffix, so read it from the response.\n",[63,140,142,144,147,149,152,155,158],{"class":65,"line":141},7,[63,143,94],{"class":69},[63,145,146],{"class":97}," app",[63,148,101],{"class":69},[63,150,151],{"class":69}," await",[63,153,154],{"class":73}," api.apps.",[63,156,157],{"class":107},"create",[63,159,160],{"class":73},"({\n",[63,162,164,167,170],{"class":65,"line":163},8,[63,165,166],{"class":73},"  name: ",[63,168,169],{"class":80},"'Analytics'",[63,171,172],{"class":73},",\n",[63,174,176,179,182],{"class":65,"line":175},9,[63,177,178],{"class":73},"  slug: ",[63,180,181],{"class":80},"'analytics'",[63,183,172],{"class":73},[63,185,187,190,193,196,199,201,204,206,209],{"class":65,"line":186},10,[63,188,189],{"class":73},"  topics: [",[63,191,192],{"class":80},"'page-views'",[63,194,195],{"class":73},", ",[63,197,198],{"class":80},"'active-users'",[63,200,195],{"class":73},[63,202,203],{"class":80},"'sales'",[63,205,195],{"class":73},[63,207,208],{"class":80},"'bookings'",[63,210,211],{"class":73},"],\n",[63,213,215],{"class":65,"line":214},11,[63,216,217],{"class":73},"})\n",[63,219,221,224,227,230],{"class":65,"line":220},12,[63,222,223],{"class":73},"console.",[63,225,226],{"class":107},"log",[63,228,229],{"class":73},"(app.slug) ",[63,231,232],{"class":120},"// e.g. \"analytics-a3f9\": this is your APP_SLUG\n",[63,234,236],{"class":65,"line":235},13,[63,237,88],{"emptyLinePlaceholder":87},[63,239,241],{"class":65,"line":240},14,[63,242,243],{"class":120},"// 2. Create the rooms. Room slugs are kept exactly as you supply them.\n",[63,245,247,250,253,255,258,261,264,267],{"class":65,"line":246},15,[63,248,249],{"class":69},"await",[63,251,252],{"class":73}," api.rooms.",[63,254,157],{"class":107},[63,256,257],{"class":73},"(app.appId, { name: ",[63,259,260],{"class":80},"'Live metrics'",[63,262,263],{"class":73},", slug: ",[63,265,266],{"class":80},"'live'",[63,268,269],{"class":73}," })\n",[63,271,273,275,277,279,281,284,286,289],{"class":65,"line":272},16,[63,274,249],{"class":69},[63,276,252],{"class":73},[63,278,157],{"class":107},[63,280,257],{"class":73},[63,282,283],{"class":80},"'Operations'",[63,285,263],{"class":73},[63,287,288],{"class":80},"'ops'",[63,290,269],{"class":73},[63,292,294],{"class":65,"line":293},17,[63,295,88],{"emptyLinePlaceholder":87},[63,297,299],{"class":65,"line":298},18,[63,300,301],{"class":120},"// 3. One actor per role. Each access token is shown once; keep both.\n",[63,303,305,307,310,312,314,317,319,322,325,328,331],{"class":65,"line":304},19,[63,306,94],{"class":69},[63,308,309],{"class":97}," publisher",[63,311,101],{"class":69},[63,313,151],{"class":69},[63,315,316],{"class":73}," api.actors.",[63,318,157],{"class":107},[63,320,321],{"class":73},"({ name: ",[63,323,324],{"class":80},"'metrics-service'",[63,326,327],{"class":73},", actorType: ",[63,329,330],{"class":80},"'service'",[63,332,269],{"class":73},[63,334,336,338,341,343,345,347,349,351,354,356,359],{"class":65,"line":335},20,[63,337,94],{"class":69},[63,339,340],{"class":97}," dashboard",[63,342,101],{"class":69},[63,344,151],{"class":69},[63,346,316],{"class":73},[63,348,157],{"class":107},[63,350,321],{"class":73},[63,352,353],{"class":80},"'dashboard'",[63,355,327],{"class":73},[63,357,358],{"class":80},"'user'",[63,360,269],{"class":73},[63,362,364,366,368],{"class":65,"line":363},21,[63,365,223],{"class":73},[63,367,226],{"class":107},[63,369,370],{"class":73},"(publisher.accessToken, dashboard.accessToken)\n",[18,372,374],{"id":373},"dashboard-setup","Dashboard Setup",[14,376,377,378,380,381,384,385,388,389,392,393,384,396,399,400,402],{},"The dashboard connects with the ",[33,379,47],{}," actor and subscribes to the metric topics. Your backend publishes ",[33,382,383],{},"{ count, timestamp }"," to ",[33,386,387],{},"page-views"," and ",[33,390,391],{},"active-users",", and ",[33,394,395],{},"{ amount, timestamp }",[33,397,398],{},"sales",", with the ",[33,401,43],{}," actor.",[50,404,405,838,983],{},[53,406,409],{"className":55,"code":407,"filename":408,"language":58,"meta":59,"style":59},"import { NoLag } from '@nolag/js-sdk'\n\nconst APP_SLUG = 'analytics-a3f9' // the slug returned when you created the app\n\ninterface Metric {\n  count: number\n  timestamp: number\n}\n\ninterface Sale {\n  amount: number\n  timestamp: number\n}\n\nconst client = NoLag('dashboard_access_token')\nclient.on('error', (err) => console.error(err)) // unknown_topic etc. arrive here\nawait client.connect()\n\nconst dashboard = client.setApp(APP_SLUG).setRoom('live')\n\n// Subscribe to real-time metrics\ndashboard.subscribe('page-views')\ndashboard.subscribe('active-users')\ndashboard.subscribe('sales')\n\n// Update charts in real-time. Payloads arrive as `unknown`; name the shape you expect.\ndashboard.on\u003CMetric>('page-views', (data) => {\n  pageViewsChart.update(data.count, data.timestamp)\n})\n\ndashboard.on\u003CMetric>('active-users', (data) => {\n  activeUsersGauge.setValue(data.count)\n})\n\ndashboard.on\u003CSale>('sales', (data) => {\n  salesChart.addDataPoint(data.amount, data.timestamp)\n  totalSales.increment(data.amount)\n})\n","TypeScript",[33,410,411,422,426,441,445,456,468,477,482,486,495,504,512,516,520,541,577,590,594,624,628,633,648,661,674,679,685,714,726,731,736,761,773,778,783,809,821,833],{"__ignoreMap":59},[63,412,413,415,418,420],{"class":65,"line":66},[63,414,70],{"class":69},[63,416,417],{"class":73}," { NoLag } ",[63,419,77],{"class":69},[63,421,81],{"class":80},[63,423,424],{"class":65,"line":84},[63,425,88],{"emptyLinePlaceholder":87},[63,427,428,430,433,435,438],{"class":65,"line":91},[63,429,94],{"class":69},[63,431,432],{"class":97}," APP_SLUG",[63,434,101],{"class":69},[63,436,437],{"class":80}," 'analytics-a3f9'",[63,439,440],{"class":120}," // the slug returned when you created the app\n",[63,442,443],{"class":65,"line":124},[63,444,88],{"emptyLinePlaceholder":87},[63,446,447,450,453],{"class":65,"line":129},[63,448,449],{"class":69},"interface",[63,451,452],{"class":107}," Metric",[63,454,455],{"class":73}," {\n",[63,457,458,462,465],{"class":65,"line":135},[63,459,461],{"class":460},"s4XuR","  count",[63,463,464],{"class":69},":",[63,466,467],{"class":97}," number\n",[63,469,470,473,475],{"class":65,"line":141},[63,471,472],{"class":460},"  timestamp",[63,474,464],{"class":69},[63,476,467],{"class":97},[63,478,479],{"class":65,"line":163},[63,480,481],{"class":73},"}\n",[63,483,484],{"class":65,"line":175},[63,485,88],{"emptyLinePlaceholder":87},[63,487,488,490,493],{"class":65,"line":186},[63,489,449],{"class":69},[63,491,492],{"class":107}," Sale",[63,494,455],{"class":73},[63,496,497,500,502],{"class":65,"line":214},[63,498,499],{"class":460},"  amount",[63,501,464],{"class":69},[63,503,467],{"class":97},[63,505,506,508,510],{"class":65,"line":220},[63,507,472],{"class":460},[63,509,464],{"class":69},[63,511,467],{"class":97},[63,513,514],{"class":65,"line":235},[63,515,481],{"class":73},[63,517,518],{"class":65,"line":240},[63,519,88],{"emptyLinePlaceholder":87},[63,521,522,524,527,529,532,535,538],{"class":65,"line":246},[63,523,94],{"class":69},[63,525,526],{"class":97}," client",[63,528,101],{"class":69},[63,530,531],{"class":107}," NoLag",[63,533,534],{"class":73},"(",[63,536,537],{"class":80},"'dashboard_access_token'",[63,539,540],{"class":73},")\n",[63,542,543,546,549,551,554,557,560,562,565,568,571,574],{"class":65,"line":272},[63,544,545],{"class":73},"client.",[63,547,548],{"class":107},"on",[63,550,534],{"class":73},[63,552,553],{"class":80},"'error'",[63,555,556],{"class":73},", (",[63,558,559],{"class":460},"err",[63,561,117],{"class":73},[63,563,564],{"class":69},"=>",[63,566,567],{"class":73}," console.",[63,569,570],{"class":107},"error",[63,572,573],{"class":73},"(err)) ",[63,575,576],{"class":120},"// unknown_topic etc. arrive here\n",[63,578,579,581,584,587],{"class":65,"line":293},[63,580,249],{"class":69},[63,582,583],{"class":73}," client.",[63,585,586],{"class":107},"connect",[63,588,589],{"class":73},"()\n",[63,591,592],{"class":65,"line":298},[63,593,88],{"emptyLinePlaceholder":87},[63,595,596,598,600,602,604,607,609,612,615,618,620,622],{"class":65,"line":304},[63,597,94],{"class":69},[63,599,340],{"class":97},[63,601,101],{"class":69},[63,603,583],{"class":73},[63,605,606],{"class":107},"setApp",[63,608,534],{"class":73},[63,610,611],{"class":97},"APP_SLUG",[63,613,614],{"class":73},").",[63,616,617],{"class":107},"setRoom",[63,619,534],{"class":73},[63,621,266],{"class":80},[63,623,540],{"class":73},[63,625,626],{"class":65,"line":335},[63,627,88],{"emptyLinePlaceholder":87},[63,629,630],{"class":65,"line":363},[63,631,632],{"class":120},"// Subscribe to real-time metrics\n",[63,634,636,639,642,644,646],{"class":65,"line":635},22,[63,637,638],{"class":73},"dashboard.",[63,640,641],{"class":107},"subscribe",[63,643,534],{"class":73},[63,645,192],{"class":80},[63,647,540],{"class":73},[63,649,651,653,655,657,659],{"class":65,"line":650},23,[63,652,638],{"class":73},[63,654,641],{"class":107},[63,656,534],{"class":73},[63,658,198],{"class":80},[63,660,540],{"class":73},[63,662,664,666,668,670,672],{"class":65,"line":663},24,[63,665,638],{"class":73},[63,667,641],{"class":107},[63,669,534],{"class":73},[63,671,203],{"class":80},[63,673,540],{"class":73},[63,675,677],{"class":65,"line":676},25,[63,678,88],{"emptyLinePlaceholder":87},[63,680,682],{"class":65,"line":681},26,[63,683,684],{"class":120},"// Update charts in real-time. Payloads arrive as `unknown`; name the shape you expect.\n",[63,686,688,690,692,695,698,701,703,705,708,710,712],{"class":65,"line":687},27,[63,689,638],{"class":73},[63,691,548],{"class":107},[63,693,694],{"class":73},"\u003C",[63,696,697],{"class":107},"Metric",[63,699,700],{"class":73},">(",[63,702,192],{"class":80},[63,704,556],{"class":73},[63,706,707],{"class":460},"data",[63,709,117],{"class":73},[63,711,564],{"class":69},[63,713,455],{"class":73},[63,715,717,720,723],{"class":65,"line":716},28,[63,718,719],{"class":73},"  pageViewsChart.",[63,721,722],{"class":107},"update",[63,724,725],{"class":73},"(data.count, data.timestamp)\n",[63,727,729],{"class":65,"line":728},29,[63,730,217],{"class":73},[63,732,734],{"class":65,"line":733},30,[63,735,88],{"emptyLinePlaceholder":87},[63,737,739,741,743,745,747,749,751,753,755,757,759],{"class":65,"line":738},31,[63,740,638],{"class":73},[63,742,548],{"class":107},[63,744,694],{"class":73},[63,746,697],{"class":107},[63,748,700],{"class":73},[63,750,198],{"class":80},[63,752,556],{"class":73},[63,754,707],{"class":460},[63,756,117],{"class":73},[63,758,564],{"class":69},[63,760,455],{"class":73},[63,762,764,767,770],{"class":65,"line":763},32,[63,765,766],{"class":73},"  activeUsersGauge.",[63,768,769],{"class":107},"setValue",[63,771,772],{"class":73},"(data.count)\n",[63,774,776],{"class":65,"line":775},33,[63,777,217],{"class":73},[63,779,781],{"class":65,"line":780},34,[63,782,88],{"emptyLinePlaceholder":87},[63,784,786,788,790,792,795,797,799,801,803,805,807],{"class":65,"line":785},35,[63,787,638],{"class":73},[63,789,548],{"class":107},[63,791,694],{"class":73},[63,793,794],{"class":107},"Sale",[63,796,700],{"class":73},[63,798,203],{"class":80},[63,800,556],{"class":73},[63,802,707],{"class":460},[63,804,117],{"class":73},[63,806,564],{"class":69},[63,808,455],{"class":73},[63,810,812,815,818],{"class":65,"line":811},36,[63,813,814],{"class":73},"  salesChart.",[63,816,817],{"class":107},"addDataPoint",[63,819,820],{"class":73},"(data.amount, data.timestamp)\n",[63,822,824,827,830],{"class":65,"line":823},37,[63,825,826],{"class":73},"  totalSales.",[63,828,829],{"class":107},"increment",[63,831,832],{"class":73},"(data.amount)\n",[63,834,836],{"class":65,"line":835},38,[63,837,217],{"class":73},[53,839,844],{"className":840,"code":841,"filename":842,"language":843,"meta":59,"style":59},"language-python shiki shiki-themes github-light github-dark","from nolag import NoLag\n\nAPP_SLUG = 'analytics-a3f9'  # the slug returned when you created the app\n\nclient = NoLag('dashboard_access_token')\nclient.on('error', lambda err: print(err))  # unknown_topic etc. arrive here\nawait client.connect()\n\ndashboard = client.set_app(APP_SLUG).set_room('live')\n\n# Subscribe to real-time metrics\nawait dashboard.subscribe('page-views')\nawait dashboard.subscribe('active-users')\nawait dashboard.subscribe('sales')\n\n# Update charts in real-time\ndef on_page_views(data, meta):\n    page_views_chart.update(data['count'], data['timestamp'])\n\ndef on_active_users(data, meta):\n    active_users_gauge.set_value(data['count'])\n\ndef on_sales(data, meta):\n    sales_chart.add_data_point(data['amount'], data['timestamp'])\n    total_sales.increment(data['amount'])\n\ndashboard.on('page-views', on_page_views)\ndashboard.on('active-users', on_active_users)\ndashboard.on('sales', on_sales)\n","Python","python",[33,845,846,851,855,860,864,869,874,879,883,888,892,897,902,907,912,916,921,926,931,935,940,945,949,954,959,964,968,973,978],{"__ignoreMap":59},[63,847,848],{"class":65,"line":66},[63,849,850],{},"from nolag import NoLag\n",[63,852,853],{"class":65,"line":84},[63,854,88],{"emptyLinePlaceholder":87},[63,856,857],{"class":65,"line":91},[63,858,859],{},"APP_SLUG = 'analytics-a3f9'  # the slug returned when you created the app\n",[63,861,862],{"class":65,"line":124},[63,863,88],{"emptyLinePlaceholder":87},[63,865,866],{"class":65,"line":129},[63,867,868],{},"client = NoLag('dashboard_access_token')\n",[63,870,871],{"class":65,"line":135},[63,872,873],{},"client.on('error', lambda err: print(err))  # unknown_topic etc. arrive here\n",[63,875,876],{"class":65,"line":141},[63,877,878],{},"await client.connect()\n",[63,880,881],{"class":65,"line":163},[63,882,88],{"emptyLinePlaceholder":87},[63,884,885],{"class":65,"line":175},[63,886,887],{},"dashboard = client.set_app(APP_SLUG).set_room('live')\n",[63,889,890],{"class":65,"line":186},[63,891,88],{"emptyLinePlaceholder":87},[63,893,894],{"class":65,"line":214},[63,895,896],{},"# Subscribe to real-time metrics\n",[63,898,899],{"class":65,"line":220},[63,900,901],{},"await dashboard.subscribe('page-views')\n",[63,903,904],{"class":65,"line":235},[63,905,906],{},"await dashboard.subscribe('active-users')\n",[63,908,909],{"class":65,"line":240},[63,910,911],{},"await dashboard.subscribe('sales')\n",[63,913,914],{"class":65,"line":246},[63,915,88],{"emptyLinePlaceholder":87},[63,917,918],{"class":65,"line":272},[63,919,920],{},"# Update charts in real-time\n",[63,922,923],{"class":65,"line":293},[63,924,925],{},"def on_page_views(data, meta):\n",[63,927,928],{"class":65,"line":298},[63,929,930],{},"    page_views_chart.update(data['count'], data['timestamp'])\n",[63,932,933],{"class":65,"line":304},[63,934,88],{"emptyLinePlaceholder":87},[63,936,937],{"class":65,"line":335},[63,938,939],{},"def on_active_users(data, meta):\n",[63,941,942],{"class":65,"line":363},[63,943,944],{},"    active_users_gauge.set_value(data['count'])\n",[63,946,947],{"class":65,"line":635},[63,948,88],{"emptyLinePlaceholder":87},[63,950,951],{"class":65,"line":650},[63,952,953],{},"def on_sales(data, meta):\n",[63,955,956],{"class":65,"line":663},[63,957,958],{},"    sales_chart.add_data_point(data['amount'], data['timestamp'])\n",[63,960,961],{"class":65,"line":676},[63,962,963],{},"    total_sales.increment(data['amount'])\n",[63,965,966],{"class":65,"line":681},[63,967,88],{"emptyLinePlaceholder":87},[63,969,970],{"class":65,"line":687},[63,971,972],{},"dashboard.on('page-views', on_page_views)\n",[63,974,975],{"class":65,"line":716},[63,976,977],{},"dashboard.on('active-users', on_active_users)\n",[63,979,980],{"class":65,"line":728},[63,981,982],{},"dashboard.on('sales', on_sales)\n",[53,984,989],{"className":985,"code":986,"filename":987,"language":988,"meta":59,"style":59},"language-go shiki shiki-themes github-light github-dark","package main\n\nimport (\n    \"log\"\n\n    nolag \"github.com/NoLagApp/go-sdk\"\n)\n\nconst appSlug = \"analytics-a3f9\" // the slug returned when you created the app\n\n// Numbers in a payload arrive as the msgpack decoder's sized types (int8, uint16,\n// int64, float64, ...), never as int, so convert through a type switch rather\n// than asserting a single type.\nfunc num(v any) float64 {\n    switch n := v.(type) {\n    case float64:\n        return n\n    case float32:\n        return float64(n)\n    case int8:\n        return float64(n)\n    case int16:\n        return float64(n)\n    case int32:\n        return float64(n)\n    case int64:\n        return float64(n)\n    case uint8:\n        return float64(n)\n    case uint16:\n        return float64(n)\n    case uint32:\n        return float64(n)\n    case uint64:\n        return float64(n)\n    }\n    return 0\n}\n\nfunc main() {\n    client := nolag.New(\"dashboard_access_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    dashboard := client.SetApp(appSlug).SetRoom(\"live\")\n\n    // Subscribe to real-time metrics with handlers\n    dashboard.Subscribe(\"page-views\", func(data any, meta nolag.MessageMeta) {\n        d, _ := data.(map[string]any)\n        pageViewsChart.Update(num(d[\"count\"]), int64(num(d[\"timestamp\"])))\n    })\n\n    dashboard.Subscribe(\"active-users\", func(data any, meta nolag.MessageMeta) {\n        d, _ := data.(map[string]any)\n        activeUsersGauge.SetValue(int(num(d[\"count\"])))\n    })\n\n    dashboard.Subscribe(\"sales\", func(data any, meta nolag.MessageMeta) {\n        d, _ := data.(map[string]any)\n        salesChart.AddDataPoint(num(d[\"amount\"]), int64(num(d[\"timestamp\"])))\n        totalSales.Increment(num(d[\"amount\"]))\n    })\n\n    select {} // keep the process alive\n}\n","Go","go",[33,990,991,996,1000,1005,1010,1014,1019,1023,1027,1032,1036,1041,1046,1051,1056,1061,1066,1071,1076,1081,1086,1090,1095,1099,1104,1108,1113,1117,1122,1126,1131,1135,1140,1144,1149,1153,1158,1163,1167,1172,1178,1184,1190,1196,1202,1208,1214,1219,1224,1230,1235,1241,1247,1253,1259,1264,1269,1275,1280,1286,1291,1296,1302,1307,1313,1319,1324,1329,1335],{"__ignoreMap":59},[63,992,993],{"class":65,"line":66},[63,994,995],{},"package main\n",[63,997,998],{"class":65,"line":84},[63,999,88],{"emptyLinePlaceholder":87},[63,1001,1002],{"class":65,"line":91},[63,1003,1004],{},"import (\n",[63,1006,1007],{"class":65,"line":124},[63,1008,1009],{},"    \"log\"\n",[63,1011,1012],{"class":65,"line":129},[63,1013,88],{"emptyLinePlaceholder":87},[63,1015,1016],{"class":65,"line":135},[63,1017,1018],{},"    nolag \"github.com/NoLagApp/go-sdk\"\n",[63,1020,1021],{"class":65,"line":141},[63,1022,540],{},[63,1024,1025],{"class":65,"line":163},[63,1026,88],{"emptyLinePlaceholder":87},[63,1028,1029],{"class":65,"line":175},[63,1030,1031],{},"const appSlug = \"analytics-a3f9\" // the slug returned when you created the app\n",[63,1033,1034],{"class":65,"line":186},[63,1035,88],{"emptyLinePlaceholder":87},[63,1037,1038],{"class":65,"line":214},[63,1039,1040],{},"// Numbers in a payload arrive as the msgpack decoder's sized types (int8, uint16,\n",[63,1042,1043],{"class":65,"line":220},[63,1044,1045],{},"// int64, float64, ...), never as int, so convert through a type switch rather\n",[63,1047,1048],{"class":65,"line":235},[63,1049,1050],{},"// than asserting a single type.\n",[63,1052,1053],{"class":65,"line":240},[63,1054,1055],{},"func num(v any) float64 {\n",[63,1057,1058],{"class":65,"line":246},[63,1059,1060],{},"    switch n := v.(type) {\n",[63,1062,1063],{"class":65,"line":272},[63,1064,1065],{},"    case float64:\n",[63,1067,1068],{"class":65,"line":293},[63,1069,1070],{},"        return n\n",[63,1072,1073],{"class":65,"line":298},[63,1074,1075],{},"    case float32:\n",[63,1077,1078],{"class":65,"line":304},[63,1079,1080],{},"        return float64(n)\n",[63,1082,1083],{"class":65,"line":335},[63,1084,1085],{},"    case int8:\n",[63,1087,1088],{"class":65,"line":363},[63,1089,1080],{},[63,1091,1092],{"class":65,"line":635},[63,1093,1094],{},"    case int16:\n",[63,1096,1097],{"class":65,"line":650},[63,1098,1080],{},[63,1100,1101],{"class":65,"line":663},[63,1102,1103],{},"    case int32:\n",[63,1105,1106],{"class":65,"line":676},[63,1107,1080],{},[63,1109,1110],{"class":65,"line":681},[63,1111,1112],{},"    case int64:\n",[63,1114,1115],{"class":65,"line":687},[63,1116,1080],{},[63,1118,1119],{"class":65,"line":716},[63,1120,1121],{},"    case uint8:\n",[63,1123,1124],{"class":65,"line":728},[63,1125,1080],{},[63,1127,1128],{"class":65,"line":733},[63,1129,1130],{},"    case uint16:\n",[63,1132,1133],{"class":65,"line":738},[63,1134,1080],{},[63,1136,1137],{"class":65,"line":763},[63,1138,1139],{},"    case uint32:\n",[63,1141,1142],{"class":65,"line":775},[63,1143,1080],{},[63,1145,1146],{"class":65,"line":780},[63,1147,1148],{},"    case uint64:\n",[63,1150,1151],{"class":65,"line":785},[63,1152,1080],{},[63,1154,1155],{"class":65,"line":811},[63,1156,1157],{},"    }\n",[63,1159,1160],{"class":65,"line":823},[63,1161,1162],{},"    return 0\n",[63,1164,1165],{"class":65,"line":835},[63,1166,481],{},[63,1168,1170],{"class":65,"line":1169},39,[63,1171,88],{"emptyLinePlaceholder":87},[63,1173,1175],{"class":65,"line":1174},40,[63,1176,1177],{},"func main() {\n",[63,1179,1181],{"class":65,"line":1180},41,[63,1182,1183],{},"    client := nolag.New(\"dashboard_access_token\")\n",[63,1185,1187],{"class":65,"line":1186},42,[63,1188,1189],{},"    client.OnError(func(err *nolag.ServerError) {\n",[63,1191,1193],{"class":65,"line":1192},43,[63,1194,1195],{},"        log.Println(err) // unknown_topic etc. arrive here\n",[63,1197,1199],{"class":65,"line":1198},44,[63,1200,1201],{},"    })\n",[63,1203,1205],{"class":65,"line":1204},45,[63,1206,1207],{},"    if err := client.Connect(); err != nil {\n",[63,1209,1211],{"class":65,"line":1210},46,[63,1212,1213],{},"        log.Fatal(err)\n",[63,1215,1217],{"class":65,"line":1216},47,[63,1218,1157],{},[63,1220,1222],{"class":65,"line":1221},48,[63,1223,88],{"emptyLinePlaceholder":87},[63,1225,1227],{"class":65,"line":1226},49,[63,1228,1229],{},"    dashboard := client.SetApp(appSlug).SetRoom(\"live\")\n",[63,1231,1233],{"class":65,"line":1232},50,[63,1234,88],{"emptyLinePlaceholder":87},[63,1236,1238],{"class":65,"line":1237},51,[63,1239,1240],{},"    // Subscribe to real-time metrics with handlers\n",[63,1242,1244],{"class":65,"line":1243},52,[63,1245,1246],{},"    dashboard.Subscribe(\"page-views\", func(data any, meta nolag.MessageMeta) {\n",[63,1248,1250],{"class":65,"line":1249},53,[63,1251,1252],{},"        d, _ := data.(map[string]any)\n",[63,1254,1256],{"class":65,"line":1255},54,[63,1257,1258],{},"        pageViewsChart.Update(num(d[\"count\"]), int64(num(d[\"timestamp\"])))\n",[63,1260,1262],{"class":65,"line":1261},55,[63,1263,1201],{},[63,1265,1267],{"class":65,"line":1266},56,[63,1268,88],{"emptyLinePlaceholder":87},[63,1270,1272],{"class":65,"line":1271},57,[63,1273,1274],{},"    dashboard.Subscribe(\"active-users\", func(data any, meta nolag.MessageMeta) {\n",[63,1276,1278],{"class":65,"line":1277},58,[63,1279,1252],{},[63,1281,1283],{"class":65,"line":1282},59,[63,1284,1285],{},"        activeUsersGauge.SetValue(int(num(d[\"count\"])))\n",[63,1287,1289],{"class":65,"line":1288},60,[63,1290,1201],{},[63,1292,1294],{"class":65,"line":1293},61,[63,1295,88],{"emptyLinePlaceholder":87},[63,1297,1299],{"class":65,"line":1298},62,[63,1300,1301],{},"    dashboard.Subscribe(\"sales\", func(data any, meta nolag.MessageMeta) {\n",[63,1303,1305],{"class":65,"line":1304},63,[63,1306,1252],{},[63,1308,1310],{"class":65,"line":1309},64,[63,1311,1312],{},"        salesChart.AddDataPoint(num(d[\"amount\"]), int64(num(d[\"timestamp\"])))\n",[63,1314,1316],{"class":65,"line":1315},65,[63,1317,1318],{},"        totalSales.Increment(num(d[\"amount\"]))\n",[63,1320,1322],{"class":65,"line":1321},66,[63,1323,1201],{},[63,1325,1327],{"class":65,"line":1326},67,[63,1328,88],{"emptyLinePlaceholder":87},[63,1330,1332],{"class":65,"line":1331},68,[63,1333,1334],{},"    select {} // keep the process alive\n",[63,1336,1338],{"class":65,"line":1337},69,[63,1339,481],{},[18,1341,1343],{"id":1342},"filtering-dashboard-updates","Filtering Dashboard Updates",[14,1345,1346],{},"Most dashboards show a subset of data. A few bookings, specific orders, or selected devices. Without filters, every update on the topic is delivered to every subscriber, even if they're only watching 3 items out of thousands.",[14,1348,1349,1353],{},[1350,1351,1352],"strong",{},"Topic filters"," solve this at the infrastructure level. Subscribe with the IDs of the entities currently visible on screen, and only updates published with one of those filter values are delivered. When the user navigates, swap filters dynamically, no resubscribe needed. Filters are routing, not a privacy boundary: a subscriber with no filters is a wildcard subscriber and still receives every update on the topic.",[50,1355,1356,1757,1919],{},[53,1357,1359],{"className":55,"code":1358,"filename":408,"language":58,"meta":59,"style":59},"import { NoLag } from '@nolag/js-sdk'\n\nconst APP_SLUG = 'analytics-a3f9' // the slug returned when you created the app\n\ninterface BookingUpdate {\n  status: string\n  guest?: string\n  reason?: string\n}\n\nconst client = NoLag('dashboard_access_token')\nawait client.connect()\n\n// The ops room carries booking updates\nconst ops = client.setApp(APP_SLUG).setRoom('ops')\n\n// User is viewing 3 specific bookings on their dashboard\nconst visibleBookings = ['booking_42', 'booking_87', 'booking_153']\n\n// Subscribe with filters: only receive updates for these bookings\nops.subscribe('bookings', {\n  filters: visibleBookings\n})\n\nops.on\u003CBookingUpdate>('bookings', (data, meta) => {\n  // meta.filter tells you which booking was updated\n  updateBookingCard(meta.filter, data)\n})\n\n// User navigates to a different page, swap filters instantly\nfunction onPageChange(newBookingIds: string[]) {\n  ops.setFilters('bookings', newBookingIds)\n}\n\n// User opens a booking detail, add it to filters\nfunction onBookingOpen(bookingId: string) {\n  ops.addFilters('bookings', [bookingId])\n}\n\n// User closes a booking detail, remove it\nfunction onBookingClose(bookingId: string) {\n  ops.removeFilters('bookings', [bookingId])\n}\n",[33,1360,1361,1371,1375,1387,1391,1400,1410,1420,1429,1433,1437,1453,1463,1467,1472,1499,1503,1508,1536,1540,1545,1559,1564,1568,1572,1602,1607,1615,1619,1623,1628,1649,1664,1668,1672,1677,1696,1710,1714,1718,1723,1740,1753],{"__ignoreMap":59},[63,1362,1363,1365,1367,1369],{"class":65,"line":66},[63,1364,70],{"class":69},[63,1366,417],{"class":73},[63,1368,77],{"class":69},[63,1370,81],{"class":80},[63,1372,1373],{"class":65,"line":84},[63,1374,88],{"emptyLinePlaceholder":87},[63,1376,1377,1379,1381,1383,1385],{"class":65,"line":91},[63,1378,94],{"class":69},[63,1380,432],{"class":97},[63,1382,101],{"class":69},[63,1384,437],{"class":80},[63,1386,440],{"class":120},[63,1388,1389],{"class":65,"line":124},[63,1390,88],{"emptyLinePlaceholder":87},[63,1392,1393,1395,1398],{"class":65,"line":129},[63,1394,449],{"class":69},[63,1396,1397],{"class":107}," BookingUpdate",[63,1399,455],{"class":73},[63,1401,1402,1405,1407],{"class":65,"line":135},[63,1403,1404],{"class":460},"  status",[63,1406,464],{"class":69},[63,1408,1409],{"class":97}," string\n",[63,1411,1412,1415,1418],{"class":65,"line":141},[63,1413,1414],{"class":460},"  guest",[63,1416,1417],{"class":69},"?:",[63,1419,1409],{"class":97},[63,1421,1422,1425,1427],{"class":65,"line":163},[63,1423,1424],{"class":460},"  reason",[63,1426,1417],{"class":69},[63,1428,1409],{"class":97},[63,1430,1431],{"class":65,"line":175},[63,1432,481],{"class":73},[63,1434,1435],{"class":65,"line":186},[63,1436,88],{"emptyLinePlaceholder":87},[63,1438,1439,1441,1443,1445,1447,1449,1451],{"class":65,"line":214},[63,1440,94],{"class":69},[63,1442,526],{"class":97},[63,1444,101],{"class":69},[63,1446,531],{"class":107},[63,1448,534],{"class":73},[63,1450,537],{"class":80},[63,1452,540],{"class":73},[63,1454,1455,1457,1459,1461],{"class":65,"line":220},[63,1456,249],{"class":69},[63,1458,583],{"class":73},[63,1460,586],{"class":107},[63,1462,589],{"class":73},[63,1464,1465],{"class":65,"line":235},[63,1466,88],{"emptyLinePlaceholder":87},[63,1468,1469],{"class":65,"line":240},[63,1470,1471],{"class":120},"// The ops room carries booking updates\n",[63,1473,1474,1476,1479,1481,1483,1485,1487,1489,1491,1493,1495,1497],{"class":65,"line":246},[63,1475,94],{"class":69},[63,1477,1478],{"class":97}," ops",[63,1480,101],{"class":69},[63,1482,583],{"class":73},[63,1484,606],{"class":107},[63,1486,534],{"class":73},[63,1488,611],{"class":97},[63,1490,614],{"class":73},[63,1492,617],{"class":107},[63,1494,534],{"class":73},[63,1496,288],{"class":80},[63,1498,540],{"class":73},[63,1500,1501],{"class":65,"line":272},[63,1502,88],{"emptyLinePlaceholder":87},[63,1504,1505],{"class":65,"line":293},[63,1506,1507],{"class":120},"// User is viewing 3 specific bookings on their dashboard\n",[63,1509,1510,1512,1515,1517,1520,1523,1525,1528,1530,1533],{"class":65,"line":298},[63,1511,94],{"class":69},[63,1513,1514],{"class":97}," visibleBookings",[63,1516,101],{"class":69},[63,1518,1519],{"class":73}," [",[63,1521,1522],{"class":80},"'booking_42'",[63,1524,195],{"class":73},[63,1526,1527],{"class":80},"'booking_87'",[63,1529,195],{"class":73},[63,1531,1532],{"class":80},"'booking_153'",[63,1534,1535],{"class":73},"]\n",[63,1537,1538],{"class":65,"line":304},[63,1539,88],{"emptyLinePlaceholder":87},[63,1541,1542],{"class":65,"line":335},[63,1543,1544],{"class":120},"// Subscribe with filters: only receive updates for these bookings\n",[63,1546,1547,1550,1552,1554,1556],{"class":65,"line":363},[63,1548,1549],{"class":73},"ops.",[63,1551,641],{"class":107},[63,1553,534],{"class":73},[63,1555,208],{"class":80},[63,1557,1558],{"class":73},", {\n",[63,1560,1561],{"class":65,"line":635},[63,1562,1563],{"class":73},"  filters: visibleBookings\n",[63,1565,1566],{"class":65,"line":650},[63,1567,217],{"class":73},[63,1569,1570],{"class":65,"line":663},[63,1571,88],{"emptyLinePlaceholder":87},[63,1573,1574,1576,1578,1580,1583,1585,1587,1589,1591,1593,1596,1598,1600],{"class":65,"line":676},[63,1575,1549],{"class":73},[63,1577,548],{"class":107},[63,1579,694],{"class":73},[63,1581,1582],{"class":107},"BookingUpdate",[63,1584,700],{"class":73},[63,1586,208],{"class":80},[63,1588,556],{"class":73},[63,1590,707],{"class":460},[63,1592,195],{"class":73},[63,1594,1595],{"class":460},"meta",[63,1597,117],{"class":73},[63,1599,564],{"class":69},[63,1601,455],{"class":73},[63,1603,1604],{"class":65,"line":681},[63,1605,1606],{"class":120},"  // meta.filter tells you which booking was updated\n",[63,1608,1609,1612],{"class":65,"line":687},[63,1610,1611],{"class":107},"  updateBookingCard",[63,1613,1614],{"class":73},"(meta.filter, data)\n",[63,1616,1617],{"class":65,"line":716},[63,1618,217],{"class":73},[63,1620,1621],{"class":65,"line":728},[63,1622,88],{"emptyLinePlaceholder":87},[63,1624,1625],{"class":65,"line":733},[63,1626,1627],{"class":120},"// User navigates to a different page, swap filters instantly\n",[63,1629,1630,1633,1636,1638,1641,1643,1646],{"class":65,"line":738},[63,1631,1632],{"class":69},"function",[63,1634,1635],{"class":107}," onPageChange",[63,1637,534],{"class":73},[63,1639,1640],{"class":460},"newBookingIds",[63,1642,464],{"class":69},[63,1644,1645],{"class":97}," string",[63,1647,1648],{"class":73},"[]) {\n",[63,1650,1651,1654,1657,1659,1661],{"class":65,"line":763},[63,1652,1653],{"class":73},"  ops.",[63,1655,1656],{"class":107},"setFilters",[63,1658,534],{"class":73},[63,1660,208],{"class":80},[63,1662,1663],{"class":73},", newBookingIds)\n",[63,1665,1666],{"class":65,"line":775},[63,1667,481],{"class":73},[63,1669,1670],{"class":65,"line":780},[63,1671,88],{"emptyLinePlaceholder":87},[63,1673,1674],{"class":65,"line":785},[63,1675,1676],{"class":120},"// User opens a booking detail, add it to filters\n",[63,1678,1679,1681,1684,1686,1689,1691,1693],{"class":65,"line":811},[63,1680,1632],{"class":69},[63,1682,1683],{"class":107}," onBookingOpen",[63,1685,534],{"class":73},[63,1687,1688],{"class":460},"bookingId",[63,1690,464],{"class":69},[63,1692,1645],{"class":97},[63,1694,1695],{"class":73},") {\n",[63,1697,1698,1700,1703,1705,1707],{"class":65,"line":823},[63,1699,1653],{"class":73},[63,1701,1702],{"class":107},"addFilters",[63,1704,534],{"class":73},[63,1706,208],{"class":80},[63,1708,1709],{"class":73},", [bookingId])\n",[63,1711,1712],{"class":65,"line":835},[63,1713,481],{"class":73},[63,1715,1716],{"class":65,"line":1169},[63,1717,88],{"emptyLinePlaceholder":87},[63,1719,1720],{"class":65,"line":1174},[63,1721,1722],{"class":120},"// User closes a booking detail, remove it\n",[63,1724,1725,1727,1730,1732,1734,1736,1738],{"class":65,"line":1180},[63,1726,1632],{"class":69},[63,1728,1729],{"class":107}," onBookingClose",[63,1731,534],{"class":73},[63,1733,1688],{"class":460},[63,1735,464],{"class":69},[63,1737,1645],{"class":97},[63,1739,1695],{"class":73},[63,1741,1742,1744,1747,1749,1751],{"class":65,"line":1186},[63,1743,1653],{"class":73},[63,1745,1746],{"class":107},"removeFilters",[63,1748,534],{"class":73},[63,1750,208],{"class":80},[63,1752,1709],{"class":73},[63,1754,1755],{"class":65,"line":1192},[63,1756,481],{"class":73},[53,1758,1760],{"className":840,"code":1759,"filename":842,"language":843,"meta":59,"style":59},"from nolag import NoLag, SubscribeOptions\n\nAPP_SLUG = 'analytics-a3f9'  # the slug returned when you created the app\n\nclient = NoLag('dashboard_access_token')\nawait client.connect()\n\n# The ops room carries booking updates\nops = client.set_app(APP_SLUG).set_room('ops')\n\n# User is viewing 3 specific bookings on their dashboard\nvisible_bookings = ['booking_42', 'booking_87', 'booking_153']\n\n# Subscribe with filters\nawait ops.subscribe('bookings', SubscribeOptions(\n    filters=visible_bookings\n))\n\ndef on_booking_update(data, meta):\n    # meta.filter tells you which booking was updated\n    update_booking_card(meta.filter, data)\n\nops.on('bookings', on_booking_update)\n\n# User navigates to a different page\nasync def on_page_change(new_booking_ids):\n    await ops.set_filters('bookings', new_booking_ids)\n\n# Add/remove individual bookings\nasync def on_booking_open(booking_id):\n    await ops.add_filters('bookings', [booking_id])\n\nasync def on_booking_close(booking_id):\n    await ops.remove_filters('bookings', [booking_id])\n",[33,1761,1762,1767,1771,1775,1779,1783,1787,1791,1796,1801,1805,1810,1815,1819,1824,1829,1834,1839,1843,1848,1853,1858,1862,1867,1871,1876,1881,1886,1890,1895,1900,1905,1909,1914],{"__ignoreMap":59},[63,1763,1764],{"class":65,"line":66},[63,1765,1766],{},"from nolag import NoLag, SubscribeOptions\n",[63,1768,1769],{"class":65,"line":84},[63,1770,88],{"emptyLinePlaceholder":87},[63,1772,1773],{"class":65,"line":91},[63,1774,859],{},[63,1776,1777],{"class":65,"line":124},[63,1778,88],{"emptyLinePlaceholder":87},[63,1780,1781],{"class":65,"line":129},[63,1782,868],{},[63,1784,1785],{"class":65,"line":135},[63,1786,878],{},[63,1788,1789],{"class":65,"line":141},[63,1790,88],{"emptyLinePlaceholder":87},[63,1792,1793],{"class":65,"line":163},[63,1794,1795],{},"# The ops room carries booking updates\n",[63,1797,1798],{"class":65,"line":175},[63,1799,1800],{},"ops = client.set_app(APP_SLUG).set_room('ops')\n",[63,1802,1803],{"class":65,"line":186},[63,1804,88],{"emptyLinePlaceholder":87},[63,1806,1807],{"class":65,"line":214},[63,1808,1809],{},"# User is viewing 3 specific bookings on their dashboard\n",[63,1811,1812],{"class":65,"line":220},[63,1813,1814],{},"visible_bookings = ['booking_42', 'booking_87', 'booking_153']\n",[63,1816,1817],{"class":65,"line":235},[63,1818,88],{"emptyLinePlaceholder":87},[63,1820,1821],{"class":65,"line":240},[63,1822,1823],{},"# Subscribe with filters\n",[63,1825,1826],{"class":65,"line":246},[63,1827,1828],{},"await ops.subscribe('bookings', SubscribeOptions(\n",[63,1830,1831],{"class":65,"line":272},[63,1832,1833],{},"    filters=visible_bookings\n",[63,1835,1836],{"class":65,"line":293},[63,1837,1838],{},"))\n",[63,1840,1841],{"class":65,"line":298},[63,1842,88],{"emptyLinePlaceholder":87},[63,1844,1845],{"class":65,"line":304},[63,1846,1847],{},"def on_booking_update(data, meta):\n",[63,1849,1850],{"class":65,"line":335},[63,1851,1852],{},"    # meta.filter tells you which booking was updated\n",[63,1854,1855],{"class":65,"line":363},[63,1856,1857],{},"    update_booking_card(meta.filter, data)\n",[63,1859,1860],{"class":65,"line":635},[63,1861,88],{"emptyLinePlaceholder":87},[63,1863,1864],{"class":65,"line":650},[63,1865,1866],{},"ops.on('bookings', on_booking_update)\n",[63,1868,1869],{"class":65,"line":663},[63,1870,88],{"emptyLinePlaceholder":87},[63,1872,1873],{"class":65,"line":676},[63,1874,1875],{},"# User navigates to a different page\n",[63,1877,1878],{"class":65,"line":681},[63,1879,1880],{},"async def on_page_change(new_booking_ids):\n",[63,1882,1883],{"class":65,"line":687},[63,1884,1885],{},"    await ops.set_filters('bookings', new_booking_ids)\n",[63,1887,1888],{"class":65,"line":716},[63,1889,88],{"emptyLinePlaceholder":87},[63,1891,1892],{"class":65,"line":728},[63,1893,1894],{},"# Add/remove individual bookings\n",[63,1896,1897],{"class":65,"line":733},[63,1898,1899],{},"async def on_booking_open(booking_id):\n",[63,1901,1902],{"class":65,"line":738},[63,1903,1904],{},"    await ops.add_filters('bookings', [booking_id])\n",[63,1906,1907],{"class":65,"line":763},[63,1908,88],{"emptyLinePlaceholder":87},[63,1910,1911],{"class":65,"line":775},[63,1912,1913],{},"async def on_booking_close(booking_id):\n",[63,1915,1916],{"class":65,"line":780},[63,1917,1918],{},"    await ops.remove_filters('bookings', [booking_id])\n",[53,1920,1922],{"className":985,"code":1921,"filename":987,"language":988,"meta":59,"style":59},"package main\n\nimport (\n    \"log\"\n\n    nolag \"github.com/NoLagApp/go-sdk\"\n)\n\nconst appSlug = \"analytics-a3f9\" // the slug returned when you created the app\n\nfunc main() {\n    client := nolag.New(\"dashboard_access_token\")\n    if err := client.Connect(); err != nil {\n        log.Fatal(err)\n    }\n\n    // The ops room carries booking updates\n    ops := client.SetApp(appSlug).SetRoom(\"ops\")\n\n    // User is viewing 3 specific bookings. Filters is []any: string items are\n    // OR-ed, and a nested []string item is an AND group.\n    visibleBookings := []any{\"booking_42\", \"booking_87\", \"booking_153\"}\n\n    // Subscribe with filters and handler\n    ops.Subscribe(\"bookings\", func(data any, meta nolag.MessageMeta) {\n        // meta.Filter tells you which booking was updated\n        updateBookingCard(meta.Filter, data)\n    }, nolag.SubscribeOptions{\n        Filters: visibleBookings,\n    })\n\n    // Swap filters on page change\n    ops.SetFilters(\"bookings\", []any{\"booking_200\", \"booking_201\"})\n\n    // Add or remove a single booking\n    ops.AddFilters(\"bookings\", []string{\"booking_300\"})\n    ops.RemoveFilters(\"bookings\", []string{\"booking_200\"})\n\n    select {} // keep the process alive\n}\n",[33,1923,1924,1928,1932,1936,1940,1944,1948,1952,1956,1960,1964,1968,1972,1976,1980,1984,1988,1993,1998,2002,2007,2012,2017,2021,2026,2031,2036,2041,2046,2051,2055,2059,2064,2069,2073,2078,2083,2088,2092,2096],{"__ignoreMap":59},[63,1925,1926],{"class":65,"line":66},[63,1927,995],{},[63,1929,1930],{"class":65,"line":84},[63,1931,88],{"emptyLinePlaceholder":87},[63,1933,1934],{"class":65,"line":91},[63,1935,1004],{},[63,1937,1938],{"class":65,"line":124},[63,1939,1009],{},[63,1941,1942],{"class":65,"line":129},[63,1943,88],{"emptyLinePlaceholder":87},[63,1945,1946],{"class":65,"line":135},[63,1947,1018],{},[63,1949,1950],{"class":65,"line":141},[63,1951,540],{},[63,1953,1954],{"class":65,"line":163},[63,1955,88],{"emptyLinePlaceholder":87},[63,1957,1958],{"class":65,"line":175},[63,1959,1031],{},[63,1961,1962],{"class":65,"line":186},[63,1963,88],{"emptyLinePlaceholder":87},[63,1965,1966],{"class":65,"line":214},[63,1967,1177],{},[63,1969,1970],{"class":65,"line":220},[63,1971,1183],{},[63,1973,1974],{"class":65,"line":235},[63,1975,1207],{},[63,1977,1978],{"class":65,"line":240},[63,1979,1213],{},[63,1981,1982],{"class":65,"line":246},[63,1983,1157],{},[63,1985,1986],{"class":65,"line":272},[63,1987,88],{"emptyLinePlaceholder":87},[63,1989,1990],{"class":65,"line":293},[63,1991,1992],{},"    // The ops room carries booking updates\n",[63,1994,1995],{"class":65,"line":298},[63,1996,1997],{},"    ops := client.SetApp(appSlug).SetRoom(\"ops\")\n",[63,1999,2000],{"class":65,"line":304},[63,2001,88],{"emptyLinePlaceholder":87},[63,2003,2004],{"class":65,"line":335},[63,2005,2006],{},"    // User is viewing 3 specific bookings. Filters is []any: string items are\n",[63,2008,2009],{"class":65,"line":363},[63,2010,2011],{},"    // OR-ed, and a nested []string item is an AND group.\n",[63,2013,2014],{"class":65,"line":635},[63,2015,2016],{},"    visibleBookings := []any{\"booking_42\", \"booking_87\", \"booking_153\"}\n",[63,2018,2019],{"class":65,"line":650},[63,2020,88],{"emptyLinePlaceholder":87},[63,2022,2023],{"class":65,"line":663},[63,2024,2025],{},"    // Subscribe with filters and handler\n",[63,2027,2028],{"class":65,"line":676},[63,2029,2030],{},"    ops.Subscribe(\"bookings\", func(data any, meta nolag.MessageMeta) {\n",[63,2032,2033],{"class":65,"line":681},[63,2034,2035],{},"        // meta.Filter tells you which booking was updated\n",[63,2037,2038],{"class":65,"line":687},[63,2039,2040],{},"        updateBookingCard(meta.Filter, data)\n",[63,2042,2043],{"class":65,"line":716},[63,2044,2045],{},"    }, nolag.SubscribeOptions{\n",[63,2047,2048],{"class":65,"line":728},[63,2049,2050],{},"        Filters: visibleBookings,\n",[63,2052,2053],{"class":65,"line":733},[63,2054,1201],{},[63,2056,2057],{"class":65,"line":738},[63,2058,88],{"emptyLinePlaceholder":87},[63,2060,2061],{"class":65,"line":763},[63,2062,2063],{},"    // Swap filters on page change\n",[63,2065,2066],{"class":65,"line":775},[63,2067,2068],{},"    ops.SetFilters(\"bookings\", []any{\"booking_200\", \"booking_201\"})\n",[63,2070,2071],{"class":65,"line":780},[63,2072,88],{"emptyLinePlaceholder":87},[63,2074,2075],{"class":65,"line":785},[63,2076,2077],{},"    // Add or remove a single booking\n",[63,2079,2080],{"class":65,"line":811},[63,2081,2082],{},"    ops.AddFilters(\"bookings\", []string{\"booking_300\"})\n",[63,2084,2085],{"class":65,"line":823},[63,2086,2087],{},"    ops.RemoveFilters(\"bookings\", []string{\"booking_200\"})\n",[63,2089,2090],{"class":65,"line":835},[63,2091,88],{"emptyLinePlaceholder":87},[63,2093,2094],{"class":65,"line":1169},[63,2095,1334],{},[63,2097,2098],{"class":65,"line":1174},[63,2099,481],{},[2101,2102,2104],"h3",{"id":2103},"publishing-filtered-updates","Publishing Filtered Updates",[14,2106,2107,2108,2111,2112,2114],{},"On the backend, publish with a ",[33,2109,2110],{},"filter"," to target specific entities. Only dashboards whose filter list includes that value receive the update, plus any subscriber that set no filters at all. The backend uses its own ",[33,2113,43],{}," actor:",[50,2116,2117,2337,2440],{},[53,2118,2120],{"className":55,"code":2119,"filename":408,"language":58,"meta":59,"style":59},"import { NoLag } from '@nolag/js-sdk'\n\nconst APP_SLUG = 'analytics-a3f9' // the slug returned when you created the app\n\n// Backend service: its own actor, so the dashboard actor receives what it publishes\nconst publisher = NoLag('metrics_service_access_token')\nawait publisher.connect()\n\nconst ops = publisher.setApp(APP_SLUG).setRoom('ops')\n\n// Only dashboard users watching booking_42 receive this\nops.emit('bookings', {\n  status: 'confirmed',\n  guest: 'John Smith',\n  checkIn: '2026-03-15'\n}, {\n  filter: 'booking_42' // the routing key\n})\n\n// Publish a different booking update\nops.emit('bookings', {\n  status: 'cancelled',\n  reason: 'Guest request'\n}, {\n  filter: 'booking_87'\n})\n",[33,2121,2122,2132,2136,2148,2152,2157,2174,2185,2189,2215,2219,2224,2237,2247,2257,2265,2270,2280,2284,2288,2293,2305,2314,2322,2326,2333],{"__ignoreMap":59},[63,2123,2124,2126,2128,2130],{"class":65,"line":66},[63,2125,70],{"class":69},[63,2127,417],{"class":73},[63,2129,77],{"class":69},[63,2131,81],{"class":80},[63,2133,2134],{"class":65,"line":84},[63,2135,88],{"emptyLinePlaceholder":87},[63,2137,2138,2140,2142,2144,2146],{"class":65,"line":91},[63,2139,94],{"class":69},[63,2141,432],{"class":97},[63,2143,101],{"class":69},[63,2145,437],{"class":80},[63,2147,440],{"class":120},[63,2149,2150],{"class":65,"line":124},[63,2151,88],{"emptyLinePlaceholder":87},[63,2153,2154],{"class":65,"line":129},[63,2155,2156],{"class":120},"// Backend service: its own actor, so the dashboard actor receives what it publishes\n",[63,2158,2159,2161,2163,2165,2167,2169,2172],{"class":65,"line":135},[63,2160,94],{"class":69},[63,2162,309],{"class":97},[63,2164,101],{"class":69},[63,2166,531],{"class":107},[63,2168,534],{"class":73},[63,2170,2171],{"class":80},"'metrics_service_access_token'",[63,2173,540],{"class":73},[63,2175,2176,2178,2181,2183],{"class":65,"line":141},[63,2177,249],{"class":69},[63,2179,2180],{"class":73}," publisher.",[63,2182,586],{"class":107},[63,2184,589],{"class":73},[63,2186,2187],{"class":65,"line":163},[63,2188,88],{"emptyLinePlaceholder":87},[63,2190,2191,2193,2195,2197,2199,2201,2203,2205,2207,2209,2211,2213],{"class":65,"line":175},[63,2192,94],{"class":69},[63,2194,1478],{"class":97},[63,2196,101],{"class":69},[63,2198,2180],{"class":73},[63,2200,606],{"class":107},[63,2202,534],{"class":73},[63,2204,611],{"class":97},[63,2206,614],{"class":73},[63,2208,617],{"class":107},[63,2210,534],{"class":73},[63,2212,288],{"class":80},[63,2214,540],{"class":73},[63,2216,2217],{"class":65,"line":186},[63,2218,88],{"emptyLinePlaceholder":87},[63,2220,2221],{"class":65,"line":214},[63,2222,2223],{"class":120},"// Only dashboard users watching booking_42 receive this\n",[63,2225,2226,2228,2231,2233,2235],{"class":65,"line":220},[63,2227,1549],{"class":73},[63,2229,2230],{"class":107},"emit",[63,2232,534],{"class":73},[63,2234,208],{"class":80},[63,2236,1558],{"class":73},[63,2238,2239,2242,2245],{"class":65,"line":235},[63,2240,2241],{"class":73},"  status: ",[63,2243,2244],{"class":80},"'confirmed'",[63,2246,172],{"class":73},[63,2248,2249,2252,2255],{"class":65,"line":240},[63,2250,2251],{"class":73},"  guest: ",[63,2253,2254],{"class":80},"'John Smith'",[63,2256,172],{"class":73},[63,2258,2259,2262],{"class":65,"line":246},[63,2260,2261],{"class":73},"  checkIn: ",[63,2263,2264],{"class":80},"'2026-03-15'\n",[63,2266,2267],{"class":65,"line":272},[63,2268,2269],{"class":73},"}, {\n",[63,2271,2272,2275,2277],{"class":65,"line":293},[63,2273,2274],{"class":73},"  filter: ",[63,2276,1522],{"class":80},[63,2278,2279],{"class":120}," // the routing key\n",[63,2281,2282],{"class":65,"line":298},[63,2283,217],{"class":73},[63,2285,2286],{"class":65,"line":304},[63,2287,88],{"emptyLinePlaceholder":87},[63,2289,2290],{"class":65,"line":335},[63,2291,2292],{"class":120},"// Publish a different booking update\n",[63,2294,2295,2297,2299,2301,2303],{"class":65,"line":363},[63,2296,1549],{"class":73},[63,2298,2230],{"class":107},[63,2300,534],{"class":73},[63,2302,208],{"class":80},[63,2304,1558],{"class":73},[63,2306,2307,2309,2312],{"class":65,"line":635},[63,2308,2241],{"class":73},[63,2310,2311],{"class":80},"'cancelled'",[63,2313,172],{"class":73},[63,2315,2316,2319],{"class":65,"line":650},[63,2317,2318],{"class":73},"  reason: ",[63,2320,2321],{"class":80},"'Guest request'\n",[63,2323,2324],{"class":65,"line":663},[63,2325,2269],{"class":73},[63,2327,2328,2330],{"class":65,"line":676},[63,2329,2274],{"class":73},[63,2331,2332],{"class":80},"'booking_87'\n",[63,2334,2335],{"class":65,"line":681},[63,2336,217],{"class":73},[53,2338,2340],{"className":840,"code":2339,"filename":842,"language":843,"meta":59,"style":59},"from nolag import NoLag, EmitOptions\n\nAPP_SLUG = 'analytics-a3f9'  # the slug returned when you created the app\n\n# Backend service: its own actor, so the dashboard actor receives what it publishes\npublisher = NoLag('metrics_service_access_token')\nawait publisher.connect()\n\nops = publisher.set_app(APP_SLUG).set_room('ops')\n\n# Only dashboard users watching booking_42 receive this\nawait ops.emit('bookings', {\n    'status': 'confirmed',\n    'guest': 'John Smith',\n    'check_in': '2026-03-15'\n}, EmitOptions(filter='booking_42'))  # the routing key\n\nawait ops.emit('bookings', {\n    'status': 'cancelled',\n    'reason': 'Guest request'\n}, EmitOptions(filter='booking_87'))\n",[33,2341,2342,2347,2351,2355,2359,2364,2369,2374,2378,2383,2387,2392,2397,2402,2407,2412,2417,2421,2425,2430,2435],{"__ignoreMap":59},[63,2343,2344],{"class":65,"line":66},[63,2345,2346],{},"from nolag import NoLag, EmitOptions\n",[63,2348,2349],{"class":65,"line":84},[63,2350,88],{"emptyLinePlaceholder":87},[63,2352,2353],{"class":65,"line":91},[63,2354,859],{},[63,2356,2357],{"class":65,"line":124},[63,2358,88],{"emptyLinePlaceholder":87},[63,2360,2361],{"class":65,"line":129},[63,2362,2363],{},"# Backend service: its own actor, so the dashboard actor receives what it publishes\n",[63,2365,2366],{"class":65,"line":135},[63,2367,2368],{},"publisher = NoLag('metrics_service_access_token')\n",[63,2370,2371],{"class":65,"line":141},[63,2372,2373],{},"await publisher.connect()\n",[63,2375,2376],{"class":65,"line":163},[63,2377,88],{"emptyLinePlaceholder":87},[63,2379,2380],{"class":65,"line":175},[63,2381,2382],{},"ops = publisher.set_app(APP_SLUG).set_room('ops')\n",[63,2384,2385],{"class":65,"line":186},[63,2386,88],{"emptyLinePlaceholder":87},[63,2388,2389],{"class":65,"line":214},[63,2390,2391],{},"# Only dashboard users watching booking_42 receive this\n",[63,2393,2394],{"class":65,"line":220},[63,2395,2396],{},"await ops.emit('bookings', {\n",[63,2398,2399],{"class":65,"line":235},[63,2400,2401],{},"    'status': 'confirmed',\n",[63,2403,2404],{"class":65,"line":240},[63,2405,2406],{},"    'guest': 'John Smith',\n",[63,2408,2409],{"class":65,"line":246},[63,2410,2411],{},"    'check_in': '2026-03-15'\n",[63,2413,2414],{"class":65,"line":272},[63,2415,2416],{},"}, EmitOptions(filter='booking_42'))  # the routing key\n",[63,2418,2419],{"class":65,"line":293},[63,2420,88],{"emptyLinePlaceholder":87},[63,2422,2423],{"class":65,"line":298},[63,2424,2396],{},[63,2426,2427],{"class":65,"line":304},[63,2428,2429],{},"    'status': 'cancelled',\n",[63,2431,2432],{"class":65,"line":335},[63,2433,2434],{},"    'reason': 'Guest request'\n",[63,2436,2437],{"class":65,"line":363},[63,2438,2439],{},"}, EmitOptions(filter='booking_87'))\n",[53,2441,2443],{"className":985,"code":2442,"filename":987,"language":988,"meta":59,"style":59},"package main\n\nimport (\n    \"log\"\n\n    nolag \"github.com/NoLagApp/go-sdk\"\n)\n\nconst appSlug = \"analytics-a3f9\" // the slug returned when you created the app\n\nfunc main() {\n    // Backend service: its own actor, so the dashboard actor receives what it publishes\n    publisher := nolag.New(\"metrics_service_access_token\")\n    if err := publisher.Connect(); err != nil {\n        log.Fatal(err)\n    }\n\n    ops := publisher.SetApp(appSlug).SetRoom(\"ops\")\n\n    // Only dashboard users watching booking_42 receive this\n    ops.Emit(\"bookings\", map[string]string{\n        \"status\":  \"confirmed\",\n        \"guest\":   \"John Smith\",\n        \"checkIn\": \"2026-03-15\",\n    }, nolag.EmitOptions{Filter: \"booking_42\"}) // the routing key\n\n    ops.Emit(\"bookings\", map[string]string{\n        \"status\": \"cancelled\",\n        \"reason\": \"Guest request\",\n    }, nolag.EmitOptions{Filter: \"booking_87\"})\n}\n",[33,2444,2445,2449,2453,2457,2461,2465,2469,2473,2477,2481,2485,2489,2494,2499,2504,2508,2512,2516,2521,2525,2530,2535,2540,2545,2550,2555,2559,2563,2568,2573,2578],{"__ignoreMap":59},[63,2446,2447],{"class":65,"line":66},[63,2448,995],{},[63,2450,2451],{"class":65,"line":84},[63,2452,88],{"emptyLinePlaceholder":87},[63,2454,2455],{"class":65,"line":91},[63,2456,1004],{},[63,2458,2459],{"class":65,"line":124},[63,2460,1009],{},[63,2462,2463],{"class":65,"line":129},[63,2464,88],{"emptyLinePlaceholder":87},[63,2466,2467],{"class":65,"line":135},[63,2468,1018],{},[63,2470,2471],{"class":65,"line":141},[63,2472,540],{},[63,2474,2475],{"class":65,"line":163},[63,2476,88],{"emptyLinePlaceholder":87},[63,2478,2479],{"class":65,"line":175},[63,2480,1031],{},[63,2482,2483],{"class":65,"line":186},[63,2484,88],{"emptyLinePlaceholder":87},[63,2486,2487],{"class":65,"line":214},[63,2488,1177],{},[63,2490,2491],{"class":65,"line":220},[63,2492,2493],{},"    // Backend service: its own actor, so the dashboard actor receives what it publishes\n",[63,2495,2496],{"class":65,"line":235},[63,2497,2498],{},"    publisher := nolag.New(\"metrics_service_access_token\")\n",[63,2500,2501],{"class":65,"line":240},[63,2502,2503],{},"    if err := publisher.Connect(); err != nil {\n",[63,2505,2506],{"class":65,"line":246},[63,2507,1213],{},[63,2509,2510],{"class":65,"line":272},[63,2511,1157],{},[63,2513,2514],{"class":65,"line":293},[63,2515,88],{"emptyLinePlaceholder":87},[63,2517,2518],{"class":65,"line":298},[63,2519,2520],{},"    ops := publisher.SetApp(appSlug).SetRoom(\"ops\")\n",[63,2522,2523],{"class":65,"line":304},[63,2524,88],{"emptyLinePlaceholder":87},[63,2526,2527],{"class":65,"line":335},[63,2528,2529],{},"    // Only dashboard users watching booking_42 receive this\n",[63,2531,2532],{"class":65,"line":363},[63,2533,2534],{},"    ops.Emit(\"bookings\", map[string]string{\n",[63,2536,2537],{"class":65,"line":635},[63,2538,2539],{},"        \"status\":  \"confirmed\",\n",[63,2541,2542],{"class":65,"line":650},[63,2543,2544],{},"        \"guest\":   \"John Smith\",\n",[63,2546,2547],{"class":65,"line":663},[63,2548,2549],{},"        \"checkIn\": \"2026-03-15\",\n",[63,2551,2552],{"class":65,"line":676},[63,2553,2554],{},"    }, nolag.EmitOptions{Filter: \"booking_42\"}) // the routing key\n",[63,2556,2557],{"class":65,"line":681},[63,2558,88],{"emptyLinePlaceholder":87},[63,2560,2561],{"class":65,"line":687},[63,2562,2534],{},[63,2564,2565],{"class":65,"line":716},[63,2566,2567],{},"        \"status\": \"cancelled\",\n",[63,2569,2570],{"class":65,"line":728},[63,2571,2572],{},"        \"reason\": \"Guest request\",\n",[63,2574,2575],{"class":65,"line":733},[63,2576,2577],{},"    }, nolag.EmitOptions{Filter: \"booking_87\"})\n",[63,2579,2580],{"class":65,"line":738},[63,2581,481],{},[2583,2584,2586],"callout",{"type":2585},"tip",[14,2587,2588,2591,2592,195,2595,195,2598,2601,2602,2605,2606,2611],{},[1350,2589,2590],{},"Tip:"," Filtering happens at the infrastructure level, no payload inspection, no wasted bandwidth. Each filter value is its own routing key, up to 100 per topic, and values cannot contain ",[33,2593,2594],{},"/",[33,2596,2597],{},"#",[33,2599,2600],{},"+",", or ",[33,2603,2604],{},"|",". See ",[2607,2608,2610],"a",{"href":2609},"/docs/concepts/filters","Topic Filters"," for full details.",[18,2613,2615],{"id":2614},"use-cases","Use Cases",[2617,2618,2619,2626,2632,2638],"ul",{},[2620,2621,2622,2625],"li",{},[1350,2623,2624],{},"Analytics dashboards"," - Page views, user sessions, conversion rates",[2620,2627,2628,2631],{},[1350,2629,2630],{},"Sales dashboards"," - Revenue, orders, inventory levels",[2620,2633,2634,2637],{},[1350,2635,2636],{},"Operations dashboards"," - Server health, error rates, response times",[2620,2639,2640,2643],{},[1350,2641,2642],{},"Trading dashboards"," - Stock prices, market data, portfolio values",[18,2645,2647],{"id":2646},"architecture","Architecture",[2649,2650,2651,2657,2660],"ol",{},[2620,2652,2653,2654,2656],{},"Backend services publish metrics to NoLag topics with their own ",[33,2655,43],{}," actor",[2620,2658,2659],{},"Dashboard clients subscribe to relevant topics with their own actors",[2620,2661,2662],{},"Charts and gauges update in real-time as data arrives",[18,2664,2666],{"id":2665},"best-practices","Best Practices",[2617,2668,2669,2676,2681,2684,2687,2699],{},[2620,2670,2671,2672,2675],{},"Use ",[1350,2673,2674],{},"filters"," to subscribe only to entities visible on screen to avoid receiving thousands of irrelevant updates",[2620,2677,2671,2678,2680],{},[33,2679,1656],{}," when the user navigates: the broker diffs the two lists, adds the new routing keys before removing the old ones, and never interrupts the filters that stay",[2620,2682,2683],{},"Use separate topics for different metric types",[2620,2685,2686],{},"Batch updates for high-frequency data to reduce rendering overhead; the broker accepts 50 publishes per second per connection",[2620,2688,2689,2690,2694,2695,2698],{},"Use a ",[2607,2691,2693],{"href":2692},"/docs/concepts/webhooks","hydration webhook"," to load the initial dashboard state on subscribe; its response arrives on the client's ",[33,2696,2697],{},"hydration"," event",[2620,2700,2701,2702,2706],{},"Consider data retention and historical data storage separately; NoLag delivers live updates and does not keep a history for you (see ",[2607,2703,2705],{"href":2704},"/docs/concepts/replay","Replay"," for what it does keep)",[18,2708,2710],{"id":2709},"next-steps","Next Steps",[2617,2712,2713,2718,2724],{},[2620,2714,2715,2717],{},[2607,2716,2610],{"href":2609}," - full reference for filter-based subscriptions",[2620,2719,2720],{},[2607,2721,2723],{"href":2722},"/docs/concepts/topics","Topics & Pub/Sub",[2620,2725,2726],{},[2607,2727,2729],{"href":2728},"/docs/concepts/qos","Quality of Service",[2731,2732,2733],"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":59,"searchDepth":84,"depth":84,"links":2735},[2736,2737,2738,2739,2742,2743,2744,2745],{"id":20,"depth":84,"text":21},{"id":27,"depth":84,"text":28},{"id":373,"depth":84,"text":374},{"id":1342,"depth":84,"text":1343,"children":2740},[2741],{"id":2103,"depth":91,"text":2104},{"id":2614,"depth":84,"text":2615},{"id":2646,"depth":84,"text":2647},{"id":2665,"depth":84,"text":2666},{"id":2709,"depth":84,"text":2710},"Build real-time dashboards and live data visualizations with NoLag.","md",{},"/docs/guides/dashboards",{"title":5,"description":2746},"docs/guides/dashboards","Bz6NSfKwFi6bWKU3oHH5NZO0IJaPA8SUk9eMUTQqwak",1789869419904]