MiniORM Documentation
Installation
To install MiniORM, add it to your project using npm:
npm install miniorm
Required Dependencies
# For MySQL
npm install mysql2
# For PostgreSQL
npm install pg
# For SQLite
npm install sqlite3
Connection method
The connection method establishes a database connection with the specified name and configuration. It supports MySQL, PostgreSQL, and SQLite databases.
Connection Examples
import { MiniOrm } from 'miniorm';
// MySQL connection
MiniOrm.connection('mysql_db', {
type: 'mysql',
host: 'localhost',
user: 'root',
password: 'password',
database: 'my_database',
port: 3306,
ping: 30000 // Optional ping interval in ms
});
// PostgreSQL connection
MiniOrm.connection('pg_db', {
type: 'postgresql',
host: 'localhost',
user: 'postgres',
password: 'password',
database: 'my_database',
port: 5432,
ssl: false
});
// SQLite connection
MiniOrm.connection('sqlite_db', {
type: 'sqlite',
path: './database.sqlite'
});
runQuery method
The runQuery method executes SQL queries on a specified database connection. It automatically handles the query execution based on the database type.
runQuery Examples
import { MiniOrm } from 'miniorm';
// Setup connections
MiniOrm.connection('mysql_db', {
type: 'mysql',
host: 'localhost',
user: 'root',
password: 'password',
database: 'my_database'
});
// Basic SELECT query
const users = await MiniOrm.runQuery('mysql_db', 'SELECT * FROM users', []);
// Query with parameters
const user = await MiniOrm.runQuery(
'mysql_db',
'SELECT * FROM users WHERE id = ?',
[5]
);
// INSERT query with parameters
await MiniOrm.runQuery(
'mysql_db',
'INSERT INTO users (name, email) VALUES (?, ?)',
['John Doe', 'john@example.com']
);
// Silent query execution (no logs)
await MiniOrm.runQuery('mysql_db false', 'SELECT 1', []);
disconnect method
The disconnect method closes a database connection properly, ensuring resources are released.
disconnect Examples
import { MiniOrm, EventManager } from 'miniorm';
// Setup connection
MiniOrm.connection('temp_db', {
type: 'mysql',
host: 'localhost',
user: 'root',
database: 'temp'
});
// Run some queries
await MiniOrm.runQuery('temp_db', 'SELECT * FROM logs', []);
// Close the connection when done
await MiniOrm.disconnect('temp_db');
// Listen for disconnect events
EventManager.on('disconnect-temp_db', () => {
console.log('Database disconnected, cleaning up resources...');
});
reconnect method
The reconnect method re-establishes a connection to a database that was previously disconnected or failed.
reconnect Examples
import { MiniOrm } from 'miniorm';
// Setup connection
MiniOrm.connection('analytics_db', {
type: 'postgresql',
host: 'localhost',
database: 'analytics',
ping: 60000 // Auto-ping every minute
});
// Manually reconnect after network issue
try {
await MiniOrm.runQuery('analytics_db', 'SELECT * FROM events', []);
} catch (error) {
console.log('Connection lost, attempting to reconnect...');
await MiniOrm.reconnect('analytics_db');
}
// Implementing custom reconnection logic
async function ensureConnection(dbName) {
try {
const result = await MiniOrm.runQuery(dbName + ' false', 'SELECT 1', []);
if (!result) await MiniOrm.reconnect(dbName);
} catch (err) {
await MiniOrm.reconnect(dbName);
}
}
TableBuilder Classes
MiniOrm provides two table builder classes with different approaches to creating database tables: FluentTableBuilder and DeclarativeTableBuilder. Both support MySQL, PostgreSQL, and SQLite with automatic SQL syntax adaptation.
Constructor
import { DeclarativeTableBuilder, FluentTableBuilder } from 'miniorm';
// Create a new table builder for a specific table and database
const usersTable = new DeclarativeTableBuilder('users', 'main_db');
const productsTable = new FluentTableBuilder('products', 'inventory_db');
FluentTableBuilder
The FluentTableBuilder provides a chainable API for defining tables with a more natural language-like syntax.
Column Methods
import { FluentTableBuilder } from 'miniorm';
const table = new FluentTableBuilder('users', 'app_db')
// Auto-incrementing primary key
.id()
// String column with length
.string('username', 100)
.notNULL()
.unique()
// Integer column
.integer('age', 3)
.notNULL()
// Boolean column
.bool('active')
.default(true)
// Enum column
.enum('role', ['user', 'admin', 'moderator'])
.default('user')
// Add created_at and updated_at columns
.timestamps();
DeclarativeTableBuilder
The DeclarativeTableBuilder uses a declarative approach where you define columns with configuration objects.
Column Method
import { DeclarativeTableBuilder, DatabaseColumnType } from 'miniorm';
const table = new DeclarativeTableBuilder('users', 'app_db')
.column({
name: 'id',
type: DatabaseColumnType.INTEGER,
primaryKey: true,
autoIncrement: true
})
.column({
name: 'username',
type: DatabaseColumnType.VARCHAR,
length: 100,
unique: true,
notNull: true
})
.column({
name: 'email',
type: DatabaseColumnType.VARCHAR,
length: 191,
unique: true,
notNull: true
})
.column({
name: 'role',
type: DatabaseColumnType.VARCHAR,
enum: ['user', 'admin', 'moderator'],
defaultValue: 'user'
});
set Method
Both builders support the set method to define multiple tables in a single chain.
FluentTableBuilder Example
import { FluentTableBuilder } from 'miniorm';
const builder = new FluentTableBuilder('users', 'app_db')
.id()
.string('name', 100)
.notNULL()
// Switch to a new table in the same database
.set({ table: 'posts' })
.id()
.string('title', 200)
.notNULL()
.integer('user_id', 11)
// Switch to a new table in a different database
.set({ table: 'logs', db: 'analytics_db' })
.id()
.string('event', 100);
DeclarativeTableBuilder Example
import { DeclarativeTableBuilder, DatabaseColumnType } from 'miniorm';
const builder = new DeclarativeTableBuilder('users', 'app_db')
.column({
name: 'id',
type: DatabaseColumnType.INTEGER,
primaryKey: true
})
.column({
name: 'name',
type: DatabaseColumnType.VARCHAR,
length: 100
})
// Switch to a new table
.set('posts')
.column({
name: 'id',
type: DatabaseColumnType.INTEGER,
primaryKey: true
})
// Switch to a new database
.set('logs', 'analytics_db')
.column({
name: 'id',
type: DatabaseColumnType.INTEGER,
primaryKey: true
});
build Method
The build method executes the CREATE TABLE statements for all defined tables.
import { MiniOrm, FluentTableBuilder } from 'miniorm';
// Setup database connections
MiniOrm.connection('app_db', {
type: 'mysql',
host: 'localhost',
user: 'root',
password: 'password',
database: 'app'
});
// Define tables
const builder = new FluentTableBuilder('users', 'app_db')
.id()
.string('email', 191)
.unique()
.notNULL()
.timestamps();
// Create tables
async function setupDatabase() {
await builder.build();
console.log('Tables created successfully');
}
setupDatabase();
checkTableExists Method
Both builders provide a static method to check if a table exists in a database.
import { MiniOrm, FluentTableBuilder } from 'miniorm';
// Setup database connection
MiniOrm.connection('app_db', {
type: 'mysql',
host: 'localhost',
user: 'root',
password: 'password',
database: 'app'
});
async function checkAndCreateTables() {
const db = MiniOrm.dbs['app_db'].db;
const dbType = MiniOrm.dbs['app_db'].config.type;
// Check if users table exists
const usersExists = await FluentTableBuilder.checkTableExists(
db,
dbType,
'users'
);
console.log('Users table exists:', usersExists);
// Create table only if it doesn't exist
if (!usersExists) {
const builder = new FluentTableBuilder('users', 'app_db')
.id()
.string('name', 100)
.notNULL();
await builder.build();
}
}
checkAndCreateTables();
QueryBuilder Class
The QueryBuilder class provides a fluent interface for building and executing SQL queries across different database types. It supports MySQL, PostgreSQL, and SQLite with automatic query adaptation and SQL injection protection.
Constructor
import { QueryBuilder, BaseEntityClass } from 'miniorm';
// Create a query builder with table name
const usersQuery = new QueryBuilder('main_db', 'users');
// Create a query builder with entity class
class User extends BaseEntityClass {
static $table = 'users'; // Optional custom table name
id!: number;
name!: string;
email!: string;
}
const userQuery = new QueryBuilder('main_db', User);
select method
The select method starts a SELECT query and optionally specifies which fields to retrieve.
Select Examples
import { QueryBuilder } from 'miniorm';
// Select all fields
const allUsers = new QueryBuilder('main_db', 'users')
.select()
.run();
// Select specific fields
const userNames = new QueryBuilder('main_db', 'users')
.select(['id', 'name', 'email'])
.run();
// Select with type safety using entity class
interface User {
id: number;
name: string;
email: string;
created_at: Date;
}
const activeUsers = new QueryBuilder<User>('main_db', 'users')
.select(['id', 'name', 'email'])
.where({ active: true })
.run();
where method
The where method adds WHERE conditions to filter query results with protection against SQL injection.
Where Examples
import { QueryBuilder } from 'miniorm';
// Simple equality condition
const activeUsers = new QueryBuilder('main_db', 'users')
.select()
.where({ active: true })
.run();
// Multiple conditions (AND)
const adminUsers = new QueryBuilder('main_db', 'users')
.select()
.where({ role: 'admin', active: true })
.run();
// Using operators
const recentUsers = new QueryBuilder('main_db', 'users')
.select()
.where({ 'created_at >': '2023-01-01' })
.run();
// OR conditions
const specialUsers = new QueryBuilder('main_db', 'users')
.select()
.where({ role: 'admin', email: 'test@example.com', _OR: true })
.run();
// IN operator
const specificUsers = new QueryBuilder('main_db', 'users')
.select()
.where({ 'id IN ': [1, 2, 3, 4] })
.run();
// LIKE operator
const searchUsers = new QueryBuilder('main_db', 'users')
.select()
.where({ 'name LIKE ': '%john%' })
.run();
// IS NULL
const noEmail = new QueryBuilder('main_db', 'users')
.select()
.where({ 'email IS ': 'NULL' })
.run();
order, limit, and offset methods
These methods control the sorting and pagination of query results.
Examples
import { QueryBuilder } from 'miniorm';
// Order results
const oldestFirst = new QueryBuilder('main_db', 'users')
.select()
.order(['created_at'], 'ASC')
.run();
const newestFirst = new QueryBuilder('main_db', 'users')
.select()
.order(['created_at'], 'DESC')
.run();
// Multiple order fields
const sortedUsers = new QueryBuilder('main_db', 'users')
.select()
.order(['role', 'created_at'], 'DESC')
.run();
// Limit results (pagination)
const firstPage = new QueryBuilder('main_db', 'users')
.select()
.limit(10)
.run();
// Limit with offset (pagination)
const secondPage = new QueryBuilder('main_db', 'users')
.select()
.limit(10)
.offset(10)
.run();
// Combining all together
const paginatedSorted = new QueryBuilder('main_db', 'users')
.select(['id', 'name', 'email'])
.where({ active: true })
.order(['created_at'], 'DESC')
.limit(10)
.offset(20)
.run();
Aggregation methods
The QueryBuilder provides methods for common SQL aggregation functions: count, max, min, avg, and sum.
Aggregation Examples
import { QueryBuilder } from 'miniorm';
// Count all records
const totalUsers = await new QueryBuilder('main_db', 'users')
.count();
// Count with conditions
const activeCount = await new QueryBuilder('main_db', 'users')
.where({ active: true })
.count();
// Count specific field (non-null values)
const emailCount = await new QueryBuilder('main_db', 'users')
.count('email');
// Maximum value
const highestScore = await new QueryBuilder('main_db', 'scores')
.max('points');
// Minimum value
const lowestScore = await new QueryBuilder('main_db', 'scores')
.min('points');
// Average value
const averageScore = await new QueryBuilder('main_db', 'scores')
.avg('points');
// Sum values
const totalPoints = await new QueryBuilder('main_db', 'scores')
.sum('points');
// With conditions
const teamPoints = await new QueryBuilder('main_db', 'scores')
.where({ team_id: 5 })
.sum('points');
insert, update, and delete methods
These methods handle data modification operations with SQL injection protection.
Data Modification Examples
import { QueryBuilder } from 'miniorm';
// Insert a new record
const insertResult = await new QueryBuilder('main_db', 'users')
.insert({
name: 'John Doe',
email: 'john@example.com',
active: true,
created_at: new Date()
});
// Update records
const updateResult = await new QueryBuilder('main_db', 'users')
.update({ active: false })
.where({ 'last_login <': '2023-01-01' })
.run();
// Delete records
const deleteResult = await new QueryBuilder('main_db', 'users')
.delete()
.where({ 'created_at <': '2022-01-01' })
.run();
// Insert with default values (if table has defaults)
const defaultInsert = await new QueryBuilder('main_db', 'logs')
.insert({});
first, end, and exists methods
These methods provide shortcuts for common query patterns.
Examples
import { QueryBuilder } from 'miniorm';
// Get first record
const firstUser = await new QueryBuilder('main_db', 'users')
.select()
.order(['id'], 'ASC')
.first();
// Get last record
const lastUser = await new QueryBuilder('main_db', 'users')
.select()
.order(['id'], 'ASC')
.end();
// Check if records exist
const hasAdmins = await new QueryBuilder('main_db', 'users')
.where({ role: 'admin' })
.exists();
// Combining with other conditions
const hasActiveAdmins = await new QueryBuilder('main_db', 'users')
.where({ role: 'admin', active: true })
.exists();
pluck and between methods
The pluck method extracts a single column from query results, while between creates a range condition.
Examples
import { QueryBuilder } from 'miniorm';
// Get all user IDs
const userIds = await new QueryBuilder('main_db', 'users')
.pluck('id');
// Get emails of active users
const activeEmails = await new QueryBuilder('main_db', 'users')
.where({ active: true })
.pluck('email');
// Find users with scores in a range
const mediumScores = await new QueryBuilder('main_db', 'scores')
.between('points', 50, 75);
// Combine with other methods
const recentUserIds = await new QueryBuilder('main_db', 'users')
.where({ active: true })
.order(['created_at'], 'DESC')
.limit(10)
.pluck('id');
debug and raw methods
The debug method helps with query troubleshooting, while raw allows executing custom SQL queries.
Examples
import { QueryBuilder } from 'miniorm';
// Debug current query
new QueryBuilder('main_db', 'users')
.select(['id', 'name'])
.where({ active: true })
.order(['created_at'], 'DESC')
.debug('now');
// Debug final query (at execution time)
const result = await new QueryBuilder('main_db', 'users')
.select(['id', 'name'])
.where({ active: true })
.debug('final')
.run();
// Custom debug handler
const customDebug = await new QueryBuilder('main_db', 'users')
.select()
.debug('final', (sql, data) => {
console.log('SQL to execute:', sql);
console.log('Parameters:', data);
// Log to file, send to monitoring service, etc.
})
.run();
// Execute raw SQL
const rawResult = await new QueryBuilder('main_db', 'users')
.raw('SELECT COUNT(*) FROM <table> WHERE created_at > ?', ['2023-01-01']);
clone method
The clone method creates a copy of the current query builder, optionally targeting a different database or table.
Clone Examples
import { QueryBuilder, BaseEntityClass } from 'miniorm';
// Create a base query
const baseQuery = new QueryBuilder('main_db', 'users')
.select(['id', 'name', 'email'])
.where({ active: true });
// Clone for the same table
const adminQuery = baseQuery.clone()
.where({ role: 'admin' });
// Clone for a different table
const archivedQuery = baseQuery.clone({ table: 'archived_users' });
// Clone for a different database
const analyticsQuery = baseQuery.clone({ db: 'analytics_db' });
// Clone with entity class
class User extends BaseEntityClass {
id!: number;
name!: string;
email!: string;
}
class AdminUser extends BaseEntityClass {
static $table = 'admin_users';
id!: number;
name!: string;
permissions!: string[];
}
const userQuery = new QueryBuilder('main_db', User)
.select(['id', 'name']);
// Clone with different entity
const adminUserQuery = userQuery.clone<AdminUser>({ table: AdminUser });
Connection Management
The QueryBuilder provides methods for managing database connections.
Connection Examples
import { MiniOrm, QueryBuilder } from 'miniorm';
// Setup database connection
MiniOrm.connection('main_db', {
type: 'mysql',
host: 'localhost',
user: 'root',
password: 'password',
database: 'main'
});
// Disconnect from database
const query = new QueryBuilder('main_db', 'users');
await query.disconnect();
// Reconnect to database
await query.reconnect();
// Run query after reconnection
const users = await query
.select()
.where({ active: true })
.run();
toSQL method
The toSQL method returns the SQL query string and parameter values without executing the query.
toSQL Examples
import { QueryBuilder } from 'miniorm';
// Build a query
const query = new QueryBuilder('main_db', 'users')
.select(['id', 'name', 'email'])
.where({ active: true })
.order(['created_at'], 'DESC')
.limit(10);
// Get SQL and parameters
const [sql, params] = query.toSQL();
console.log('SQL Query:', sql);
console.log('Parameters:', params);
// Useful for logging or custom execution
// Example output:
// SQL Query: SELECT id,name,email FROM users WHERE active = ? ORDER BY created_at DESC LIMIT 10
// Parameters: [true]
SQL Injection Protection
The QueryBuilder includes robust protection against SQL injection attacks.
Security Features
import { QueryBuilder } from 'miniorm';
// All user inputs are automatically parameterized
const safeQuery = new QueryBuilder('main_db', 'users')
.select()
.where({
username: userInput, // Safe: value will be parameterized
'email LIKE': `%${userInput}%` // Safe: value will be parameterized
});
// The isIgnoreField and isIgnoreValue methods check for SQL injection attempts
try {
const riskyQuery = new QueryBuilder('main_db', 'users')
.select()
.where({
username: "admin'; DROP TABLE users; --" // Will be caught
});
} catch (error) {
console.error('SQL injection attempt detected:', error.message);
}
// The QueryBuilder blocks common SQL injection patterns
// - SQL comments (-- or /* */)
// - Multiple statements (;)
// - SQL keywords (UNION, SELECT, DROP, etc.)
// - Hex encoded values (0x...)
// - Unicode control characters
// - And many more
BaseEntityClass
The BaseEntityClass provides a foundation for creating strongly-typed entity classes that work with QueryBuilder.
Entity Examples
import { BaseEntityClass, QueryBuilder } from 'miniorm';
// Basic entity class
class User extends BaseEntityClass<User> {
// Table name defaults to lowercase class name ('user')
id!: number;
username!: string;
email!: string;
active: boolean = true;
created_at!: Date;
}
// Entity with custom table name
class AdminUser extends BaseEntityClass<AdminUser> {
static $table = 'administrators'; // Custom table name
id!: number;
username!: string;
permissions!: string[];
}
// Using entities with QueryBuilder
const userQuery = new QueryBuilder<User>('main_db', User);
const adminQuery = new QueryBuilder<AdminUser>('main_db', AdminUser);
// Type-safe queries
const activeUsers = await userQuery
.select(['id', 'username', 'email']) // TypeScript checks field names
.where({ active: true })
.run();
// The result is typed as User[]
activeUsers.forEach(user => {
console.log(user.username); // TypeScript knows these properties exist
});
Practical Examples
Here are some real-world examples showing how to use QueryBuilder for common database operations.
User Authentication
import { QueryBuilder } from 'miniorm';
async function authenticateUser(email: string, password: string) {
const user = await new QueryBuilder('main_db', 'users')
.select(['id', 'name', 'email', 'password_hash', 'role'])
.where({ email: email })
.first();
if (!user) {
return { success: false, message: 'User not found' };
}
// Verify password (using a hypothetical password verification function)
const passwordValid = await verifyPassword(password, user.password_hash);
if (!passwordValid) {
return { success: false, message: 'Invalid password' };
}
// Update last login timestamp
await new QueryBuilder('main_db', 'users')
.update({ last_login: new Date() })
.where({ id: user.id })
.run();
// Return user without password hash
const { password_hash, ...safeUser } = user;
return { success: true, user: safeUser };
}
Pagination
import { QueryBuilder } from 'miniorm';
async function getPaginatedUsers(page: number, pageSize: number, filters: any = {}) {
const offset = (page - 1) * pageSize;
// Get total count for pagination metadata
const totalCount = await new QueryBuilder('main_db', 'users')
.where(filters)
.count();
// Get page of users
const users = await new QueryBuilder('main_db', 'users')
.select(['id', 'name', 'email', 'role', 'created_at'])
.where(filters)
.order(['created_at'], 'DESC')
.limit(pageSize)
.offset(offset)
.run();
return {
data: users,
pagination: {
page,
pageSize,
totalCount,
totalPages: Math.ceil(totalCount / pageSize),
hasNextPage: page * pageSize < totalCount,
hasPrevPage: page > 1
}
};
}
Complex Reporting
import { QueryBuilder } from 'miniorm';
async function getMonthlyStats(year: number, month: number) {
// Format date for SQL comparison
const startDate = `${year}-${month.toString().padStart(2, '0')}-01`;
const endDate = month === 12
? `${year + 1}-01-01`
: `${year}-${(month + 1).toString().padStart(2, '0')}-01`;
// Get new user count
const newUsers = await new QueryBuilder('main_db', 'users')
.where({ 'created_at >=': startDate, 'created_at <': endDate })
.count();
// Get active user count
const activeUsers = await new QueryBuilder('main_db', 'user_logins')
.where({ 'login_time >=': startDate, 'login_time <': endDate })
.pluck('user_id')
.then(ids => new Set(ids).size);
// Get revenue stats
const totalRevenue = await new QueryBuilder('main_db', 'orders')
.where({ 'created_at >=': startDate, 'created_at <': endDate, status: 'completed' })
.sum('amount');
// Get top products
const topProducts = await new QueryBuilder('main_db', 'order_items')
.raw(`
SELECT p.name, p.id, SUM(oi.quantity) as total_sold
FROM <table> oi
JOIN products p ON oi.product_id = p.id
JOIN orders o ON oi.order_id = o.id
WHERE o.created_at >= ? AND o.created_at < ? AND o.status = ?
GROUP BY p.id
ORDER BY total_sold DESC
LIMIT 5
`, [startDate, endDate, 'completed']);
return {
period: { year, month },
newUsers,
activeUsers,
totalRevenue,
topProducts
};
}
Model Class
The Model class provides a higher-level abstraction over QueryBuilder, offering a simpler API for common database operations.
Basic Usage
import { Model, BaseEntityClass } from 'miniorm';
// Define an entity class
class User extends BaseEntityClass<User> {
id!: number;
name!: string;
email!: string;
active: boolean = true;
}
// Create a model instance
const userModel = new Model('main_db', User);
// Or with a string table name
const postsModel = new Model('main_db', 'posts');
Find Methods
The Model class provides several methods for retrieving data with a simplified API.
Find Examples
import { Model, BaseEntityClass } from 'miniorm';
class User extends BaseEntityClass<User> {
id!: number;
name!: string;
email!: string;
active: boolean = true;
}
const userModel = new Model<User>('main_db', 'user');
// Find all users
const allUsers = await userModel.findAll();
// Find all users with specific fields
const userNames = await userModel.findAll(['id', 'name']);
// Find with conditions
const activeUsers = await userModel.find({
fields: ['id', 'name', 'email'],
where: [{ active: true }],
order: ['created_at', 'DESC'],
limit: 10,
offset: 0
});
// Find a single user
const firstUser = await userModel.findOne({
where: [{ active: true }],
order: ['id', 'ASC']
});
// Shorthand for findOne
const user = await userModel.first({
where: [{ email: 'test@example.com' }]
});
CRUD Operations
The Model class simplifies create, update, and delete operations.
CRUD Examples
import { Model, BaseEntityClass } from 'miniorm';
class User extends BaseEntityClass<User> {
id!: number;
name!: string;
email!: string;
active: boolean = true;
}
const userModel = new Model<User>('main_db', User);
// Create a new user
const created = await userModel.create({
name: 'John Doe',
email: 'john@example.com',
active: true
});
// Update users
const updated = await userModel.update(
{ active: false },
{ 'last_login <': '2023-01-01' }
);
// Delete users
const deleted = await userModel.delete(
{ 'created_at <': '2022-01-01' }
);
Aggregation Methods
The Model class provides direct access to common SQL aggregation functions.
Aggregation Examples
import { Model } from 'miniorm';
const userModel = new Model('main_db', 'users');
const scoreModel = new Model('main_db', 'scores');
// Count all records
const totalUsers = await userModel.count();
// Count specific field (non-null values)
const emailCount = await userModel.count('email');
// Maximum value
const highestScore = await scoreModel.max('points');
// Minimum value
const lowestScore = await scoreModel.min('points');
// Average value
const averageScore = await scoreModel.avg('points');
// Sum values
const totalPoints = await scoreModel.sum('points');
Utility Methods
The Model class includes utility methods for common database operations.
Utility Examples
import { Model } from 'miniorm';
const userModel = new Model('main_db', 'users');
// Check if records exist
const hasAdmins = await userModel.exists({ role: 'admin' });
// Get values from a single column
const emails = await userModel.pluck('email');
// Execute raw SQL
const result = await userModel.raw(
'SELECT COUNT(*) FROM <table> WHERE created_at > ?',
['2023-01-01']
);
// Access the underlying query builder for complex queries
const complexQuery = userModel.query()
.select(['id', 'name'])
.where({ active: true })
.order(['created_at'], 'DESC')
.limit(10);
const results = await complexQuery.run();
Connection Management
The Model class includes methods for managing database connections.
Connection Examples
import { Model, MiniOrm } from 'miniorm';
// Setup database connection
MiniOrm.connection('main_db', {
type: 'mysql',
host: 'localhost',
user: 'root',
password: 'password',
database: 'app'
});
// Create model
const userModel = new Model('main_db', 'users');
// Disconnect from database
await userModel.disconnect();
// Reconnect to database
await userModel.reconnect();
// Run query after reconnection
const users = await userModel.find({
where: [{ active: true }]
});
Debugging
The Model class provides a debug method for troubleshooting queries.
Debug Example
import { Model } from 'miniorm';
const userModel = new Model('main_db', 'users');
// Debug the current query
userModel.debug('now').find({
fields: ['id', 'name'],
where: [{ active: true }],
order: ['created_at', 'DESC'],
limit: 10
});
// Debug the final query at execution time
const result = await userModel.debug('final').find({
where: [{ role: 'admin' }]
});
// Custom debug handler
const customDebug = await userModel.debug('final', (sql, data) => {
console.log('SQL to execute:', sql);
console.log('Parameters:', data);
// Log to file, send to monitoring service, etc.
}).findAll();
Complete Example
This example demonstrates a complete workflow using the Model class.
Repository Pattern Example
import { MiniOrm, Model, BaseEntityClass } from 'miniorm';
// 1. Define entity class
class User extends BaseEntityClass<User> {
id!: number;
username!: string;
email!: string;
active: boolean = true;
created_at!: Date;
}
// 2. Create a repository class
class UserRepository {
private model: Model<User>;
constructor() {
this.model = new Model<User>('app_db', User);
}
async findById(id: number): Promise<User | undefined> {
return await this.model.findOne({
where: [{ id }]
});
}
async findByEmail(email: string): Promise<User | undefined> {
return await this.model.findOne({
where: [{ email }]
});
}
async getActiveUsers(page: number = 1, pageSize: number = 10): Promise<User[]> {
return await this.model.find({
where: [{ active: true }],
order: ['created_at', 'DESC'],
limit: pageSize,
offset: (page - 1) * pageSize
});
}
async createUser(userData: Omit<User, 'id' | 'created_at'>): Promise<boolean> {
return await this.model.create(userData);
}
async updateUser(id: number, userData: Partial<User>): Promise<any> {
return await this.model.update(userData, { id });
}
async deactivateUser(id: number): Promise<any> {
return await this.model.update({ active: false }, { id });
}
async deleteUser(id: number): Promise<any> {
return await this.model.delete({ id });
}
async getUserCount(onlyActive: boolean = false): Promise<number> {
if (onlyActive) {
return await this.model.query()
.where({ active: true })
.count();
}
return await this.model.count();
}
}
// 3. Application usage
async function main() {
// Setup database connection
MiniOrm.connection('app_db', {
type: 'mysql',
host: 'localhost',
user: 'app_user',
password: 'secure_password',
database: 'application'
});
const userRepo = new UserRepository();
// Create a user
await userRepo.createUser({
username: 'john_doe',
email: 'john@example.com',
active: true
});
// Find user by email
const user = await userRepo.findByEmail('john@example.com');
if (user) {
console.log(`Found user: ${user.username}`);
// Update user
await userRepo.updateUser(user.id, {
username: 'john_doe_updated'
});
// Get active users
const activeUsers = await userRepo.getActiveUsers(1, 10);
console.log(`Active users: ${activeUsers.length}`);
// Get user count
const totalUsers = await userRepo.getUserCount();
const activeCount = await userRepo.getUserCount(true);
console.log(`Total users: ${totalUsers}, Active: ${activeCount}`);
}
// Clean up
await userRepo.model.disconnect();
}
main();
Best Practices
Here are some recommended practices when using the QueryBuilder:
- Use entity classes for type safety and better code organization
- Chain methods for readable query construction
- Use debug() during development to inspect generated queries
- Leverage aggregation methods for efficient data analysis
- Use parameterized values instead of string concatenation for dynamic queries
- Implement pagination for large result sets
- Use transactions for operations that need to be atomic
- Handle errors appropriately in production code