PiTyUs.Hire me

Frameworks

Callbacks: asking the server a question and getting an answer

How callbacks work in FiveM: why events alone cannot return values, ox_lib lib.callback (client to server and server to client), ESX.RegisterServerCallback/TriggerServerCallback, QBCore CreateCallback/TriggerCallback, writing your own with request IDs, and securing callbacks.

Updated 11 min readBy PiTyUs · FiveM developer

Overview

Events are one-way: the client tells the server something and gets nothing back. But scripts constantly need answers — how much money do I have, is this garage slot free, can I open this door? A callback is an event pair with a reply: the client asks, the server answers, and the client code continues with the result.

Why events cannot return values

TriggerServerEvent sends a message and returns immediately; the server handler runs later, on another machine. To get an answer, the server must send a second event back, and the client must match that reply to the question. Callback helpers do exactly that for you. Events themselves are covered in events explained.

ox_lib callbacks

server.lualua
lib.callback.register('garage:getVehicles', function(source)
    local src = source
    local id = GetPlayerIdentifierByType(src, 'license')
    return MySQL.query.await('SELECT plate, model FROM player_vehicles WHERE license = ?', { id })
end)
client.lualua
-- waits for the answer (inside a thread / command / event handler)
local vehicles = lib.callback.await('garage:getVehicles', false)
print(#vehicles .. ' vehicles')

-- or with a function, without waiting
lib.callback('garage:getVehicles', false, function(result)
    print(#result)
end)

The second argument is a client-side rate limit in milliseconds (false for none). ox_lib also supports the reverse: the server calls lib.callback.await('name', playerId, ...) and the client answers with lib.callback.register — useful to ask a client for something only it knows, like its current waypoint. More about the library: ox_lib guide.

ESX callbacks

server.lualua
ESX.RegisterServerCallback('bank:getBalance', function(source, cb)
    local xPlayer = ESX.GetPlayerFromId(source)
    cb(xPlayer and xPlayer.getAccount('bank').money or 0)
end)
client.lualua
ESX.TriggerServerCallback('bank:getBalance', function(balance)
    ESX.ShowNotification(('Balance: $%d'):format(balance))
end)

QBCore callbacks

server.lualua
QBCore.Functions.CreateCallback('bank:getBalance', function(source, cb)
    local Player = QBCore.Functions.GetPlayer(source)
    cb(Player and Player.PlayerData.money.bank or 0)
end)
client.lualua
QBCore.Functions.TriggerCallback('bank:getBalance', function(balance)
    QBCore.Functions.Notify(('Balance: $%d'):format(balance), 'success')
end)

How it works underneath

Every helper follows the same pattern: give each request an ID, send it with the arguments, and resolve a promise when a reply with that ID arrives.

client.lua — a minimal callback implementationlua
local pending, nextId = {}, 0

function TriggerCallback(name, ...)
    nextId = nextId + 1
    local p = promise.new()
    pending[nextId] = p
    TriggerServerEvent('cb:request', name, nextId, ...)
    return Citizen.Await(p)
end

RegisterNetEvent('cb:response', function(id, ...)
    local p = pending[id]
    if p then pending[id] = nil; p:resolve({ ... }) end
end)

Use a maintained library in real code; this shows why a callback is just two events and a promise.

Securing callbacks

  • Anyone can call any registered callback with any arguments — check types, ownership and permissions.
  • Return only data the player may see (their own vehicles, not everyone’s).
  • Do heavy database work once and cache, not on every UI refresh.
  • Handle nil results on the client; a callback can fail.

The full checklist is in secure server events.

Frequently asked questions

What is a callback in FiveM?

A request/response pair: one side calls a named handler on the other side and receives its return value.

How do I get a value from the server in FiveM?

Use a callback — for example lib.callback.await('name', false) on the client with lib.callback.register on the server.

Can the server call a client callback?

Yes, with ox_lib: lib.callback.await('name', playerId, ...) on the server and lib.callback.register on the client.

Why does lib.callback.await error outside a thread?

Awaiting needs a coroutine. Call it inside a thread, command, event handler or NUI callback.

Need this built, not just explained?

Ten years of FiveM work, from Lua to NUI

Custom resources, React NUI, ESX / QBCore / Qbox integration, OneSync performance audits and security reviews — plus the websites and SEO around your server brand.

Related guides