# Best Practices

Follow these guidelines to build high-quality NOVA scripts.

## Security

### Always Validate Server-Side

```lua
-- ❌ BAD: Trusting client data
RegisterNetEvent('shop:buyItem')
AddEventHandler('shop:buyItem', function(item, price)
    local player = Nova.Functions.GetPlayer(source)
    player:RemoveMoney(price)  -- Client could send price = 0!
    player:AddItem(item, 1)
end)

-- ✅ GOOD: Server validates everything
RegisterNetEvent('shop:buyItem')
AddEventHandler('shop:buyItem', function(itemName)
    local source = source
    local player = Nova.Functions.GetPlayer(source)
    if not player then return end
    
    local item = Config.ShopItems[itemName]
    if not item then return end  -- Item doesn't exist
    
    if player:GetMoney() < item.price then
        Nova.Functions.Notify(source, 'Not enough money', 'error')
        return
    end
    
    player:RemoveMoney(item.price)
    player:AddItem(itemName, 1)
    Nova.Functions.Notify(source, 'Purchased ' .. item.label, 'success')
end)
```

### Never Expose Sensitive Data to Client

```lua
-- ❌ BAD: Sending all player data
TriggerClientEvent('sendData', source, player)

-- ✅ GOOD: Send only what's needed
TriggerClientEvent('sendData', source, {
    name = player.fullname,
    job = player:GetJob().label,
})
```

## Performance

### Use Callbacks Instead of Polling

```lua
-- ❌ BAD: Polling every frame
CreateThread(function()
    while true do
        Wait(0)
        -- Check something every frame... 
    end
end)

-- ✅ GOOD: Event-driven
AddEventHandler('Nova:PlayerLoaded', function(source, player)
    -- React to events instead
end)
```

### Minimize Database Queries

```lua
-- ❌ BAD: Query on every action
RegisterNetEvent('doAction')
AddEventHandler('doAction', function()
    local result = MySQL.Sync.fetchAll('SELECT * FROM ...', {})
    -- ...
end)

-- ✅ GOOD: Cache and use player object
RegisterNetEvent('doAction')
AddEventHandler('doAction', function()
    local player = Nova.Functions.GetPlayer(source)
    -- Player data is already cached in memory
    local money = player:GetMoney()  -- No DB query needed
end)
```

### Use Proper Wait Times

```lua
-- ❌ BAD: Wait(0) when not needed
CreateThread(function()
    while true do
        Wait(0)  -- Runs every frame (60+ times/sec)
        DrawSomething()
    end
end)

-- ✅ GOOD: Only use Wait(0) for drawing, higher wait for checks
CreateThread(function()
    while true do
        Wait(1000)  -- Check every second
        if IsPlayerNearSomething() then
            -- Show interaction
        end
    end
end)
```

## Code Organization

### Namespace Your Events

```lua
-- ❌ BAD: Generic names that might conflict
RegisterNetEvent('getData')
RegisterNetEvent('doAction')

-- ✅ GOOD: Prefixed with script name
RegisterNetEvent('nova_myscript:getData')
RegisterNetEvent('nova_myscript:doAction')
```

### Use the Locale System

```lua
-- ❌ BAD: Hardcoded strings
Nova.Functions.Notify(source, 'Dinheiro insuficiente!', 'error')

-- ✅ GOOD: Localized
Nova.Functions.Notify(source, L('not_enough_money'), 'error')
```

## UI Guidelines

### Follow the NOVA Theme

* **Background:** `rgba(15, 15, 25, 0.95)` with `backdrop-filter: blur(20px)`
* **Accent Color:** `#84cc16` (lime green)
* **Border:** `1px solid rgba(132, 204, 22, 0.3)`
* **Border Radius:** `16px` for containers, `8px` for buttons
* **Font:** `'Segoe UI', sans-serif`
* **Text Color:** `#e4e4e7`

### Support i18n in NUI

Always pass locale strings to your NUI and apply them dynamically. Never hardcode text in HTML.
