The Database component provides access to MongoDB for all persistent data storage. It uses a dual-database architecture with separate databases for authentication and game data.
Overview
Access the Database component via Database.Game (for game data) or Database.Auth (for authentication data).
Database . Game -- Characters, vehicles, inventory, properties, phone data, etc.
Database . Auth -- Accounts, bans
Server-Side Only: Database operations are only available on the server. Never attempt database queries from client-side code.
Params Table Pattern: All MongoDB methods use a single params table as the first argument — NOT separate positional arguments. See examples below.
Call Signature
Every database method follows the same pattern:
Database . Game : methodName ({
collection = "collectionName" ,
query = { ... }, -- for find/update/delete
document = { ... }, -- for insertOne
documents = { ... }, -- for insert (bulk)
update = { ... }, -- for update/updateOne
options = { ... }, -- optional MongoDB options
limit = 10 , -- optional result limit (find)
aggregate = { ... }, -- pipeline stages (aggregate only)
}, function ( success , result , insertedIds )
-- success (boolean) - whether the operation succeeded
-- result - data on success, error message string on failure
-- insertedIds - array of IDs (insert operations only)
end )
Methods
findOne
Find a single document matching the query.
Database . Game : findOne ({
collection = "characters" ,
query = { SID = 1 }
}, function ( success , results )
if success and results and # results > 0 then
local character = results [ 1 ]
print ( "Found:" , character . First , character . Last )
end
end )
findOne internally calls find with limit = 1. The callback receives an array — access the first element with results[1].
With projection (return only specific fields):
Database . Game : findOne ({
collection = "characters" ,
query = { SID = 1 },
options = {
projection = { First = 1 , Last = 1 , Cash = 1 }
}
}, function ( success , results )
if success and results and # results > 0 then
-- results[1] only contains First, Last, Cash, _id
end
end )
find
Find multiple documents matching the query.
Database . Game : find ({
collection = "characters" ,
query = { [ "Jobs.Id" ] = "police" }
}, function ( success , results )
if success then
print ( "Found" , # results , "police officers" )
end
end )
With limit:
Database . Game : find ({
collection = "characters" ,
query = {},
limit = 10
}, function ( success , results )
if success then
for _ , char in ipairs ( results ) do
print ( char . First , char . Last )
end
end
end )
With options (sort, projection):
Database . Game : find ({
collection = "characters" ,
query = {},
options = {
sort = { createdAt = - 1 },
projection = { First = 1 , Last = 1 , _id = 0 }
},
limit = 20
}, function ( success , results )
-- 20 most recent characters, names only
end )
insertOne
Insert a single document.
Database . Game : insertOne ({
collection = "characters" ,
document = {
SID = 1 ,
First = "John" ,
Last = "Doe" ,
DOB = "1990-01-15" ,
Gender = "Male" ,
createdAt = os.time ()
}
}, function ( success , count , ids )
if success then
print ( "Inserted with ID:" , ids [ 1 ])
end
end )
insertOne wraps the document into a single-element array and calls insert internally. The callback signature is (success, insertedCount, arrayOfIds).
insert
Insert multiple documents at once (bulk insert).
Database . Game : insert ({
collection = "logs" ,
documents = {
{ type = "join" , player = "John" , timestamp = os.time () },
{ type = "join" , player = "Jane" , timestamp = os.time () },
{ type = "join" , player = "Bob" , timestamp = os.time () }
}
}, function ( success , insertedCount , ids )
if success then
print ( "Inserted" , insertedCount , "documents" )
end
end )
The method name is insert, NOT insertMany. The params.documents field must be an array of document tables.
updateOne
Update a single document matching the query.
Database . Game : updateOne ({
collection = "characters" ,
query = { SID = 1 },
update = {
[ "$set" ] = {
job = "police" ,
grade = "officer"
}
}
}, function ( success , modifiedCount )
if success then
print ( "Modified" , modifiedCount , "documents" )
end
end )
Increment values:
Database . Game : updateOne ({
collection = "characters" ,
query = { SID = 1 },
update = {
[ "$inc" ] = { cash = 500 }
}
})
Push to array:
Database . Game : updateOne ({
collection = "characters" ,
query = { SID = 1 },
update = {
[ "$push" ] = { licenses = "drivers_license" }
}
})
Multiple operations at once:
Database . Game : updateOne ({
collection = "characters" ,
query = { SID = 1 },
update = {
[ "$set" ] = { job = "police" , lastUpdated = os.time () },
[ "$inc" ] = { arrests = 1 }
}
})
update
Update all documents matching the query (bulk update).
Database . Game : update ({
collection = "characters" ,
query = { job = "police" , onDuty = true },
update = {
[ "$set" ] = { onDuty = false }
}
}, function ( success , modifiedCount )
if success then
print ( "Clocked out" , modifiedCount , "officers" )
end
end )
The method name is update, NOT updateMany. Without the internal isUpdateOne flag, update applies to ALL matching documents.
deleteOne
Delete a single document matching the query.
Database . Game : deleteOne ({
collection = "characters" ,
query = { SID = 1 }
}, function ( success , deletedCount )
if success and deletedCount > 0 then
print ( "Character deleted" )
end
end )
delete
Delete all documents matching the query (bulk delete).
local thirtyDaysAgo = os.time () - ( 30 * 24 * 60 * 60 )
Database . Game : delete ({
collection = "logs" ,
query = { timestamp = { [ "$lt" ] = thirtyDaysAgo } }
}, function ( success , deletedCount )
if success then
print ( "Deleted" , deletedCount , "old logs" )
end
end )
The method name is delete, NOT deleteMany. Without the internal isDeleteOne flag, delete removes ALL matching documents.
findOneAndUpdate
Atomically find a document, update it, and return the result.
Database . Game : findOneAndUpdate ({
collection = "sequences" ,
query = { _id = "characterSID" },
update = {
[ "$inc" ] = { value = 1 }
},
options = {
returnDocument = "after" -- return the updated document
}
}, function ( success , document )
if success and document then
print ( "New sequence value:" , document . value )
end
end )
This method is useful for atomic operations like generating sequential IDs.
count
Count documents matching the query.
Database . Game : count ({
collection = "characters" ,
query = { job = "police" , onDuty = true }
}, function ( success , count )
if success then
print ( "Online police:" , count )
end
end )
aggregate
Run an aggregation pipeline.
Database . Game : aggregate ({
collection = "characters" ,
aggregate = {
{
[ "$group" ] = {
_id = "$job" ,
count = { [ "$sum" ] = 1 }
}
},
{
[ "$sort" ] = { count = - 1 }
}
}
}, function ( success , results )
if success then
for _ , result in ipairs ( results ) do
print ( result . _id , "has" , result . count , "members" )
end
end
end )
The pipeline stages go in params.aggregate (NOT a separate pipeline argument).
Auth Database
The Auth database has the same methods as Game. Use it for accounts and bans:
-- Find account
Database . Auth : findOne ({
collection = "accounts" ,
query = { Identifier = identifier }
}, function ( success , results )
if success and results and # results > 0 then
local account = results [ 1 ]
print ( "Account:" , account . AccountID )
end
end )
-- Check for ban
Database . Auth : findOne ({
collection = "bans" ,
query = { Identifier = identifier , active = true }
}, function ( success , results )
if success and results and # results > 0 then
print ( "Player is banned:" , results [ 1 ]. reason )
end
end )
Deprecated Methods
The top-level Database:method() calls (without .Game or .Auth) still work but print deprecation warnings. Always use Database.Game:method() or Database.Auth:method().
-- ❌ Deprecated (prints warning)
Database : findOne ({ collection = "characters" , query = { SID = 1 } }, callback )
-- ✅ Correct
Database . Game : findOne ({ collection = "characters" , query = { SID = 1 } }, callback )
MySQL (oxmysql)
MySQL is available via the oxmysql resource for the inventory system and compatibility. It is NOT accessed through the Database component — use oxmysql exports directly:
-- oxmysql exports (not part of Database component)
exports . oxmysql : execute ( "SELECT * FROM inventory WHERE id = ?" , { itemId }, function ( results )
-- Process results
end )
exports . oxmysql : scalar ( "SELECT COUNT(*) FROM inventory" , {}, function ( count )
print ( "Total items:" , count )
end )
exports . oxmysql : insert ( "INSERT INTO inventory (name, item_id, slot) VALUES (?, ?, ?)" , {
name , itemId , slot
}, function ( insertId )
print ( "Inserted row:" , insertId )
end )
See oxmysql documentation for full API reference.
Best Practices
Always Use Database.Game or Database.Auth
Never use the top-level deprecated methods: -- ✅ Good
Database . Game : findOne ({ collection = "characters" , query = { SID = 1 } }, cb )
Database . Auth : findOne ({ collection = "accounts" , query = { id = 1 } }, cb )
-- ❌ Bad (deprecated, prints warning)
Database : findOne ({ collection = "characters" , query = { SID = 1 } }, cb )
Handle Errors in Callbacks
Database . Game : findOne ({
collection = "characters" ,
query = { SID = id }
}, function ( success , results )
if not success then
print ( "Database error:" , results ) -- results is error message on failure
return
end
if not results or # results == 0 then
print ( "Character not found" )
return
end
local character = results [ 1 ]
-- Safe to use character
end )
Use Params Table Correctly
All methods take a single params table. The fields vary by method: Method Required Fields Optional Fields find / findOnecollection, queryoptions, limitinsertOnecollection, documentoptionsinsertcollection, documentsoptionsupdateOne / updatecollection, query, updateoptionsdeleteOne / deletecollection, queryoptionsfindOneAndUpdatecollection, query, updateoptionscountcollection, queryoptionsaggregatecollection, aggregate—
Create indexes for frequently queried fields: // MongoDB shell
db . characters . createIndex ({ SID: 1 }, { unique: true })
db . inventory . createIndex ({ owner: 1 })
db . vehicles . createIndex ({ owner: 1 , plate: 1 })
db . logs . createIndex ({ timestamp: - 1 })
Next Steps
Base API Core framework exports
Logger API Logging component
Database Architecture Understanding the database system
Database Setup Configure databases