Why I built this
I like poking at the FiveM ecosystem, mostly because the runtime breaks in interesting ways. What I did not enjoy was hand-writing raw SQL for every database call and then finding out at runtime that I had misspelled a column name. So I built aq-orm: a type-safe way to talk to MySQL that complains in my editor instead of in production.
What it does
aq-orm is a lightweight ORM designed specifically for FiveM servers using oxmysql as the database driver. It provides a fluent, chainable query builder with full TypeScript type inference from your schema definitions.
Schema definition
Define your tables with type-safe column builders:
import { table, int, varchar, boolean, timestamp } from "@aquapha/aq-orm";
const users = table("users", {
id: int("id").primaryKey().autoIncrement(),
name: varchar("name", 255).notNull(),
email: varchar("email").notNull().unique(),
active: boolean("active").default(true),
createdAt: timestamp("created_at").notNull(),
});TypeScript types are inferred directly from the schema, so no manual type definitions are needed:
import type { Infer } from "@aquapha/aq-orm";
type User = Infer<typeof users>;
// { id: number | null; name: string; email: string; active: boolean; createdAt: Date }Query builder
Queries are built with a fluent API that reads naturally:
const activeUsers = await db
.select(users)
.where(eq(users.columns.active, true))
.orderBy(users.columns.createdAt, "DESC")
.limit(10)
.execute();It supports joins, aggregates, group by, transactions, and even MySQL-specific functions like JSON_OBJECT and JSON_EXTRACT.
Key features
- Type-safe schemas: column types map directly to TypeScript types
- Fluent query builder: select, insert, update, delete with chainable methods
- Conditions:
eq,gt,like,inArray,between, and more - Aggregates:
count,sum,avg,min,max - Joins: inner, left, and self-joins with table aliases
- Transactions: batch multiple operations atomically
- Raw SQL escape hatch: tagged template literals with parameterized queries