Skip to main content
Home
Shop
Free Mods
Tools
Bundles
Full Servers
  1. Home
  2. Blog
  3. Development

From mysql-async to oxmysql: Safe Migration & Query Patterns

Published on August 16, 2025·by Lars Miller(Founder & Lead Editor)·Credentials·6 min read·Updated on March 24, 2026
Developmentfrom mysql-async to oxmysql safe migration

1. Full backup: mysqldump --single-transaction yourdb > backup.sql. 2. Staging env mirroring production schema + data subset. 3.

From mysql-async to oxmysql: Safe Migration & Query Patterns
From mysql-async to oxmysql: Safe Migration & Query Patterns

Introduction to Audience: FiveM server owners, scripters, maintainers

FiveM SQL Database Migration Guide

Audience: FiveM server owners, scripters, maintainers
Goal: Replace mysql-async with oxmysql safely, speed up queries, and modernize your SQL usage.

This guide is part of our complete FiveM frameworks guide, where we compare ESX, QBCore, and QBOX in depth and help you choose the right one.

Also read:

  • FiveM Server Optimization: The Definitive 2026 Playbook — https://fivemx.com/blog/how-to-optimize-fivem-server-performance
  • Adapter Patterns: ESX↔QBCore↔QBOX Exports, Events & Player Models — https://fivemx.com/blog/adapter-patterns

TL;DR

  • Use oxmysql: prepared statements, promise/await API, better diagnostics, strong performance.
  • Minimal code changes: swap @param → ? (positional) or :name (named) params; replace MySQL.Async.* calls with MySQL.*/exports.oxmysql:*.
  • Run the SQL “UP” scripts below (charset/index fixes) and keep the rollback handy.
  • Verify with the micro‑benchmark harness at the end to confirm wins on your hardware.

1) Pre‑flight Safety Checklist

  1. Full backup: mysqldump --single-transaction yourdb > backup.sql.
  2. Staging env mirroring production schema + data subset.
  3. Artifact & deps: Current FXServer build, latest oxmysql.
  4. Downtime window for prod switch (usually < 5 minutes).
  5. Health probes ready: /players, login flow, economy ops, garage ops, inventory ops, ban checks.

2) Install & Wire Up oxmysql

2.1 server.cfg

# Stop using mysql-async default_prio 500

ensure mysql-async # ← comment out or remove

Start oxmysql

default_prio 50

```lua
```lua
ensure oxmysql

Connection string consumed by oxmysql

```lua
```lua
set mysql_connection_string "mysql://user:pass@127.0.0.1:3306/yourdb?charset=utf8mb4"

Optional diagnostics

```lua
```lua
set mysql_slow_query_warning 200   # log queries slower than 200ms
set mysql_debug false              # true for verbose logging during staging

Keep mysql-async disabled but available in your resources folder during the staging phase (for rapid rollback).

3) API Mapping: mysql‑async → oxmysql

mysql-async (legacy):

mysql-async (legacy):

  • Async: MySQL.Async.fetchAll, MySQL.Async.fetchScalar, MySQL.Async.execute
  • Sync: MySQL.Sync.fetchAll, MySQL.Sync.fetchScalar, MySQL.Sync.execute
  • Parameters: @param style tables like { ['@identifier']=identifier }

oxmysql (modern):

  • Callback style via export: exports.oxmysql:query|scalar|single|insert|update(sql, params, cb)
  • Promise/await via global: MySQL.query|scalar|single|insert|update.await(sql, params) and non-await callbacks without .await
  • Parameters: positional ? via array, or named :name via object

3.1 Common replacements

SELECT many

```lua
```lua
-- mysql-async
MySQL.Async.fetchAll(
  'SELECT * FROM users WHERE identifier = @id',
  { ['@id'] = identifier },
  function(rows) ... end
)

-- oxmysql (callback via export)
exports.oxmysql:query(
  'SELECT * FROM users WHERE identifier = ?',
  { identifier },
  function(rows) ... end
)

-- oxmysql (await)
local rows = MySQL.query.await(
  'SELECT * FROM users WHERE identifier = ?',
  { identifier }
)

