The Database component provides a unified interface for both MongoDB (primary) and MySQL (compatibility) database operations.
Overview
Access the Database component via Database.Game (for game data) or Database.Auth (for authentication data) after the Core:Shared:Ready event.
Dual Database Architecture
Database . Game -- Primary database for game data (characters, inventory, vehicles, etc.)
Database . Auth -- Separate database for authentication (users, roles, permissions)
MongoDB Primary Document database for game data, characters, inventory
MySQL Support Relational database via oxmysql for compatibility
Promise-Based Async/await patterns with callbacks
Type-Safe Automatic data type handling
Server-Side Only: Database operations are only available on the server. Never attempt database queries from client-side code.
MongoDB Operations
MongoDB is the primary database for Mythic Framework, storing characters, inventory, vehicles, and most game data.
findOne
Find a single document matching the query.
Database . Game : findOne ( params , callback )
Parameter table with the following fields:
collection (string) - Collection name
query (table) - Query filter
options (table, optional) - Query options (projection, etc.)
Callback function receiving (success, result)
success (boolean) - Whether query succeeded
result (table|nil) - Document found, or nil if not found
Examples:
-- Basic findOne
Database . Game : findOne ({
collection = 'characters' ,
query = {
SID = 1
}
}, function ( success , character )
if success and character then
print ( 'Found character:' , character . First , character . Last )
else
print ( 'Character not found' )
end
end )
-- Query by multiple fields
Database . Game : findOne ({
collection = 'inventory' ,
query = {
owner = characterId ,
type = 'main'
}
}, function ( success , inventory )
if success and inventory then
-- Process inventory
end
end )
-- With projection (only specific fields)
Database . Game : findOne ({
collection = 'characters' ,
query = { SID = 1 },
options = {
projection = {
First = 1 ,
Last = 1 ,
Cash = 1
}
}
}, function ( success , character )
-- character only contains First, Last, Cash fields
end )
find
Find multiple documents matching the query.
Database . Game : find ( params , callback )
Parameter table with the following fields:
collection (string) - Collection name
query (table) - Query filter
options (table, optional) - Query options:
limit (number) - Maximum documents to return
skip (number) - Skip first N documents
sort (table) - Sort order { field = 1 } (asc) or { field = -1 } (desc)
projection (table) - Fields to include/exclude
Callback function receiving (success, results)
success (boolean) - Whether query succeeded
results (table) - Array of documents (empty array if none found)
Examples:
-- Find all documents
Database . Game : find ({
collection = 'characters' ,
query = {}
}, function ( success , allCharacters )
if success then
print ( 'Found' , # allCharacters , 'characters' )
end
end )
-- Find with filter
Database . Game : find ({
collection = 'characters' ,
query = {
[ 'Jobs.Id' ] = 'police' -- Query array fields
}
}, function ( success , policeOfficers )
-- Process results
end )
-- Find with limit and sort
Database . Game : find ({
collection = 'characters' ,
query = {},
options = {
limit = 10 ,
sort = { createdAt = - 1 } -- Newest first
}
}, function ( success , recentCharacters )
-- Process results
end )
-- Find with pagination
Database . Game : find ({
collection = 'characters' ,
query = {},
options = {
skip = 20 ,
limit = 20 -- Page 2 (20 per page)
}
}, function ( success , page2 )
-- Process results
end )
-- Find with projection
Database . Game : find ({
collection = 'characters' ,
query = {},
options = {
projection = {
First = 1 ,
Last = 1 ,
_id = 0 -- Exclude _id
}
}
}, function ( success , characterNames )
-- Results only contain First and Last fields
end )
-- Complex query
local activePolice = COMPONENTS . Database : find ( 'characters' , {
job = 'police' ,
onDuty = true
}, {
limit = 50 ,
sort = { rank = - 1 }
})
insertOne
Insert a single document into a collection.
COMPONENTS . Database : insertOne ( collection , document , callback )
Document to insert as key-value pairs
Callback function receiving (success, insertedId)
Examples:
-- Insert character
COMPONENTS . Database : insertOne ( 'characters' , {
SID = 1 ,
First = 'John' ,
Last = 'Doe' ,
DOB = '1990-01-15' ,
Gender = 'Male' ,
job = 'unemployed' ,
createdAt = os.time ()
}, function ( success , id )
if success then
print ( 'Character created with ID:' , id )
else
print ( 'Failed to create character' )
end
end )
-- Insert inventory
COMPONENTS . Database : insertOne ( 'inventory' , {
owner = characterId ,
type = 'main' ,
items = {},
maxSlots = 50 ,
maxWeight = 100
})
-- Insert log entry
COMPONENTS . Database : insertOne ( 'logs' , {
type = 'admin_action' ,
admin = adminId ,
action = 'teleport' ,
target = targetId ,
timestamp = os.time ()
})
insertMany
Insert multiple documents at once.
COMPONENTS . Database : insertMany ( collection , documents , callback )
Array of documents to insert
Callback function receiving (success, insertedIds)
Example:
-- Insert multiple items
local items = {
{ name = 'water' , label = 'Water Bottle' , weight = 0.5 },
{ name = 'sandwich' , label = 'Sandwich' , weight = 0.3 },
{ name = 'phone' , label = 'Phone' , weight = 0.2 }
}
COMPONENTS . Database : insertMany ( 'items' , items , function ( success , ids )
if success then
print ( 'Inserted' , # ids , 'items' )
end
end )
updateOne
Update a single document matching the query.
COMPONENTS . Database : updateOne ( collection , query , update , callback )
Query filter to find document to update
Update operations using MongoDB update operators ($set, $inc, $push, etc.)
Callback function receiving (success, modifiedCount)
Examples:
-- Update character job
COMPONENTS . Database : updateOne ( 'characters' ,
{ SID = 1 },
{
[ '$set' ] = {
job = 'police' ,
grade = 'officer' ,
onDuty = true
}
}
)
-- Increment money
COMPONENTS . Database : updateOne ( 'characters' ,
{ SID = characterId },
{
[ '$inc' ] = {
cash = 500 -- Add $500
}
}
)
-- Push to array
COMPONENTS . Database : updateOne ( 'characters' ,
{ SID = characterId },
{
[ '$push' ] = {
licenses = 'drivers_license'
}
}
)
-- Update nested field
COMPONENTS . Database : updateOne ( 'inventory' ,
{ owner = characterId },
{
[ '$set' ] = {
[ 'items.1.count' ] = 5 -- Update item count in slot 1
}
}
)
-- Multiple operations
COMPONENTS . Database : updateOne ( 'characters' ,
{ SID = characterId },
{
[ '$set' ] = {
job = 'police' ,
lastUpdated = os.time ()
},
[ '$inc' ] = {
arrests = 1
}
}
)
updateMany
Update all documents matching the query.
COMPONENTS . Database : updateMany ( collection , query , update , callback )
Example:
-- Clock out all police officers
COMPONENTS . Database : updateMany ( 'characters' ,
{ job = 'police' , onDuty = true },
{
[ '$set' ] = {
onDuty = false
}
},
function ( success , count )
print ( 'Clocked out' , count , 'officers' )
end
)
-- Reset all character positions (server restart)
COMPONENTS . Database : updateMany ( 'characters' ,
{},
{
[ '$set' ] = {
lastPosition = { x = 0 , y = 0 , z = 0 }
}
}
)
deleteOne
Delete a single document matching the query.
COMPONENTS . Database : deleteOne ( collection , query , callback )
Query filter to find document to delete
Callback function receiving (success, deletedCount)
Examples:
-- Delete character
COMPONENTS . Database : deleteOne ( 'characters' , { SID = 1 }, function ( success , count )
if success and count > 0 then
print ( 'Character deleted' )
else
print ( 'Character not found' )
end
end )
-- Delete inventory
COMPONENTS . Database : deleteOne ( 'inventory' , { owner = characterId })
-- Delete old log entry
COMPONENTS . Database : deleteOne ( 'logs' , { _id = logId })
deleteMany
Delete all documents matching the query.
COMPONENTS . Database : deleteMany ( collection , query , callback )
Example:
-- Delete old logs (older than 30 days)
local thirtyDaysAgo = os.time () - ( 30 * 24 * 60 * 60 )
COMPONENTS . Database : deleteMany ( 'logs' ,
{ timestamp = { [ '$lt' ] = thirtyDaysAgo } },
function ( success , count )
print ( 'Deleted' , count , 'old logs' )
end
)
-- Delete all vehicles for a character
COMPONENTS . Database : deleteMany ( 'vehicles' , { owner = characterId })
count
Count documents matching the query.
COMPONENTS . Database : count ( collection , query , callback )
Callback function receiving (success, count)
Examples:
-- Count all characters
local totalCharacters = COMPONENTS . Database : count ( 'characters' , {})
print ( 'Total characters:' , totalCharacters )
-- Count online police
local onlinePolice = COMPONENTS . Database : count ( 'characters' , {
job = 'police' ,
onDuty = true
})
-- Count vehicles by type
local sportsCars = COMPONENTS . Database : count ( 'vehicles' , {
class = 'Sports'
})
aggregate
Perform aggregation operations (advanced queries).
COMPONENTS . Database : aggregate ( collection , pipeline , callback )
Array of aggregation stages
Callback function receiving (success, results)
Examples:
-- Count characters by job
COMPONENTS . Database : aggregate ( 'characters' , {
{
[ '$group' ] = {
_id = '$job' ,
count = { [ '$sum' ] = 1 }
}
},
{
[ '$sort' ] = { count = - 1 }
}
}, function ( success , results )
for _ , result in ipairs ( results ) do
print ( result . _id , 'has' , result . count , 'members' )
end
end )
-- Get richest players
COMPONENTS . Database : aggregate ( 'characters' , {
{
[ '$project' ] = {
name = { [ '$concat' ] = { '$First' , ' ' , '$Last' } },
totalMoney = { [ '$add' ] = { '$cash' , '$bank' } }
}
},
{
[ '$sort' ] = { totalMoney = - 1 }
},
{
[ '$limit' ] = 10
}
})
MySQL Operations
MySQL operations use oxmysql for compatibility with existing FiveM resources.
fetchAll
Execute a SELECT query and return all results.
COMPONENTS . Database . MySQL : fetchAll ( query , parameters , callback )
SQL SELECT query (use ? for parameters)
Array of parameter values to replace ? in query
Callback function receiving results
Examples:
-- Fetch all banned players
local banned = COMPONENTS . Database . MySQL : fetchAll ( 'SELECT * FROM bans WHERE active = ?' , { 1 })
for _ , ban in ipairs ( banned ) do
print ( 'Banned:' , ban . player_name , 'Reason:' , ban . reason )
end
-- Fetch characters for player
COMPONENTS . Database . MySQL : fetchAll (
'SELECT * FROM characters WHERE owner = ? ORDER BY created_at DESC' ,
{ playerId },
function ( characters )
print ( 'Found' , # characters , 'characters' )
end
)
-- Join query
local vehicles = COMPONENTS . Database . MySQL : fetchAll ( [[
SELECT v.*, c.First, c.Last
FROM vehicles v
JOIN characters c ON v.owner = c.SID
WHERE v.type = ?
]] , { 'car' })
fetchScalar
Execute a query and return a single value.
COMPONENTS . Database . MySQL : fetchScalar ( query , parameters , callback )
Examples:
-- Count total characters
local count = COMPONENTS . Database . MySQL : fetchScalar ( 'SELECT COUNT(*) FROM characters' )
print ( 'Total characters:' , count )
-- Get specific value
local cash = COMPONENTS . Database . MySQL : fetchScalar (
'SELECT cash FROM characters WHERE SID = ?' ,
{ characterId }
)
execute
Execute an INSERT, UPDATE, or DELETE query.
COMPONENTS . Database . MySQL : execute ( query , parameters , callback )
SQL query (INSERT, UPDATE, DELETE)
Array of parameter values
Callback receiving affected rows or insert ID
Examples:
-- Insert
COMPONENTS . Database . MySQL : execute (
'INSERT INTO logs (type, player, action, timestamp) VALUES (?, ?, ?, ?)' ,
{ 'admin' , playerId , 'teleport' , os.time () },
function ( insertId )
print ( 'Log created with ID:' , insertId )
end
)
-- Update
COMPONENTS . Database . MySQL : execute (
'UPDATE characters SET cash = cash + ? WHERE SID = ?' ,
{ 500 , characterId }
)
-- Delete
COMPONENTS . Database . MySQL : execute (
'DELETE FROM logs WHERE timestamp < ?' ,
{ thirtyDaysAgo },
function ( affectedRows )
print ( 'Deleted' , affectedRows , 'old logs' )
end
)
transaction
Execute multiple queries in a transaction.
COMPONENTS . Database . MySQL : transaction ( queries , callback )
Example:
-- Transfer money between characters
COMPONENTS . Database . MySQL : transaction ({
{
query = 'UPDATE characters SET cash = cash - ? WHERE SID = ?' ,
parameters = { amount , senderId }
},
{
query = 'UPDATE characters SET cash = cash + ? WHERE SID = ?' ,
parameters = { amount , receiverId }
},
{
query = 'INSERT INTO transactions (from, to, amount, timestamp) VALUES (?, ?, ?, ?)' ,
parameters = { senderId , receiverId , amount , os.time () }
}
}, function ( success )
if success then
print ( 'Transaction completed' )
else
print ( 'Transaction failed (rolled back)' )
end
end )
Best Practices
Use MongoDB for Game Data
MongoDB is preferred for:
Characters, inventory, vehicles (flexible schema)
Nested data structures
Arrays and embedded documents
Rapidly changing data
MySQL is better for:
Transactional data (banking, trades)
Relations between entities
Legacy compatibility
Complex joins
-- ✅ Good: Store character in MongoDB
COMPONENTS . Database : insertOne ( 'characters' , {
SID = 1 ,
First = 'John' ,
Last = 'Doe' ,
metadata = { -- Nested data
appearance = { ... },
tattoos = { ... }
},
licenses = { 'drivers' , 'weapon' } -- Array
})
❌ Dangerous (SQL Injection): local query = 'SELECT * FROM users WHERE name = "' .. playerName .. '"'
COMPONENTS . Database . MySQL : fetchAll ( query ) -- Vulnerable!
✅ Safe (Parameterized): COMPONENTS . Database . MySQL : fetchAll (
'SELECT * FROM users WHERE name = ?' ,
{ playerName } -- Safe from injection
)
-- ❌ Bad: Assume success
local character = COMPONENTS . Database : findOne ( 'characters' , { SID = id })
print ( character . First ) -- May error if not found
-- ✅ Good: Check result
local character = COMPONENTS . Database : findOne ( 'characters' , { SID = id })
if character then
print ( character . First )
else
COMPONENTS . Logger : Warn ( 'Database' , 'Character not found' , { SID = id })
end
-- ✅ Better: Use callback with error handling
COMPONENTS . Database : findOne ( 'characters' , { SID = id }, function ( success , character )
if success and character then
print ( character . First )
else
COMPONENTS . Logger : Error ( 'Database' , 'Failed to fetch character' )
end
end )
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 })
-- MySQL
CREATE INDEX idx_owner ON vehicles( owner );
CREATE INDEX idx_timestamp ON logs( timestamp );
❌ Bad: Fetch everything local allCharacters = COMPONENTS . Database : find ( 'characters' , {})
for _ , char in ipairs ( allCharacters ) do
if char . job == 'police' then
-- Process
end
end
✅ Good: Filter at database local police = COMPONENTS . Database : find ( 'characters' , { job = 'police' })
for _ , char in ipairs ( police ) do
-- Process
end
✅ Better: Use projection local police = COMPONENTS . Database : find ( 'characters' ,
{ job = 'police' },
{
projection = { First = 1 , Last = 1 , rank = 1 } -- Only needed fields
}
)
Next Steps
Performance Note: Database queries are I/O operations and can be slow. Always use indexes, limit results, and avoid queries in tight loops or every frame.