Database
@aicore-fivem/mysql, typed tables and ai-mysql for third-party scripts.
AI Core has its own database engine, @aicore-fivem/mysql. Every ai-* resource that uses the database runs a small pool in its own process (no export hop per query); ai-mysql stands in for oxmysql, mysql-async and ghmattimysql so third-party scripts keep their API.
Tables
A resource keeps its tables in sql/main.sql and installs them at start:
createDatabaseTables(ResourceName, "sql/main.sql");It creates missing tables and adds missing columns; it never drops or changes existing ones. Every table is prefixed with the resource (ai_<name>_...), has a primary key and uses utf8mb4_unicode_ci.
Queries
The build writes typed row types (resource/server/tables.ts) from the SQL. Simple queries use them:
export const db = tables<Tables, TableInserts>();
const rows = await db.select("ai_garages_vehicles", ["id", "plate"], { owner }, { limit: 50 });Joins, ranges, upserts and bulk statements stay plain SQL with placeholders:
await mysql.query("UPDATE ai_x SET count = count - ? WHERE id = ? AND count >= ?", [n, id, n]);Rules
- Placeholders for every value; identifiers only from a fixed list.
- Money is an integer, times are UTC
DATETIME, flags areTINYINT(1). - Player data is read once at load and kept in memory; frequent values are written by a periodic flush, critical ones (money, items) in one conditional statement or transaction.
- Bulk writes are one statement per chunk, never one per row.
- Each resource that uses the database needs
add_convar_permission <resource> read mysql_connection_stringinenv.cfg.