SELECT single row

```lua
```lua
-- mysql-async (fetchAll + rows[1])

-- oxmysql
local row = MySQL.single.await(
  'SELECT * FROM users WHERE identifier = ?',
  { identifier }
)

SELECT scalar (e.g., count, id)

```lua
```lua
-- mysql-async

-- oxmysql
local count = MySQL.scalar.await(
  'SELECT COUNT(*) FROM owned_vehicles WHERE owner = ?',
  { owner }
)

INSERT (get insertId)

```lua
```lua
-- mysql-async (execute)

-- oxmysql
local insertId = MySQL.insert.await(
  'INSERT INTO notes (owner, text) VALUES (?, ?)',
  { cid, text }
)

UPDATE/DELETE (affectedRows)

```lua
```lua
-- mysql-async (execute)

-- oxmysql
local changed = MySQL.update.await(
  'UPDATE users SET job = ?, job_grade = ? WHERE identifier = ?',
  { job, grade, identifier }
)

Transactions (manual)

```lua
```lua
-- oxmysql manual transaction
MySQL.query.await('START TRANSACTION')
local ok = true

local r1 = MySQL.update.await('UPDATE users SET bank = bank - ? WHERE identifier = ? AND bank >= ?', { amount, fromId, amount })
local r2 = MySQL.update.await('UPDATE users SET bank = bank + ? WHERE identifier = ?', { amount, toId })

if r1 == 1 and r2 == 1 then
  MySQL.query.await('COMMIT')
else
  MySQL.query.await('ROLLBACK')
end

Some frameworks expose wrappers (e.g., ox_lib ) that

Some frameworks expose wrappers (e.g., ox_lib) that add MySQL.ready, .transaction, etc. The calls above are safe without extra wrappers.

4) Prepared Statements Cheat‑Sheet

Param styles

  • mysql‑async (legacy): @name placeholders with a table: { ['@name']=value }
  • oxmysql (positional): ? placeholders with an array: { value1, value2 }
  • oxmysql (named): :name placeholders with an object: { name = value }

Examples

```lua
```lua
-- Named params (recommended for readability)
local row = MySQL.single.await(
  'SELECT * FROM users WHERE identifier = :id',
  { id = identifier }
)

-- IN (...) list
-- Build placeholders dynamically and pass a flat array
local ids = { 'cid1','cid2','cid3' }
local qs = ('?,' ):rep(#ids):sub(1,-2) -- "?, ?, ?"
local rows = MySQL.query.await('SELECT * FROM players WHERE citizenid IN ('..qs..')', ids)

-- JSON fields (MySQL 5.7+/MariaDB 10.2+)
local name = MySQL.scalar.await('SELECT JSON_UNQUOTE(JSON_EXTRACT(data, "$.name")) FROM players WHERE citizenid = ?', { cid })

Do

  • Use prepared statements everywhere (never string‑concatenate user input).
  • Prefer named params for clarity in complex statements.
  • Add LIMIT 1 when reading a single entity.

Avoid

  • Wildcard SELECT * in hot paths (project needed columns).
  • Per‑row N+1 queries; batch with IN (...).

5) Database “UP” Migration Scripts (Ready‑to‑Run)

Choose the blocks that match your framework (ESX/QBCore) and server (MySQL 8+ or MariaDB 10.4+). Run on staging first.

5.1 Normalize Charset & Collation (UTF‑8 everywhere)

(A) MySQL 8+ — replace yourdb once

