START HERE·THE ECOSYSTEM MAP·Verified June 2026 · Lua 5.4 · ox_lib 3.x
Learning with an AI assistant?
Copies this whole lesson - every step, code block, and the exact console errors - plus 2026 ground rules (no lua54 'yes', Cfx.re Portal, correct callback signatures) as a ready-to-paste mentor prompt.
Start Here · The ecosystem map

Databases and oxmysql: how a server remembers

Here is a fact that surprises every beginner: a FiveM server forgets almost everything the moment you restart it. The cash a player earned, the car they bought, the character they made, gone, unless the server wrote it down somewhere that survives a restart. That somewhere is a database. And the way your Lua code talks to a database is a resource called oxmysql. This lesson is a map, not a build. You will learn what these two things are and how they fit together, so that when you reach the paid Tracks and actually wire them up, none of it is a mystery.

You'll learn
What a database is, what oxmysql does, the four things you do with data, and the one safety rule that protects all of it
Time
~10 minutes
Difficulty
Beginner. No setup, no code to run yet.
You need
Nothing installed. This is an awareness lesson. You just read and understand.
BEFORE YOU START

What a database is

A database is an organized store of data that lives on disk and survives a restart. Think of a spreadsheet, but bigger, faster, and built to be read and written by code instead of by a person clicking cells.

The data inside is arranged into tables. A table is one subject, like players or vehicles. Inside a table, each row is one record (one player, one car), and each column is one fact about every record (a name, a license, an amount of cash). So a players table might have a column for the player's name and a column for their cash, and one row per player on your server.

You do not read or write a table by hand. You send it a query: a short instruction, written in a language called SQL, that says what you want. "Give me the row for this player." "Add 100 to their cash." The database does the work and hands back an answer.

The most common database engine in FiveM is MySQL, or its drop-in twin MariaDB. They speak the same language and behave the same way, so when a tutorial says "MySQL" it almost always means "MySQL or MariaDB."

Vocabulary

Database
An organized store of data that lives on disk and survives a server restart. The server's long-term memory.
Table
One subject inside the database, like players or vehicles. A grid of rows and columns.
Row
One record in a table. One player, one car, one bank account.
Column
One fact stored about every row in a table, like a name, a license, or an amount of cash.
Query
A short instruction, written in SQL, that tells the database what to read or change.
MySQL / MariaDB
The common database engine for FiveM. Two near-identical twins that speak the same language.
oxmysql
A FiveM resource that lets your Lua code send queries to MySQL or MariaDB and read the answers back.

Why you need one

Everything you have written so far lives in variables, and variables live in memory. Memory is temporary. The instant the resource restarts, every variable is wiped clean and starts from nothing. That is fine for a value you only need for a second, but it is a disaster for anything a player expects to still be there tomorrow.

A variable is a sticky note on your desk: handy right now, gone when you tidy up. A database is a filing cabinet: slower to reach into, but what you file stays filed.

So the rule is simple. If a piece of data must outlive a restart, it belongs in a database, not in a variable. That covers most of what makes a roleplay server feel real:

  • Player money and bank balances
  • Characters players create
  • Vehicles they own and where those vehicles are parked
  • Inventories, houses, jobs, businesses, anything earned or saved

If you have ever joined a server and found your money reset to zero after the owner restarted, you watched a server that was keeping money in a variable instead of a database. The fix was always the same: write it to a database.

The four things you do with data

Almost everything you ever do to a database is one of four actions. SQL has a keyword for each. You do not need to memorize the syntax today. Just recognize the four verbs, because every database feature you ever build is some combination of them.

SELECT, to read

SELECT asks the database for data and gives it back without changing anything. It is the safe, look-but-do-not-touch action.

sql
SELECT cash FROM players WHERE license = 'abc123';

In plain words: "Find the player whose license is abc123, and tell me their cash."

INSERT, to add

INSERT creates a brand new row. You use it the first time something exists, like when a player makes a new character.

sql
INSERT INTO players (name, cash) VALUES ('Tony', 500);

In plain words: "Add a new player named Tony who starts with 500 cash."

UPDATE, to change

UPDATE changes data that already exists. This is how money goes up and down, how a car moves, how a job changes.

sql
UPDATE players SET cash = 600 WHERE license = 'abc123';

In plain words: "Set that player's cash to 600."

DELETE, to remove

DELETE removes a row for good. You use it the least, and you treat it with the most care, because it does not ask twice.

