Basic CRUD Operations

Standard database operations with OxMySQL-like syntax

-- Insert a player
DB.insert('players', {
    identifier = 'license:abc123',
    name = 'John Doe',
    money = 5000
}, function(insertId, err)
    if not err then
        print("Player created:", insertId)
    end
end)

-- Select players
local players = DB.select('players', {
    where = {money = {operator = ">", value = 10000}},
    orderBy = {money = 'desc'},
    limit = 10
})

-- Update player
DB.update('players', {
    where = {identifier = license},
    set = {money = 7500, lastSeen = Date.now()}
})

-- Delete player
DB.delete('players', {
    where = {identifier = license}
})

Real-time Subscriptions

Live data updates without polling - the killer feature over MySQL

-- Subscribe to top players (updates automatically!)
local unsubscribe = DB.subscribe('players', {
    orderBy = {money = 'desc'},
    limit = 10,
    select = {'name', 'money'}
}, function(players)
    -- This runs automatically when data changes
    TriggerClientEvent('leaderboard:update', -1, players)
    print("Leaderboard updated with", #players, "players")
end)

-- No more polling loops!
-- Old way: while true do Citizen.Wait(5000) SELECT ... end
-- New way: Automatic push when data changes

-- Cleanup on resource stop
AddEventHandler('onResourceStop', function(resourceName)
    if resourceName == GetCurrentResourceName() then
        unsubscribe()
    end
end)

Atomic Transactions

Safe money transfers with automatic rollback on failure

function TransferMoney(from, to, amount)
    -- Get current balances
    local fromPlayer = DB.selectOne('players', {where = {identifier = from}})
    local toPlayer = DB.selectOne('players', {where = {identifier = to}})

    -- Validate
    if fromPlayer.money < amount then
        return false, "Insufficient funds"
    end

    -- Execute atomically (all succeed or all fail)
    local operations = {
        {type = "update", table = "players",
         where = {identifier = from},
         data = {money = fromPlayer.money - amount}},
        {type = "update", table = "players",
         where = {identifier = to},
         data = {money = toPlayer.money + amount}}
    }

    local p = promise.new()
    exports['cfx-db']:executeTransaction(operations, function(success, result)
        if success then p:resolve(true)
        else p:reject(result.error) end
    end)

    return Citizen.Await(p)
end

Relationships & Joins

Query related data across tables with automatic batching

-- Get player with all vehicles and inventory
DB.relations('players', {
    where = {identifier = license},
    relations = {
        {
            name = 'vehicles',
            table = 'vehicles',
            foreignKey = 'owner',
            localKey = 'identifier',
            type = 'many',
            select = {'plate', 'model', 'mileage'}
        },
        {
            name = 'inventory',
            table = 'inventory',
            foreignKey = 'owner',
            localKey = 'identifier',
            type = 'many'
        }
    }
}, function(players)
    local player = players[1]
    print(player.name, "has", #player.vehicles, "vehicles")
    print("Inventory:", #player.inventory, "items")
end)