Exports
Exports allow resources to share functions with other resources. The server example below uses ESX .
Defining Exports
Server Export
-- server.lua
exports('getPlayerData', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
return xPlayer
end)Client Export
-- client.lua
exports('showNotification', function(message)
-- Show notification
end)Consuming Exports
Server
local playerData = exports['myresource']:getPlayerData(source)Client
exports['myresource']:showNotification('Hello!')Namespacing
Use resource name as namespace:
-- Good
exports['qb-core']:GetPlayer(source)
-- Bad
exports:GetPlayer(source)Identifying the Caller
Inside an export, GetInvokingResource() returns the name of the resource
that called it. It returns nil when there is no invoking script runtime
(native ,
source ).
Use it to allow-list callers and to key per-caller state, then drop that state
when the caller stops. Listening to onResourceStop for another resource’s
name is reliable, because your own resource is still running. Cfx’s own
chat resource
uses this pattern for registerMessageHook.
-- server.lua (provider resource)
local allowedCallers = { ['my-shop'] = true }
local hooks = {}
exports('registerHook', function(fn)
local caller = GetInvokingResource()
if not caller or not allowedCallers[caller] then
return false
end
hooks[caller] = fn
return true
end)
AddEventHandler('onResourceStop', function(resourceName)
-- Drop the registration of a caller that stopped
hooks[resourceName] = nil
end)Error Handling Patterns
Checking Export Availability
Before calling an export, check if the resource is started to avoid errors.
if GetResourceState('ox_inventory') == 'started' then
local items = exports.ox_inventory:Items()
else
print('ox_inventory is not started')
endProtected Calls (pcall)
Use pcall when the export might fail internally or return unexpected data.
local success, result = pcall(function()
return exports['myresource']:riskyOperation()
end)
if success then
print('Result:', result)
else
print('Export call failed:', result)
endVersioning Strategies
When updating exports, maintain backward compatibility.
Strategy A: Argument Inspection
Check arguments inside the export to support multiple formats.
exports('giveItem', function(target, item, count)
if type(target) == 'table' then
-- Handle new object-style arguments
count = target.count
item = target.item
target = target.source
end
-- ... implementation
end)Strategy B: Versioned Names
Create new exports for breaking changes.
exports('giveItem', function(...) end) -- v1 (Deprecated)
exports('giveItem_v2', function(opts) end) -- v2Testing Exports
Create a test suite script to verify your exports behave as expected.
-- tests.lua
CreateThread(function()
Wait(1000) -- Wait for resources to load
print('Testing exports...')
local result = exports['myresource']:calculate(5, 5)
assert(result == 10, 'Math export failed')
print('Tests passed!')
end)Documentation Standards
Document your exports clearly in your README or code comments so other developers know how to use them.
--- @export getBalance
--- Retreives the current bank balance for a player
--- @param source number The player server ID
--- @param accountType string "bank" or "cash"
--- @return number The balance, or 0 if not found
exports('getBalance', function(source, accountType)
-- ...
end)Best Practices
- Use descriptive names:
getUservsget. - Return values: Always return something (even
falseornil) to confirm execution. - Async consideration: Remember exports are synchronous. For heavy tasks, use callbacks or events.