
por aquapha-v
aq-orm is a lightweight ORM built specifically for FiveM that sits on top of oxmysql. If you’re tired of writing raw SQL strings and wrestling with typos in your queries, this library gives you a fluent, fully type-safe query builder with zero runtime overhead.
Most FiveM resources interact with the database through raw SQL strings. That works, but it means:
aq-orm solves all of that with a functional schema definition and a chainable query builder that produces parameterized SQL under the hood.
@overextended/oxmysqlpnpm add @aquapha/aq-orm
import { oxmysql } from "@overextended/oxmysql";
import { Database, Driver, table, int, varchar, boolean, eq } from "@aquapha/aq-orm";
// Define your schema
const users = table("users", {
id: int("id").primaryKey().autoIncrement(),
name: varchar("name", 255).notNull(),
active: boolean("active").default(true),
});
// Create a database instance
const db = new Database(new Driver(oxmysql));
// Type-safe queries — autocomplete and compile-time checks included
const activeUsers = await db
.select(users)
.where(eq(users.columns.active, true))
.execute();
The result type of activeUsers is automatically inferred from your schema. No manual typing required.
Define tables using plain functions — no decorators, no classes, no magic:
const players = table("players", {
id: int("id").primaryKey().autoIncrement(),
name: varchar("name", 255).notNull(),
email: varchar("email").notNull().unique(),
role: enumCol("role", ["admin", "user", "moderator"]).notNull(),
balance: decimal("balance", 10, 2).default(0),
metadata: json<{ level: number; xp: number }>("metadata"),
createdAt: timestamp("created_at").notNull(),
});
// Infer the TypeScript type directly from the schema
type Player = Infer<typeof players>;
// { id: number | null; name: string; email: string; role: "admin" | "user" | "moderator"; ... }
Supported column types: int, bigint, varchar, text, boolean, date, timestamp, datetime, json, decimal, enumCol.
// Select with joins and aggregation:
const revenue = await db
.select(orders)
.columns({
customerId: orders.columns.customerId,
totalSpent: sum(orders.columns.amount),
orderCount: count(),
})
.where(eq(orders.columns.status, "completed"))
.groupBy(orders.columns.customerId)
.having(gte(sum(orders.columns.amount), 1000))
.orderBy(sum(orders.columns.amount), "DESC")
.limit(10)
.execute();
// Upsert:
await db
.insert(players)
.values({ id: 1, name: "John", email: "john@example.com" })
.onDuplicateKeyUpdate(["name", "email"])
.execute();
// Transactions:
await db
.transaction()
.add(db.update(accounts).set({ balance: 500 }).where(eq(accounts.columns.id, 1)))
.add(db.insert(logs).values({ action: "balance_update", accountId: 1 }))
.execute();
// Raw SQL when you need it:
import { sql } from "@aquapha/aq-orm";
const userId = 1;
const query = sql`SELECT * FROM users WHERE id = ${userId}`;
await db.raw(query.sql, query.params);
pnpm add @aquapha/aq-orm┌───────────────────────┬─────────┐
│
├───────────────────────┼─────────┤
│ Code is accessible │ Yes │
├───────────────────────┼─────────┤
│ Subscription-based │ No │
├───────────────────────┼─────────┤
│ Lines (approximately) │ 2700 │
├───────────────────────┼─────────┤
│ Requirements │ oxmysql │
├───────────────────────┼─────────┤
│ Support │ GitHub issues │
└───────────────────────┴─────────┘