Exports: share code between resources
An export lets one resource call a function that lives in another resource. That is the whole idea. Instead of copying the same code into every script that needs it, you write the function once in one resource, hand it out, and let other resources call it by name. This is exactly how the big tools you will use later, like ox_lib, frameworks, and inventories, let your script borrow their features.
What an export is
You already know what a function is: a named block of code you run by calling its name. An export takes that one step further. It is a function one resource offers to every other resource on the server, so they can run it too.
A function normally lives inside one file and only that file can call it. An export punches a small, deliberate hole through that wall. The resource that owns the function says "here, anyone may call this one", and other resources call it across the gap.
There are two moves, and only two. First you register the export in the resource that owns the function:
exports('getMoney', getMoney)Then, from a different resource, you call it by naming the resource and the export:
exports.qu_bank:getMoney(playerId)The first line publishes a function under the name getMoney. The second line, written in a totally separate resource, runs that function and gets the answer back. We will build both halves in a minute. For now, just hold the shape in your head: one resource offers, another calls.
Vocabulary
- export
- A function one resource offers to other resources, so they can call it across the resource boundary.
- resource
- A single folder of code that the server loads and runs as one unit. A server runs many resources side by side.
- provider
- The resource that owns the function and registers the export. It hands the function out.
- consumer
- The resource that calls another resource's export. It borrows the function instead of writing its own.
Why exports exist
Picture a server with a bank resource, a job resource, and a shop resource. All three need to know how much money a player has. Without exports, each one would write its own copy of the "read this player's balance" code. Three copies of the same logic. The day you change how money is stored, you have to find and fix all three, and the day you forget one, you get a bug that only shows up in the shop.
Exports kill that duplication. The bank resource owns the money. It registers one export, getMoney. The job resource and the shop resource just call exports.qu_bank:getMoney(...) whenever they need a balance. One source of truth, one place to fix, and everyone else simply asks.
This is also how every framework and library you will ever use works. When you write exports.ox_inventory:GetItemCount(...), you are not running your own code. You are calling a function that lives inside the ox_inventory resource, written by someone else, offered to you as an export. Learning to register and call exports is learning the language that every serious FiveM resource speaks to every other.
The two halves
Every export is two pieces of code in two different places: the resource that provides the function, and the resource that calls it. Get both halves clear and exports stop being mysterious.
Providing: register the function
In the resource that owns the function, you call exports as a function. You hand it two things: the public name other resources will type, and the actual Lua function to run.
local function getMoney(playerId)
-- look up and return this player's balance
return 500
end
exports('getMoney', function(playerId)
return getMoney(playerId)
end)Read exports('getMoney', ...) as "publish a function under the name getMoney". The first argument is the public name, a string. The second argument is the function to run when someone calls that name. The public name and your own function name do not have to match, which lets you keep your real function local and private while still offering one clean name to the outside world.
Calling: name the resource, then the export
In a different resource, you reach the export by naming the resource folder, then the export, joined with a colon.
local balance = exports.qu_bank:getMoney(playerId)Read it left to right. exports.qu_bank picks the resource you want, by its folder name. The colon then calls its published getMoney function and passes playerId. The answer comes straight back into balance, exactly like calling any normal function. Do not drop the colon. exports.qu_bank:getMoney(playerId) is the form FiveM expects.
Build it
You will make two tiny resources. qu_bank provides an export, and qu_bank_user calls it. When you restart both and run a command, the called value prints in your server console.
Make the two resource folders
Inside your server's resources folder, create both of these folders:
resources/qu_bank
resources/qu_bank_userWrite qu_bank (the provider)
Create qu_bank/fxmanifest.lua with this:
fx_version 'cerulean'
game 'gta5'
server_script 'server.lua'Then create qu_bank/server.lua with this:
local function getMoney(playerId)
return 500
end
exports('getMoney', function(playerId)
return getMoney(playerId)
end)Write qu_bank_user (the consumer)
Create qu_bank_user/fxmanifest.lua with this:
fx_version 'cerulean'
game 'gta5'
dependencies {
'qu_bank'
}
server_script 'server.lua'Then create qu_bank_user/server.lua with this:
RegisterCommand('checkbank', function(source)
local balance = exports.qu_bank:getMoney(source)
print('[qu_bank_user] balance is ' .. balance)
end, true)The dependencies block tells FiveM to start qu_bank first, every time, so the export exists before anyone calls it.
Start both, then test
Open server.cfg and add both lines, provider first:
ensure qu_bank
ensure qu_bank_userSave. In the txAdmin Live Console (the box where you type server commands), start both resources, provider first:
ensure qu_bank
ensure qu_bank_userNow run the command itself in that same console:
checkbankThe 500 came back across the resource boundary from qu_bank. You just called a function that lives in one resource from a completely separate one.
Common beginner mistakes
| Symptom | Fix |
|---|---|
No such export getMoney in resource qu_bank | Three things to check. First, is qu_bank actually started and green in txAdmin? An export from a stopped resource does not exist. Second, is the name spelled and capitalised exactly the same in both the exports('getMoney', ...) line and the exports.qu_bank:getMoney() call? Lua is case sensitive. Third, are both sides server code? Calling a server export from a client script gives this same error, because the export was never registered on the client side. |
attempt to index a nil value (field 'qu_bank') | qu_bank is not running, or the resource name is misspelled in the call. Confirm ensure qu_bank is in server.cfg and the resource is started before qu_bank_user calls it. The dependencies block in the consumer manifest is what guarantees that order. |
The export call errors only sometimes, right after a restart | You called the export before the provider had finished loading. Add the dependencies { 'qu_bank' } block to the consumer manifest so FiveM always starts the provider first, instead of leaving the load order to luck. |
You get 'No such export'. Name two things to check first.
Two of the most common causes. One, the provider resource is not running. If qu_bank is stopped or crashed, its exports do not exist, so check that it is started and green in txAdmin. Two, the export name is misspelled or differs in capitalisation between where you registered it, exports('getMoney', ...), and where you call it, exports.qu_bank:getMoney(). Lua is case sensitive, so getMoney and GetMoney are two different names. A third worth remembering: you may be calling a server export from a client script, which never sees it.
Try it yourself
What you can do now
- Explain that an export is a function one resource offers to other resources.
- Say why exports exist: so resources reuse each other's code instead of copying it.
- Register an export with exports('name', fn) in the resource that owns the function.
- Call another resource's export with exports.resourceName:name(args) and use the value it returns.
- Remember that server exports and client exports stay on their own side and do not cross over.
You can now wire two resources together, which is most of what real FiveM development is: small pieces calling each other's exports. The next thing every resource needs is a tidy place to keep settings you can change without touching the logic, like a starting balance or a list of allowed jobs. That is what a config file is for. Next up: "config.lua: settings you can safely change".