Back to community

Pure NUI message bus that survives resource restarts

60 rep268 views1 min read

One pattern I refused to copy from old tutorials: NUI fetch() with hardcoded resource names. Nightmare to maintain.

Here's a tiny bus I use across all my resources:

-- ui_bus.lua (client)
local listeners = {}

RegisterNUICallback('bus', function(data, cb)
    local handlers = listeners[data.channel] or {}
    for _, h in ipairs(handlers) do h(data.payload) end
    cb({ ok = true })
end)

function busOn(channel, fn)
    listeners[channel] = listeners[channel] or {}
    table.insert(listeners[channel], fn)
end

function busSend(channel, payload)
    SendNUIMessage({ channel = channel, payload = payload })
end
// ui-bus.js (NUI side)
const listeners = {};

window.addEventListener('message', (e) => {
    const { channel, payload } = e.data;
    (listeners[channel] || []).forEach(fn => fn(payload));
});

export function on(channel, fn) {
    (listeners[channel] = listeners[channel] || []).push(fn);
}

export async function send(channel, payload) {
    return fetch(`https://${GetParentResourceName?.() || 'guardianfx-ui'}/bus`, {
        method: 'POST',
        body: JSON.stringify({ channel, payload }),
    }).then(r => r.json());
}

You can now bus.on('inventory:updated', ...) from JS and busSend('inventory:updated', items) from Lua, and none of your resources need to know each other's names.

Survives restarts, survives renames, survives moving features between resources.

26 1 commentsSign in to vote

Comments (1)

Sign in to leave a comment.
  • This is exactly what my old codebase needed. Refactoring tomorrow.

    7 points