```sql
```sql
-- Force database default to utf8mb4 (emoji‑safe)
ALTER DATABASE \`yourdb\` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;

-- Convert common tables (extend list as needed)
ALTER TABLE \`users\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE \`owned_vehicles\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE \`players\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE \`player_vehicles\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

(B) MariaDB 10.4+ — same statements are valid.

(B) MariaDB 10.4+ — same statements are valid.

Add other hot tables (inventory, billing, phone, society, jobs) as present in your server.

5.2 ESX Indexes (safe performance wins)

MySQL 8+

```sql
```sql
ALTER TABLE \`users\`

ADD INDEX IF NOT EXISTS `idx_users_identifier` (`identifier`), ADD INDEX IF NOT EXISTS `idx_users_job` (`job`), ADD INDEX IF NOT EXISTS `idx_users_name` (`name`);

```sql
```sql
ALTER TABLE \`owned_vehicles\`

ADD UNIQUE INDEX IF NOT EXISTS `ux_owned_vehicles_plate` (`plate`), ADD INDEX IF NOT EXISTS `idx_owned_vehicles_owner` (`owner`);

MariaDB 10.4+

```lua
```lua
-- Drop first to be idempotent where IF NOT EXISTS is unavailable
DROP INDEX IF EXISTS \`idx_users_identifier\` ON \`users\`;
DROP INDEX IF EXISTS \`idx_users_job\` ON \`users\`;
DROP INDEX IF EXISTS \`idx_users_name\` ON \`users\`;
CREATE INDEX \`idx_users_identifier\` ON \`users\` (\`identifier\`);
CREATE INDEX \`idx_users_job\` ON \`users\` (\`job\`);
CREATE INDEX \`idx_users_name\` ON \`users\` (\`name\`);

DROP INDEX IF EXISTS \`ux_owned_vehicles_plate\` ON \`owned_vehicles\`;
DROP INDEX IF EXISTS \`idx_owned_vehicles_owner\` ON \`owned_vehicles\`;
CREATE UNIQUE INDEX \`ux_owned_vehicles_plate\` ON \`owned_vehicles\` (\`plate\`);
CREATE INDEX \`idx_owned_vehicles_owner\` ON \`owned_vehicles\` (\`owner\`);

5.3 QBCore/QBOX Indexes

MySQL 8+

```sql
```sql
ALTER TABLE \`players\`

ADD UNIQUE INDEX IF NOT EXISTS `ux_players_citizenid` (`citizenid`), ADD INDEX IF NOT EXISTS `idx_players_license` (`license`), ADD INDEX IF NOT EXISTS `idx_players_steam` (`steam`), ADD INDEX IF NOT EXISTS `idx_players_last_name` (`lastname`);

```sql
```sql
ALTER TABLE \`player_vehicles\`

ADD UNIQUE INDEX IF NOT EXISTS `ux_player_vehicles_plate` (`plate`), ADD INDEX IF NOT EXISTS `idx_player_vehicles_citizenid` (`citizenid`);

MariaDB 10.4+

```lua
```lua
DROP INDEX IF EXISTS \`ux_players_citizenid\` ON \`players\`;
DROP INDEX IF EXISTS \`idx_players_license\` ON \`players\`;
DROP INDEX IF EXISTS \`idx_players_steam\` ON \`players\`;
DROP INDEX IF EXISTS \`idx_players_last_name\` ON \`players\`;
CREATE UNIQUE INDEX \`ux_players_citizenid\` ON \`players\` (\`citizenid\`);
CREATE INDEX \`idx_players_license\` ON \`players\` (\`license\`);
CREATE INDEX \`idx_players_steam\` ON \`players\` (\`steam\`);
CREATE INDEX \`idx_players_last_name\` ON \`players\` (\`lastname\`);

DROP INDEX IF EXISTS \`ux_player_vehicles_plate\` ON \`player_vehicles\`;
DROP INDEX IF EXISTS \`idx_player_vehicles_citizenid\` ON \`player_vehicles\`;
CREATE UNIQUE INDEX \`ux_player_vehicles_plate\` ON \`player_vehicles\` (\`plate\`);
CREATE INDEX \`idx_player_vehicles_citizenid\` ON \`player_vehicles\` (\`citizenid\`);

5.4 Optional: ox_inventory (if installed)

```sql
```sql
ALTER TABLE \`ox_inventory\`

ADD INDEX IF NOT EXISTS `idx_inv_owner` (`owner`), ADD INDEX IF NOT EXISTS `idx_inv_type` (`type`);

```sql
```sql
ALTER TABLE \`ox_inventory_items\`

ADD INDEX IF NOT EXISTS `idx_items_inv_owner_name` (`inventory`, `owner`, `name`);

Adjust table names if your schema differs (some setups

Adjust table names if your schema differs (some setups use inventories / items).

6) Rollback Plan (Zero‑Panic)

6.1 Code rollback

  1. Revert your resource changes (keep a legacy-mysql-async branch).
  2. In server.cfg swap: # ensure oxmysql ensure mysql-async
  3. Restart FXServer or the affected resources in dependency order.

6.2 SQL rollback

  • If you only added indexes: drop them (see the MariaDB blocks above — use DROP INDEX IF EXISTS).
  • If you changed charset/collation and must undo, revert the DB and tables:
```sql
```sql
ALTER DATABASE \`yourdb\` CHARACTER SET = utf8 COLLATE = utf8_general_ci;
ALTER TABLE \`users\` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE \`owned_vehicles\` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE \`players\` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE \`player_vehicles\` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;

Prefer restoring from backup.sql instead of mass charset reversals when possible.

7) End‑to‑End Migration Procedure (Scriptable)

  1. Freeze deploys, back up DB.
  2. Apply Section 5 “UP” SQL on staging → verify → prod.
  3. Commit code refactor: replace calls (Section 3) + parameter styles (Section 4).
  4. Deploy, ensure oxmysql, restart server.
  5. Run smoke tests (login, paychecks, inventory, vehicle spawn/despawn, bans, society money, job duty toggles).
  6. Watch logs for 15–30 minutes (mysql_slow_query_warning helps); address any missed params or schema mismatches.

8) Micro‑Benchmarks (Bring Your Own Numbers)

A tiny resource you can drop in to compare hot‑path queries on your hardware and dataset.

fxmanifest.lua

fx_version 'cerulean'

```lua
```lua
game 'gta5'

server_script 'bench.lua'

bench.lua

```lua
```lua
local COUNT = 2000  -- adjust for your server

local function bench(name, fn)
  local t0 = os.clock()
  local ok, err = pcall(fn)
  local dt = (os.clock() - t0) * 1000.0

print(('[bench] %s: %.2f ms %s'):format(name, dt, ok and '' or ('ERR: '..tostring(err))))

```lua
```lua
end

-- Hot path 1: ownership lookup
bench('SELECT single', function()
  for i=1,COUNT do
    local row = MySQL.single.await('SELECT owner FROM owned_vehicles WHERE plate = :p LIMIT 1', { p = ('TEST%04d'):format(i % 500) })
  end
end)

-- Hot path 2: batched fetch
bench('SELECT batch IN', function()
  local ids = {}
  for i=1,100 do ids[#ids+1] = ('cid%04d'):format(i) end
  local qs = ('?,' ):rep(#ids):sub(1,-2)
  local rows = MySQL.query.await('SELECT citizenid, firstname, lastname FROM players WHERE citizenid IN ('..qs..')', ids)
end)

-- Hot path 3: update with guard
bench('UPDATE guarded', function()
  for i=1,COUNT do
    local changed = MySQL.update.await('UPDATE users SET bank = bank + :d WHERE identifier = :id AND bank >= 0', { d = 1, id = ('lic:%04d'):format(i % 500) })
  end
end)

How to run

How to run

  1. Put the resource in a folder (e.g., ox-bench/), add ensure ox-bench to server.cfg.
  2. Tail server console; results print as lines like: [bench] SELECT single: 134.21 ms.
  3. For a before/after comparison, run once with mysql-async (adjust the calls if necessary), then with oxmysql.

What to look for

  • Lower total ms per section after migration.
  • Lower P95/P99 latency on gameplay actions tied to queries.
  • Fewer slow‑query warnings over an hour of live play.

9) Troubleshooting

Q: I get “no such export: query/single/…”.
A: oxmysql isn’t started early enough. Ensure ensure oxmysql is above resources that use it.

Q: Parameter errors or empty results.
A: You likely kept @param placeholders. Replace with ? or :name and pass an array/object accordingly.

Q: Deadlocks or partial writes.
A: Wrap multi‑step balances/transfers in a transaction (see Section 3), add indexes from Section 5.

Q: JSON path returns NULL.
A: Confirm your engine supports JSON functions (MySQL ≥5.7/MariaDB ≥10.2) and that the column type is JSON (not LONGTEXT).

Q: Slow after migration.
A: Check missing indexes, EXPLAIN your query, project only needed columns, and review the Server Optimization Playbook.

10) Code Review Checklist (copy/paste)

  • No string‑concatenated SQL; all queries parameterized.
  • Use .single/.scalar with LIMIT 1 when only one row/value is required.
  • Batch IN (...) reads for collections.
  • Transactions around multi‑step money/inventory ops.
  • Index present for every hot WHERE/JOIN column.
  • Avoid SELECT * in hot paths.
  • Log slow queries; track top offenders weekly.

Internal Links

  • FiveM Server Optimization: The Definitive 2026 Playbook — https://fivemx.com/blog/how-to-optimize-fivem-server-performance
  • Adapter Patterns: ESX↔QBCore↔QBOX Exports, Events & Player Models — https://fivemx.com/blog/adapter-patterns

Credits Maintained by fivemx.com

Credits
Maintained by fivemx.com. Contributions welcome (send diffs of additional safe indexes or wrapper helpers).

Frequently Asked Questions

What are the key advantages of using `oxmysql` over `mysql-async` in FiveM?

`oxmysql` offers several advantages, including prepared statements for improved security and performance, a promise/await-based API simplifying asynchronous operations, better error diagnostics for easier debugging, and overall enhanced performance compared to `mysql-async`. It also allows for positional (`?`) or named (`:name`) parameters in queries.

What's the most important step to take before migrating from `mysql-async` to `oxmysql` on my FiveM server?

Before making any changes, perform a complete database backup using `mysqldump --single-transaction yourdb > backup.sql`. This backup is crucial in case any issues arise during the migration process, allowing you to revert to the previous state without data loss. It's also highly recommended to test the migration process in a staging environment that mirrors your production setup.

After switching to `oxmysql`, how do I verify that the migration was successful and that queries are performing as expected?

Implement health probes to check critical server functions like `/players` endpoint access, player login flow, economy operations (money transfers), garage operations (vehicle spawning), inventory operations (item transfers), and ban checks. Additionally, you can use the micro-benchmark harness mentioned in the guide to test raw query performance and compare it with previous benchmarks, ensuring the switch resulted in tangible performance gains. It is also important to check server console for any oxmysql errors.

What specific code changes are required when switching from `mysql-async` to `oxmysql` in my FiveM scripts?

The primary code changes involve parameter syntax and function calls. Replace `@param` style parameters with either positional (`?`) or named (`:name`) parameters. Replace `MySQL.Async.*` calls with `MySQL.*` or `exports.oxmysql:*` calls, depending on whether you're using the direct `MySQL` object or exporting functions from the `oxmysql` resource. Consult the `oxmysql` documentation or examples for specifics on the correct usage of `exports.oxmysql:*`.

My resources throw "no such export: query/single/..." error after the migration. What does it mean?

This error typically indicates that `oxmysql` is not started early enough in your `server.cfg`. Ensure that the `ensure oxmysql` line appears *before* any other resource that depends on it. The `default_prio 50` setting also helps ensure it loads early. Double-check your server configuration file to confirm the load order and adjust as needed.

How to run?

How to run 1. Put the resource in a folder (e.g., ox-bench/), add ensure ox-bench to server.cfg. 2. Tail server console; results print as lines like: [bench] SELECT single: 134.21 ms.

How do I fix common From mysql-async to oxmysql: Safe Migration & Query Patterns issues?

Q: I get “no such export: query/single/…”. A: oxmysql isn’t started early enough. Ensure ensure oxmysql is above resources that use it. Q: Parameter errors or empty results.

Previous Article

SQL & Identifiers Migration: steam/license to citizenid

Next Article

Pre-Purchase Checklist: Red Flags, License Terms & Benchmarks

More on This Topic

QBox Framework Guide: Migrate from QBCore and Boost Performance (2026)The History of FiveM: From Mod Project to Official PlatformFiveM Server Management: The Complete Guide from Setup to ScaleSQL & Identifiers Migration: steam/license to citizenidBreaking Free from Tebex: Complete Guide to FiveM Server Monetization

Move from research to a production-ready server stack

Once you know the direction, jump into the highest-leverage commercial hubs for verified scripts, curated bundles, and framework-specific buying paths.

Framework hub

Browse QBCore-ready scripts

Move into the QBCore landing page to compare verified scripts, framework fit, and install-ready products built for modern FiveM servers.

Open QBCore hub

Premium catalog

Browse premium FiveM scripts

Move from research into the main shop to compare real products, framework labels, screenshots, and production-ready quality signals.

Open premium shop

Launch faster

Compare curated bundles

Bundles shorten the path from planning to launch by grouping the highest-leverage scripts into a cleaner commercial starting point.

View bundles

Disclosure: Some links below are affiliate links to FiveMX products. We may earn a commission at no extra cost to you.

Premium Scripts You Might Like

50x HQ CHROMIUM Weapon Pack

50x HQ CHROMIUM Weapon Pack

$95.00
ADS Police Script

ADS Police Script

$15.00
Pierce Tires / Knife Tires — Slash Tires Script

Pierce Tires / Knife Tires — Slash Tires Script

$9.99
l2s-dispatch

l2s-dispatch

$39.00

Free Scripts You Might Like

Project X Prompt Sandy Bank Robbery Heist - QB | QBOX | ESX | Custom

Project X Prompt Sandy Bank Robbery Heist - QB | QBOX | ESX | Custom

294 downloads
Realistic Grapple Gun - Nodus Scripts

Realistic Grapple Gun - Nodus Scripts

202 downloads
OP Gangs 3.0 — Most Advanced Gang Script [ESX/QB/QBOX]

OP Gangs 3.0 — Most Advanced Gang Script [ESX/QB/QBOX]

161 downloads
[FREE][QBOX][QBCORE] Pawnshop Script + FREE MLO

[FREE][QBOX][QBCORE] Pawnshop Script + FREE MLO

157 downloads

Related Articles

Eliminate Annoying Delays: Optimize FiveM Server Loading ...

Eliminate Annoying Delays: Optimize FiveM Server Loading ...

Learn how to optimize FiveM server loading times by managing resources, using efficient mods, and choosing the right server host to eliminate annoying delays.

September 3, 2024
How to Translate FiveM Scripts with AI: The Complete 2026 Workflow

How to Translate FiveM Scripts with AI: The Complete 2026 Workflow

Translate FiveM scripts to any language using AI — without breaking placeholders, color codes, or UI layout. Complete workflow covering locale architecture, DeepL and OpenAI pipelines, automated QA checks, and ESX/QBCore integration.

August 17, 2025
Inventory & Weight Tuning: From items.lua to Metadata

Inventory & Weight Tuning: From items.lua to Metadata

A stable RP economy depends on scarcity, friction, and meaningful choices. Inventory rules (slots, weight, stack limits, metadata like durability/serials) are the levers that make…

August 18, 2025
Secure CheckoutInstant AccessMoney-Back GuaranteeLifetime Updates
FiveMX

Premium FiveM scripts and mods for serious server owners.

Shop

  • Shop
  • QBCore Scripts
  • ESX Scripts
  • FiveM Scripts
  • Free Mods
  • Best Scripts & Mods

Help

  • About
  • FAQ
  • Support
  • Contact
  • Account
  • Affiliate Program

Legal

  • Privacy Policy
  • Terms of Service
  • Refund Policy
  • Cookie Policy
  • GDPR Compliance
  • DMCA
  • Imprint
  • Editorial Policy
© 2026 FiveMX. All rights reserved.·support@fivemx.com

FiveMX is not affiliated with Rockstar Games, Take-Two Interactive, or CFX.re. All trademarks are property of their respective owners.

Flash Sale — Up to 19% off!Flash Sale — 19% off!Shop Now