sql
DELETE FROM players WHERE license = 'abc123';

In plain words: "Remove that player's row entirely."

How FiveM talks to it: oxmysql

Your Lua code cannot speak SQL to a database on its own. It needs a translator that knows how to open a connection, send a query, wait for the answer, and hand it back to your script. In FiveM, that translator is a resource called oxmysql. It is the standard, and virtually every modern resource depends on it.

Once oxmysql is running and your other resources depend on it, a global named MySQL becomes available in your server scripts. You hand it a query and it does the round trip to the database for you. Here is a tiny illustration of reading one value. Do not build this yet, just read it.

lua
-- ask the database for one player's cash, then print it
local cash = MySQL.scalar.await(
    'SELECT cash FROM players WHERE license = ?',
    { license }
)
 
print('this player has', cash)

That is the whole idea: a line of SQL, a place to slot in your value, and oxmysql carrying the message both ways. The deep, hands-on CRUD building, creating tables, saving a player on disconnect, loading them on join, is taught step by step in the paid Tracks. This lesson is only the map.

Why the .await and the curly braces?

In the snippet above, .await tells oxmysql "wait here for the database to answer before moving on," which keeps the code readable top to bottom. The { license } part is a list of values that fill in the ? placeholder. You will use both constantly in Track B, where they are explained line by line. For now, just notice the shape: SQL string first, values second.

The one safety rule: never glue player input into a query

This is the single most important habit in database work, and it is easy to get right once you see it. SQL injection is when an attacker types something crafted into a name box or a command, and because your code glued their raw text straight into a query, their text becomes part of the instruction the database runs. A clever string can read data they should never see, or delete an entire table.

The fix is always the same, and oxmysql makes it easy. Never paste a value into the query text. Instead, write a ? placeholder where the value goes, and pass the real value separately. oxmysql then escapes it safely, so it can only ever be treated as a value, never as a command.

lua
-- BAD: the player's text becomes part of the query
MySQL.query.await("SELECT * FROM players WHERE name = '" .. playerInput .. "'")
 
-- GOOD: the value is passed separately and escaped safely
MySQL.query.await('SELECT * FROM players WHERE name = ?', { playerInput })

In the bad line, a player who types '; DROP TABLE players; -- can wipe your whole players table. In the good line, that exact same text is searched for as a literal name and nothing else happens. Same data, one safe shape.

Common beginner mistakes

These are the three that bite people first. You will not hit them today, since you are not building yet, but knowing the symptom now means you will recognize the fix later.

SymptomFix
Every query errors and the console mentions MySQL being niloxmysql is not started, so the MySQL global does not exist. Make sure oxmysql is in your server.cfg and starts before any resource that depends on it. A resource should also declare it depends on oxmysql so it refuses to start without it.
A crafted player name or command wipes or leaks your dataYou built the query by joining a player's text into the SQL string. That is SQL injection. Use a ? placeholder and pass the value separately so oxmysql escapes it.
oxmysql logs that it cannot connect, and the server starts but no data ever savesThe connection string is wrong. Check the username, password, host, port, and database name in your server.cfg. If oxmysql cannot reach the database, every query fails before it runs.
Why should you never paste a player's typed value straight into a SQL query?

Because a player controls what they type, and if their text is glued directly into the query, the database treats it as part of the instruction, not as a plain value. A crafted string can then read data they should not see or delete entire tables. This is called SQL injection. The fix is to write a ? placeholder where the value belongs and pass the real value separately, so oxmysql escapes it and it can only ever be treated as a value.

Try it yourself

What you can do now

  • Explain what a database is and why a server needs one: variables vanish on restart, a database does not.
  • Name the parts of a database: tables made of rows and columns, read and written with queries.
  • List the four things you do with data: SELECT reads, INSERT adds, UPDATE changes, DELETE removes.
  • Describe what oxmysql does: it lets your Lua code send queries to MySQL or MariaDB and read the answers back.
  • State the one safety rule: never glue player input into a query, always use ? placeholders.

You now have the map. Data that must survive a restart lives in a database, oxmysql is how your Lua talks to it, four verbs cover almost everything, and one placeholder rule keeps it safe. Next up is "Frameworks: Qbox, ESX, and QBCore," where you will see how the big roleplay frameworks build all of this for you on top of oxmysql. The hands-on CRUD building, where you actually create tables and save players, waits for you in the paid Tracks.