mysql-orm-lite 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,1421 @@
1
+ # MySQL ORM Lite
2
+
3
+ A lightweight ORM for MySQL with connection pooling, CRUD operations, query builder, and transaction support for Node.js applications.
4
+
5
+ [![npm version](https://badge.fury.io/js/mysql-orm-lite.svg)](https://www.npmjs.com/package/mysql-orm-lite)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Why MySQL ORM Lite?
9
+
10
+ - ðŸŠķ **Lightweight** - Minimal overhead, maximum performance
11
+ - ðŸŽŊ **Simple API** - Intuitive methods for common operations
12
+ - 🔄 **Connection Pooling** - Efficient MySQL connection management
13
+ - 🏗ïļ **Query Builder** - Build complex queries with MongoDB-style operators
14
+ - ðŸ’ū **Transaction Support** - Full ACID transaction capabilities
15
+ - 🔒 **SQL Injection Safe** - Parameterized queries by default
16
+ - 📊 **Performance Monitoring** - Automatic slow query logging (>1s)
17
+ - 🌐 **Multiple Databases** - Connect to multiple MySQL databases simultaneously
18
+ - 📝 **Custom Logger** - Integrate with Winston, Bunyan, or any custom logger
19
+
20
+ ## Table of Contents
21
+
22
+ - [Installation](#installation)
23
+ - [Quick Start](#quick-start)
24
+ - [API Reference](#api-reference)
25
+ - [Connection Management](#connection-management)
26
+ - [CRUD Operations](#crud-operations)
27
+ - [Query Builder](#query-builder)
28
+ - [WHERE Operators](#where-operators)
29
+ - [JOIN Operations](#join-operations)
30
+ - [Transactions](#transactions)
31
+ - [Transaction Manager](#transaction-manager)
32
+ - [Advanced Usage](#advanced-usage)
33
+ - [Multiple Database Connections](#multiple-database-connections)
34
+ - [Raw SQL Expressions](#raw-sql-expressions)
35
+ - [Custom Logger Integration](#custom-logger-integration)
36
+ - [Error Handling](#error-handling)
37
+ - [Performance Tips](#performance-tips)
38
+ - [Complete Examples](#complete-examples)
39
+ - [API Quick Reference](#api-quick-reference)
40
+ - [License](#license)
41
+
42
+ ---
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ npm install mysql-orm-lite mysql2
48
+ ```
49
+
50
+ > **Note:** `mysql2` is a peer dependency and must be installed separately.
51
+
52
+ ---
53
+
54
+ ## Quick Start
55
+
56
+ ```javascript
57
+ const db = require('mysql-orm-lite');
58
+
59
+ // Initialize with your database configuration
60
+ db.connectionManager.init({
61
+ host: 'localhost',
62
+ user: 'root',
63
+ password: 'your_password',
64
+ database: 'your_database',
65
+ port: 3306,
66
+ connectionLimit: 10
67
+ });
68
+
69
+ // Basic CRUD operations
70
+ async function example() {
71
+ // Insert a record
72
+ const userId = await db.insert('users', {
73
+ name: 'John Doe',
74
+ email: 'john@example.com',
75
+ age: 30
76
+ });
77
+
78
+ // Find using query builder
79
+ const users = await db.select({
80
+ table: 'users',
81
+ where: { id: userId }
82
+ });
83
+
84
+ // Update a record
85
+ await db.updateWhere({
86
+ table: 'users',
87
+ data: { name: 'Jane Doe' },
88
+ where: { id: userId }
89
+ });
90
+
91
+ // Delete a record
92
+ await db.deleteWhere({
93
+ table: 'users',
94
+ where: { id: userId }
95
+ });
96
+
97
+ // Close connections on app shutdown
98
+ await db.connectionManager.closeAllPools();
99
+ }
100
+
101
+ example();
102
+ ```
103
+
104
+ ---
105
+
106
+ ## API Reference
107
+
108
+ ### Connection Management
109
+
110
+ #### `connectionManager.init(config, logger?)`
111
+
112
+ Initialize the connection manager with database configuration.
113
+
114
+ **Parameters:**
115
+ | Parameter | Type | Required | Description |
116
+ |-----------|------|----------|-------------|
117
+ | `config` | Object | Yes | Database configuration object |
118
+ | `logger` | Object | No | Custom logger instance |
119
+
120
+ **Config Options:**
121
+ | Option | Type | Default | Description |
122
+ |--------|------|---------|-------------|
123
+ | `host` | string | - | Database host |
124
+ | `user` | string | - | Database user |
125
+ | `password` | string | - | Database password |
126
+ | `database` | string | - | Database name |
127
+ | `port` | number | 3306 | Database port |
128
+ | `connectionLimit` | number | 10 | Max pool connections |
129
+
130
+ ```javascript
131
+ db.connectionManager.init({
132
+ host: 'localhost',
133
+ user: 'root',
134
+ password: 'password',
135
+ database: 'mydb',
136
+ port: 3306,
137
+ connectionLimit: 10
138
+ });
139
+ ```
140
+
141
+ #### `connectionManager.setLogger(customLogger)`
142
+
143
+ Set a custom logger with `info`, `error`, and `warn` methods.
144
+
145
+ ```javascript
146
+ db.connectionManager.setLogger({
147
+ info: (...args) => console.log('[DB INFO]', ...args),
148
+ error: (...args) => console.error('[DB ERROR]', ...args),
149
+ warn: (...args) => console.warn('[DB WARN]', ...args)
150
+ });
151
+ ```
152
+
153
+ #### `connectionManager.getPool(dbConfig?)`
154
+
155
+ Get or create a connection pool. Returns a Promise.
156
+
157
+ ```javascript
158
+ const pool = await db.connectionManager.getPool();
159
+ ```
160
+
161
+ #### `connectionManager.closePool(config)`
162
+
163
+ Close a specific connection pool.
164
+
165
+ ```javascript
166
+ await db.connectionManager.closePool({
167
+ host: 'localhost',
168
+ user: 'root',
169
+ database: 'mydb'
170
+ });
171
+ ```
172
+
173
+ #### `connectionManager.closeAllPools()`
174
+
175
+ Close all connection pools. **Call this on application shutdown.**
176
+
177
+ ```javascript
178
+ process.on('SIGINT', async () => {
179
+ await db.connectionManager.closeAllPools();
180
+ process.exit(0);
181
+ });
182
+ ```
183
+
184
+ #### `connectionManager.getLogger()`
185
+
186
+ Get the current logger instance.
187
+
188
+ ```javascript
189
+ const logger = db.connectionManager.getLogger();
190
+ logger.info('Custom log message');
191
+ ```
192
+
193
+ ---
194
+
195
+ ### CRUD Operations
196
+
197
+ #### `insert(table, data, dbConfig?, debug?, isIgnore?)`
198
+
199
+ Insert a record into a table. Returns the `insertId`.
200
+
201
+ **Parameters:**
202
+ | Parameter | Type | Required | Description |
203
+ |-----------|------|----------|-------------|
204
+ | `table` | string | Yes | Table name |
205
+ | `data` | Object | Yes | Key-value pairs to insert |
206
+ | `dbConfig` | Object | No | Database config (uses default if not provided) |
207
+ | `debug` | boolean | No | Log insert details |
208
+ | `isIgnore` | boolean | No | Use INSERT IGNORE |
209
+
210
+ ```javascript
211
+ // Basic insert
212
+ const id = await db.insert('users', {
213
+ name: 'John',
214
+ email: 'john@example.com',
215
+ created_at: new Date()
216
+ });
217
+
218
+ // Insert with debug logging
219
+ const id = await db.insert('users', { name: 'John' }, null, true);
220
+
221
+ // INSERT IGNORE (skip duplicates)
222
+ const id = await db.insert('users', { email: 'john@example.com' }, null, false, true);
223
+ ```
224
+
225
+ #### `update(table, data, whereClause, dbConfig?, debug?)`
226
+
227
+ Update records in a table. Returns `affectedRows`.
228
+
229
+ **Parameters:**
230
+ | Parameter | Type | Required | Description |
231
+ |-----------|------|----------|-------------|
232
+ | `table` | string | Yes | Table name |
233
+ | `data` | Object | Yes | Key-value pairs to update |
234
+ | `whereClause` | string | Yes | WHERE clause (must include 'WHERE') |
235
+ | `dbConfig` | Object | No | Database config |
236
+ | `debug` | boolean | No | Log update details |
237
+
238
+ ```javascript
239
+ const affectedRows = await db.update(
240
+ 'users',
241
+ { name: 'Jane', updated_at: new Date() },
242
+ 'WHERE id = 1'
243
+ );
244
+
245
+ // With debug
246
+ const affectedRows = await db.update(
247
+ 'users',
248
+ { status: 'active' },
249
+ 'WHERE created_at < NOW()',
250
+ null,
251
+ true
252
+ );
253
+ ```
254
+
255
+ #### `delete(whereClause, table, dbConfig?)`
256
+
257
+ Delete records from a table. Returns `affectedRows`.
258
+
259
+ **Parameters:**
260
+ | Parameter | Type | Required | Description |
261
+ |-----------|------|----------|-------------|
262
+ | `whereClause` | string | Yes | WHERE clause (must include 'WHERE') |
263
+ | `table` | string | Yes | Table name |
264
+ | `dbConfig` | Object | No | Database config |
265
+
266
+ ```javascript
267
+ const affectedRows = await db.delete('WHERE id = 1', 'users');
268
+
269
+ // Delete with complex condition
270
+ const affectedRows = await db.delete(
271
+ 'WHERE status = "deleted" AND deleted_at < DATE_SUB(NOW(), INTERVAL 30 DAY)',
272
+ 'users'
273
+ );
274
+ ```
275
+
276
+ #### `find(query, params?, dbConfig?)`
277
+
278
+ Execute a raw SELECT query. Returns an array of results.
279
+
280
+ **Parameters:**
281
+ | Parameter | Type | Required | Description |
282
+ |-----------|------|----------|-------------|
283
+ | `query` | string | Yes | SQL query string |
284
+ | `params` | Array | No | Query parameters (for ? placeholders) |
285
+ | `dbConfig` | Object | No | Database config |
286
+
287
+ ```javascript
288
+ // Simple query
289
+ const users = await db.find('SELECT * FROM users');
290
+
291
+ // Parameterized query
292
+ const users = await db.find(
293
+ 'SELECT * FROM users WHERE age > ? AND status = ?',
294
+ [18, 'active']
295
+ );
296
+
297
+ // Complex query with joins
298
+ const orders = await db.find(`
299
+ SELECT o.*, u.name as user_name
300
+ FROM orders o
301
+ INNER JOIN users u ON o.user_id = u.id
302
+ WHERE o.total > ?
303
+ ORDER BY o.created_at DESC
304
+ LIMIT 10
305
+ `, [100]);
306
+ ```
307
+
308
+ #### `findCount(query, params?, dbConfig?)`
309
+
310
+ Execute a COUNT query. Returns the count value.
311
+
312
+ **Parameters:**
313
+ | Parameter | Type | Required | Description |
314
+ |-----------|------|----------|-------------|
315
+ | `query` | string | Yes | COUNT query (must return `count` alias) |
316
+ | `params` | Array | No | Query parameters |
317
+ | `dbConfig` | Object | No | Database config |
318
+
319
+ ```javascript
320
+ const count = await db.findCount(
321
+ 'SELECT COUNT(*) as count FROM users WHERE age > ?',
322
+ [18]
323
+ );
324
+ console.log(`Found ${count} users`);
325
+ ```
326
+
327
+ ---
328
+
329
+ ### Query Builder
330
+
331
+ The query builder provides a fluent API to construct SQL queries programmatically. All methods support both verbose and concise naming.
332
+
333
+ #### Method Aliases
334
+
335
+ | Verbose Name | Short Aliases |
336
+ |--------------|---------------|
337
+ | `buildAndExecuteSelectQuery` | `select`, `findWhere`, `query` |
338
+ | `buildAndExecuteUpdateQuery` | `updateWhere`, `updateQuery` |
339
+ | `buildAndExecuteDeleteQuery` | `deleteWhere`, `remove` |
340
+
341
+ #### `select(options, dbConfig?)` / `buildAndExecuteSelectQuery()`
342
+
343
+ Build and execute a SELECT query.
344
+
345
+ **Options:**
346
+ | Option | Type | Description |
347
+ |--------|------|-------------|
348
+ | `table` | string | Table name (required) |
349
+ | `alias` | string | Table alias |
350
+ | `fields` | Array/string | Fields to select (default: '*') |
351
+ | `where` | Object | WHERE conditions |
352
+ | `joins` | Array | JOIN clauses |
353
+ | `orderBy` | string | ORDER BY clause |
354
+ | `groupBy` | string | GROUP BY clause |
355
+ | `having` | string | HAVING clause |
356
+ | `limit` | number | LIMIT value |
357
+ | `offset` | number | OFFSET value |
358
+
359
+ ```javascript
360
+ // Simple select
361
+ const users = await db.select({
362
+ table: 'users',
363
+ where: { status: 'active' }
364
+ });
365
+
366
+ // Select with all options
367
+ const users = await db.select({
368
+ table: 'users',
369
+ alias: 'u',
370
+ fields: ['id', 'name', 'email', 'created_at'],
371
+ where: {
372
+ age: { $gte: 18, $lte: 65 },
373
+ status: 'active'
374
+ },
375
+ orderBy: 'created_at DESC',
376
+ groupBy: 'department_id',
377
+ having: 'COUNT(*) > 5',
378
+ limit: 10,
379
+ offset: 0
380
+ });
381
+
382
+ // Using aliases
383
+ const result1 = await db.findWhere({ table: 'users', where: { id: 1 } });
384
+ const result2 = await db.query({ table: 'products', limit: 100 });
385
+ ```
386
+
387
+ #### `updateWhere(options, dbConfig?)` / `buildAndExecuteUpdateQuery()`
388
+
389
+ Build and execute an UPDATE query. Returns `affectedRows`.
390
+
391
+ **Options:**
392
+ | Option | Type | Description |
393
+ |--------|------|-------------|
394
+ | `table` | string | Table name (required) |
395
+ | `data` | Object | Data to update (required) |
396
+ | `where` | Object | WHERE conditions |
397
+
398
+ ```javascript
399
+ // Update with query builder
400
+ const affectedRows = await db.updateWhere({
401
+ table: 'users',
402
+ data: {
403
+ status: 'inactive',
404
+ updated_at: new Date()
405
+ },
406
+ where: {
407
+ last_login: { $lt: '2023-01-01' }
408
+ }
409
+ });
410
+
411
+ // Update multiple conditions
412
+ await db.updateQuery({
413
+ table: 'products',
414
+ data: { stock: 0 },
415
+ where: {
416
+ $or: [
417
+ { discontinued: true },
418
+ { expiry_date: { $lt: new Date() } }
419
+ ]
420
+ }
421
+ });
422
+ ```
423
+
424
+ #### `deleteWhere(options, dbConfig?)` / `buildAndExecuteDeleteQuery()`
425
+
426
+ Build and execute a DELETE query. Returns `affectedRows`. **Requires a WHERE clause for safety.**
427
+
428
+ **Options:**
429
+ | Option | Type | Description |
430
+ |--------|------|-------------|
431
+ | `table` | string | Table name (required) |
432
+ | `where` | Object | WHERE conditions (required) |
433
+
434
+ ```javascript
435
+ // Delete with conditions
436
+ const affectedRows = await db.deleteWhere({
437
+ table: 'sessions',
438
+ where: {
439
+ expired_at: { $lt: new Date() }
440
+ }
441
+ });
442
+
443
+ // Using 'remove' alias
444
+ await db.remove({
445
+ table: 'logs',
446
+ where: {
447
+ created_at: { $lt: '2024-01-01' },
448
+ level: { $in: ['debug', 'trace'] }
449
+ }
450
+ });
451
+ ```
452
+
453
+ ---
454
+
455
+ ### WHERE Operators
456
+
457
+ The query builder supports MongoDB-style operators for building WHERE clauses.
458
+
459
+ #### Comparison Operators
460
+
461
+ | Operator | SQL Equivalent | Example |
462
+ |----------|----------------|---------|
463
+ | `$eq` | `=` | `{ status: { $eq: 'active' } }` |
464
+ | `$ne` | `!=` | `{ status: { $ne: 'deleted' } }` |
465
+ | `$gt` | `>` | `{ age: { $gt: 18 } }` |
466
+ | `$gte` | `>=` | `{ age: { $gte: 21 } }` |
467
+ | `$lt` | `<` | `{ price: { $lt: 100 } }` |
468
+ | `$lte` | `<=` | `{ quantity: { $lte: 10 } }` |
469
+
470
+ #### Array Operators
471
+
472
+ | Operator | SQL Equivalent | Example |
473
+ |----------|----------------|---------|
474
+ | `$in` | `IN (...)` | `{ status: { $in: ['active', 'pending'] } }` |
475
+ | `$nin` / `$notIn` | `NOT IN (...)` | `{ role: { $nin: ['admin', 'moderator'] } }` |
476
+
477
+ #### Pattern Matching
478
+
479
+ | Operator | SQL Equivalent | Example |
480
+ |----------|----------------|---------|
481
+ | `$like` | `LIKE` | `{ name: { $like: '%John%' } }` |
482
+ | `$between` | `BETWEEN` | `{ age: { $between: [18, 65] } }` |
483
+
484
+ #### Logical Operators
485
+
486
+ | Operator | Description | Example |
487
+ |----------|-------------|---------|
488
+ | `$and` | Logical AND | `{ $and: [{ age: { $gte: 18 } }, { status: 'active' }] }` |
489
+ | `$or` | Logical OR | `{ $or: [{ city: 'NYC' }, { city: 'LA' }] }` |
490
+ | `$not` | Logical NOT | `{ $not: { status: 'deleted' } }` |
491
+
492
+ #### NULL Handling
493
+
494
+ ```javascript
495
+ // Check for NULL
496
+ { deleted_at: null } // WHERE deleted_at IS NULL
497
+ ```
498
+
499
+ #### Complete WHERE Examples
500
+
501
+ ```javascript
502
+ // Multiple operators on same field
503
+ await db.select({
504
+ table: 'users',
505
+ where: {
506
+ age: { $gte: 18, $lte: 65 } // age >= 18 AND age <= 65
507
+ }
508
+ });
509
+
510
+ // OR conditions
511
+ await db.select({
512
+ table: 'users',
513
+ where: {
514
+ $or: [
515
+ { city: 'New York' },
516
+ { city: 'Los Angeles' },
517
+ { city: 'Chicago' }
518
+ ]
519
+ }
520
+ });
521
+
522
+ // Complex nested conditions
523
+ await db.select({
524
+ table: 'orders',
525
+ where: {
526
+ status: 'pending',
527
+ $or: [
528
+ { total: { $gte: 1000 } },
529
+ {
530
+ $and: [
531
+ { priority: 'high' },
532
+ { customer_type: 'vip' }
533
+ ]
534
+ }
535
+ ]
536
+ }
537
+ });
538
+
539
+ // BETWEEN operator
540
+ await db.select({
541
+ table: 'products',
542
+ where: {
543
+ price: { $between: [10, 100] },
544
+ created_at: { $between: ['2024-01-01', '2024-12-31'] }
545
+ }
546
+ });
547
+
548
+ // LIKE patterns
549
+ await db.select({
550
+ table: 'users',
551
+ where: {
552
+ email: { $like: '%@gmail.com' },
553
+ name: { $like: 'John%' }
554
+ }
555
+ });
556
+
557
+ // IN and NOT IN
558
+ await db.select({
559
+ table: 'products',
560
+ where: {
561
+ category_id: { $in: [1, 2, 3, 4, 5] },
562
+ status: { $nin: ['discontinued', 'out_of_stock'] }
563
+ }
564
+ });
565
+ ```
566
+
567
+ ---
568
+
569
+ ### JOIN Operations
570
+
571
+ The query builder supports all standard SQL JOIN types.
572
+
573
+ #### JOIN Syntax
574
+
575
+ ```javascript
576
+ await db.select({
577
+ table: 'orders',
578
+ alias: 'o',
579
+ fields: ['o.id', 'o.total', 'u.name as user_name', 'p.name as product_name'],
580
+ joins: [
581
+ {
582
+ type: 'INNER', // JOIN type: INNER, LEFT, RIGHT, FULL
583
+ table: 'users', // Table to join
584
+ alias: 'u', // Table alias (optional)
585
+ on: 'o.user_id = u.id' // JOIN condition
586
+ },
587
+ {
588
+ type: 'LEFT',
589
+ table: 'products',
590
+ alias: 'p',
591
+ on: 'o.product_id = p.id'
592
+ }
593
+ ],
594
+ where: { 'o.status': 'completed' },
595
+ orderBy: 'o.created_at DESC'
596
+ });
597
+ ```
598
+
599
+ #### Multiple ON Conditions
600
+
601
+ ```javascript
602
+ await db.select({
603
+ table: 'order_items',
604
+ alias: 'oi',
605
+ joins: [
606
+ {
607
+ type: 'INNER',
608
+ table: 'products',
609
+ alias: 'p',
610
+ on: ['oi.product_id = p.id', 'p.active = 1'] // Array for multiple conditions
611
+ }
612
+ ]
613
+ });
614
+ ```
615
+
616
+ ---
617
+
618
+ ### Transactions
619
+
620
+ Transactions ensure that multiple database operations succeed or fail together (ACID compliance).
621
+
622
+ #### Creating a Transaction
623
+
624
+ ```javascript
625
+ // Method 1: Using createTransaction()
626
+ const transaction = db.createTransaction();
627
+
628
+ // Method 2: Using TransactionCRUD class directly
629
+ const transaction = new db.TransactionCRUD();
630
+ ```
631
+
632
+ #### Basic Transaction Flow
633
+
634
+ ```javascript
635
+ const transaction = db.createTransaction();
636
+
637
+ try {
638
+ // Initialize transaction
639
+ await transaction.init();
640
+
641
+ // Perform multiple operations
642
+ const userId = await transaction.insert('users', {
643
+ name: 'John',
644
+ email: 'john@example.com'
645
+ });
646
+
647
+ await transaction.insert('user_profiles', {
648
+ user_id: userId,
649
+ bio: 'Software Developer'
650
+ });
651
+
652
+ await transaction.insert('user_settings', {
653
+ user_id: userId,
654
+ theme: 'dark',
655
+ notifications: true
656
+ });
657
+
658
+ // Commit all changes
659
+ await transaction.commit();
660
+ console.log('Transaction successful');
661
+
662
+ } catch (error) {
663
+ // Rollback on any error
664
+ await transaction.rollback();
665
+ console.error('Transaction failed:', error);
666
+ throw error;
667
+ }
668
+ ```
669
+
670
+ #### TransactionCRUD Methods
671
+
672
+ The `TransactionCRUD` class provides all CRUD methods available in the main API:
673
+
674
+ **Basic Methods:**
675
+ | Method | Description |
676
+ |--------|-------------|
677
+ | `init(dbConfig?)` | Initialize transaction |
678
+ | `commit()` | Commit transaction |
679
+ | `rollback()` | Rollback transaction |
680
+ | `executeQuery(query, params?)` | Execute raw query |
681
+
682
+ **CRUD Methods:**
683
+ | Method | Description |
684
+ |--------|-------------|
685
+ | `find(query, params?)` | Execute SELECT query |
686
+ | `insert(table, data)` | Insert record |
687
+ | `update(table, data, whereClause)` | Update records |
688
+ | `delete(whereClause, table)` | Delete records |
689
+
690
+ **Query Builder Methods (Verbose):**
691
+ | Method | Description |
692
+ |--------|-------------|
693
+ | `buildAndExecuteSelectQuery(options)` | Build & execute SELECT |
694
+ | `buildAndExecuteUpdateQuery(options)` | Build & execute UPDATE |
695
+ | `buildAndExecuteDeleteQuery(options)` | Build & execute DELETE |
696
+
697
+ **Query Builder Methods (Short Aliases):**
698
+ | Alias | Maps To |
699
+ |-------|---------|
700
+ | `select(options)` | `buildAndExecuteSelectQuery` |
701
+ | `findWhere(options)` | `buildAndExecuteSelectQuery` |
702
+ | `query(options)` | `buildAndExecuteSelectQuery` |
703
+ | `updateWhere(options)` | `buildAndExecuteUpdateQuery` |
704
+ | `updateQuery(options)` | `buildAndExecuteUpdateQuery` |
705
+ | `deleteWhere(options)` | `buildAndExecuteDeleteQuery` |
706
+ | `remove(options)` | `buildAndExecuteDeleteQuery` |
707
+
708
+ #### Complete Transaction Example
709
+
710
+ ```javascript
711
+ const transaction = db.createTransaction();
712
+
713
+ try {
714
+ await transaction.init();
715
+
716
+ // Insert using basic method
717
+ const orderId = await transaction.insert('orders', {
718
+ user_id: 123,
719
+ total: 299.99,
720
+ status: 'pending'
721
+ });
722
+
723
+ // Insert using transaction
724
+ await transaction.insert('order_items', {
725
+ order_id: orderId,
726
+ product_id: 456,
727
+ quantity: 2,
728
+ price: 149.99
729
+ });
730
+
731
+ // Update using query builder (short alias)
732
+ await transaction.updateWhere({
733
+ table: 'products',
734
+ data: { stock: { __raw: true, value: 'stock - 2' } },
735
+ where: { id: 456 }
736
+ });
737
+
738
+ // Select within transaction
739
+ const inventory = await transaction.select({
740
+ table: 'products',
741
+ fields: ['id', 'stock'],
742
+ where: { id: 456 }
743
+ });
744
+
745
+ // Check stock
746
+ if (inventory[0].stock < 0) {
747
+ throw new Error('Insufficient stock');
748
+ }
749
+
750
+ // Find using raw query
751
+ const user = await transaction.find(
752
+ 'SELECT email FROM users WHERE id = ?',
753
+ [123]
754
+ );
755
+
756
+ // Commit all changes
757
+ await transaction.commit();
758
+
759
+ return { orderId, userEmail: user[0].email };
760
+
761
+ } catch (error) {
762
+ await transaction.rollback();
763
+ throw error;
764
+ }
765
+ ```
766
+
767
+ ---
768
+
769
+ ### Transaction Manager
770
+
771
+ The Transaction Manager allows you to share a transaction instance across different modules or files in your application.
772
+
773
+ #### API
774
+
775
+ | Method | Description |
776
+ |--------|-------------|
777
+ | `setInstance(transaction)` | Store transaction instance |
778
+ | `getInstance()` | Retrieve stored instance |
779
+ | `clearInstance()` | Clear stored instance |
780
+ | `hasActiveTransaction()` | Check if active transaction exists |
781
+
782
+ #### Usage Pattern
783
+
784
+ ```javascript
785
+ // ========== Main file (e.g., orderController.js) ==========
786
+ const db = require('mysql-orm-lite');
787
+ const { processPayment } = require('./paymentService');
788
+ const { updateInventory } = require('./inventoryService');
789
+
790
+ async function createOrder(orderData) {
791
+ const transaction = db.createTransaction();
792
+
793
+ try {
794
+ await transaction.init();
795
+
796
+ // Store transaction for other modules
797
+ db.transactionManager.setInstance(transaction);
798
+
799
+ // Insert order
800
+ const orderId = await transaction.insert('orders', orderData);
801
+
802
+ // These functions will use the shared transaction
803
+ await processPayment(orderId, orderData.total);
804
+ await updateInventory(orderData.items);
805
+
806
+ await transaction.commit();
807
+ return orderId;
808
+
809
+ } catch (error) {
810
+ await transaction.rollback();
811
+ throw error;
812
+ } finally {
813
+ // Always clear the instance
814
+ db.transactionManager.clearInstance();
815
+ }
816
+ }
817
+
818
+ // ========== paymentService.js ==========
819
+ const db = require('mysql-orm-lite');
820
+
821
+ async function processPayment(orderId, amount) {
822
+ const transaction = db.transactionManager.getInstance();
823
+
824
+ if (!transaction) {
825
+ throw new Error('No active transaction');
826
+ }
827
+
828
+ await transaction.insert('payments', {
829
+ order_id: orderId,
830
+ amount: amount,
831
+ status: 'completed'
832
+ });
833
+ }
834
+
835
+ module.exports = { processPayment };
836
+
837
+ // ========== inventoryService.js ==========
838
+ const db = require('mysql-orm-lite');
839
+
840
+ async function updateInventory(items) {
841
+ const transaction = db.transactionManager.getInstance();
842
+
843
+ if (!transaction) {
844
+ throw new Error('No active transaction');
845
+ }
846
+
847
+ for (const item of items) {
848
+ await transaction.updateWhere({
849
+ table: 'products',
850
+ data: { stock: { __raw: true, value: `stock - ${item.quantity}` } },
851
+ where: { id: item.product_id }
852
+ });
853
+ }
854
+ }
855
+
856
+ module.exports = { updateInventory };
857
+ ```
858
+
859
+ #### Checking Transaction State
860
+
861
+ ```javascript
862
+ if (db.transactionManager.hasActiveTransaction()) {
863
+ const transaction = db.transactionManager.getInstance();
864
+ await transaction.insert('logs', { message: 'Within transaction' });
865
+ } else {
866
+ await db.insert('logs', { message: 'No transaction' });
867
+ }
868
+ ```
869
+
870
+ ---
871
+
872
+ ## Advanced Usage
873
+
874
+ ### Multiple Database Connections
875
+
876
+ Connect to multiple MySQL databases simultaneously:
877
+
878
+ ```javascript
879
+ // Define database configurations
880
+ const primaryDB = {
881
+ host: 'primary-server.com',
882
+ user: 'admin',
883
+ password: 'secret',
884
+ database: 'main_app',
885
+ connectionLimit: 20
886
+ };
887
+
888
+ const analyticsDB = {
889
+ host: 'analytics-server.com',
890
+ user: 'reader',
891
+ password: 'readonly',
892
+ database: 'analytics',
893
+ connectionLimit: 5
894
+ };
895
+
896
+ const replicaDB = {
897
+ host: 'replica-server.com',
898
+ user: 'reader',
899
+ password: 'secret',
900
+ database: 'main_app',
901
+ connectionLimit: 10
902
+ };
903
+
904
+ // Initialize with default database
905
+ db.connectionManager.init(primaryDB);
906
+
907
+ // Use default connection
908
+ const users = await db.find('SELECT * FROM users');
909
+
910
+ // Use analytics database
911
+ const events = await db.find(
912
+ 'SELECT * FROM events WHERE date > ?',
913
+ ['2024-01-01'],
914
+ analyticsDB
915
+ );
916
+
917
+ // Use replica for read-heavy operations
918
+ const reports = await db.select({
919
+ table: 'orders',
920
+ where: { status: 'completed' },
921
+ orderBy: 'created_at DESC',
922
+ limit: 1000
923
+ }, replicaDB);
924
+
925
+ // Write to primary, read from replica
926
+ await db.insert('orders', orderData, primaryDB);
927
+ const order = await db.find('SELECT * FROM orders WHERE id = ?', [orderId], replicaDB);
928
+ ```
929
+
930
+ ### Raw SQL Expressions
931
+
932
+ Use raw SQL expressions in updates:
933
+
934
+ ```javascript
935
+ // Increment a value
936
+ await db.updateWhere({
937
+ table: 'products',
938
+ data: {
939
+ stock: { __raw: true, value: 'stock - 1' },
940
+ view_count: { __raw: true, value: 'view_count + 1' },
941
+ updated_at: new Date()
942
+ },
943
+ where: { id: productId }
944
+ });
945
+
946
+ // Use MySQL functions
947
+ await db.updateWhere({
948
+ table: 'users',
949
+ data: {
950
+ last_login: { __raw: true, value: 'NOW()' },
951
+ login_count: { __raw: true, value: 'login_count + 1' }
952
+ },
953
+ where: { id: userId }
954
+ });
955
+ ```
956
+
957
+ ### Custom Logger Integration
958
+
959
+ #### Winston Logger
960
+
961
+ ```javascript
962
+ const winston = require('winston');
963
+
964
+ const logger = winston.createLogger({
965
+ level: 'info',
966
+ format: winston.format.combine(
967
+ winston.format.timestamp(),
968
+ winston.format.json()
969
+ ),
970
+ transports: [
971
+ new winston.transports.File({ filename: 'error.log', level: 'error' }),
972
+ new winston.transports.File({ filename: 'database.log' }),
973
+ new winston.transports.Console({
974
+ format: winston.format.simple()
975
+ })
976
+ ]
977
+ });
978
+
979
+ db.connectionManager.init(dbConfig, logger);
980
+ // or
981
+ db.connectionManager.setLogger(logger);
982
+ ```
983
+
984
+ #### Bunyan Logger
985
+
986
+ ```javascript
987
+ const bunyan = require('bunyan');
988
+
989
+ const logger = bunyan.createLogger({
990
+ name: 'mysql-orm',
991
+ level: 'info'
992
+ });
993
+
994
+ db.connectionManager.setLogger(logger);
995
+ ```
996
+
997
+ #### Pino Logger
998
+
999
+ ```javascript
1000
+ const pino = require('pino');
1001
+
1002
+ const logger = pino({
1003
+ level: 'info',
1004
+ transport: {
1005
+ target: 'pino-pretty'
1006
+ }
1007
+ });
1008
+
1009
+ db.connectionManager.setLogger(logger);
1010
+ ```
1011
+
1012
+ ---
1013
+
1014
+ ## Error Handling
1015
+
1016
+ ### Common Error Codes
1017
+
1018
+ | Error Code | Description |
1019
+ |------------|-------------|
1020
+ | `ER_DUP_ENTRY` | Duplicate key violation |
1021
+ | `ER_NO_REFERENCED_ROW` | Foreign key constraint fails |
1022
+ | `ER_ROW_IS_REFERENCED` | Cannot delete parent row |
1023
+ | `ER_BAD_FIELD_ERROR` | Unknown column |
1024
+ | `ER_NO_SUCH_TABLE` | Table doesn't exist |
1025
+ | `PROTOCOL_CONNECTION_LOST` | Connection lost |
1026
+ | `ECONNREFUSED` | Connection refused |
1027
+ | `ER_CON_COUNT_ERROR` | Too many connections |
1028
+
1029
+ ### Error Handling Examples
1030
+
1031
+ ```javascript
1032
+ try {
1033
+ await db.insert('users', { email: 'existing@example.com' });
1034
+ } catch (error) {
1035
+ switch (error.code) {
1036
+ case 'ER_DUP_ENTRY':
1037
+ console.error('Email already exists');
1038
+ break;
1039
+ case 'ER_NO_SUCH_TABLE':
1040
+ console.error('Table does not exist');
1041
+ break;
1042
+ case 'ECONNREFUSED':
1043
+ console.error('Cannot connect to database');
1044
+ break;
1045
+ default:
1046
+ console.error('Database error:', error.message);
1047
+ }
1048
+ }
1049
+ ```
1050
+
1051
+ ### Transaction Error Handling
1052
+
1053
+ ```javascript
1054
+ const transaction = db.createTransaction();
1055
+
1056
+ try {
1057
+ await transaction.init();
1058
+
1059
+ await transaction.insert('orders', orderData);
1060
+ await transaction.insert('order_items', itemsData);
1061
+
1062
+ await transaction.commit();
1063
+ } catch (error) {
1064
+ // Rollback is safe to call multiple times
1065
+ await transaction.rollback();
1066
+
1067
+ if (error.code === 'ER_DUP_ENTRY') {
1068
+ throw new Error('Order already exists');
1069
+ }
1070
+
1071
+ throw error;
1072
+ }
1073
+ ```
1074
+
1075
+ ---
1076
+
1077
+ ## Performance Tips
1078
+
1079
+ 1. **Use Connection Pooling** (Built-in)
1080
+ - Connections are reused automatically
1081
+ - Set `connectionLimit` based on your needs
1082
+
1083
+ 2. **Monitor Slow Queries**
1084
+ - Queries taking >1 second are automatically logged
1085
+ - Review and optimize slow queries regularly
1086
+
1087
+ 3. **Use Transactions Wisely**
1088
+ - Group related operations in transactions
1089
+ - Keep transactions short to avoid lock contention
1090
+
1091
+ 4. **Use Parameterized Queries**
1092
+ - Always use `?` placeholders
1093
+ - Never concatenate user input into queries
1094
+
1095
+ 5. **Select Only Needed Fields**
1096
+ ```javascript
1097
+ // Good - select specific fields
1098
+ await db.select({
1099
+ table: 'users',
1100
+ fields: ['id', 'name', 'email']
1101
+ });
1102
+
1103
+ // Avoid - selecting all fields
1104
+ await db.select({ table: 'users' }); // SELECT *
1105
+ ```
1106
+
1107
+ 6. **Use Indexes**
1108
+ - Add indexes on frequently queried columns
1109
+ - Use EXPLAIN to analyze query performance
1110
+
1111
+ 7. **Pagination**
1112
+ ```javascript
1113
+ // Always use limit and offset for large datasets
1114
+ await db.select({
1115
+ table: 'logs',
1116
+ orderBy: 'created_at DESC',
1117
+ limit: 50,
1118
+ offset: page * 50
1119
+ });
1120
+ ```
1121
+
1122
+ 8. **Close Pools on Shutdown**
1123
+ ```javascript
1124
+ process.on('SIGINT', async () => {
1125
+ await db.connectionManager.closeAllPools();
1126
+ process.exit(0);
1127
+ });
1128
+
1129
+ process.on('SIGTERM', async () => {
1130
+ await db.connectionManager.closeAllPools();
1131
+ process.exit(0);
1132
+ });
1133
+ ```
1134
+
1135
+ ---
1136
+
1137
+ ## Complete Examples
1138
+
1139
+ ### User Service Class
1140
+
1141
+ ```javascript
1142
+ const db = require('mysql-orm-lite');
1143
+
1144
+ class UserService {
1145
+ constructor() {
1146
+ db.connectionManager.init({
1147
+ host: process.env.DB_HOST || 'localhost',
1148
+ user: process.env.DB_USER || 'root',
1149
+ password: process.env.DB_PASSWORD,
1150
+ database: process.env.DB_NAME,
1151
+ connectionLimit: 10
1152
+ });
1153
+ }
1154
+
1155
+ async createUser(userData) {
1156
+ const transaction = db.createTransaction();
1157
+
1158
+ try {
1159
+ await transaction.init();
1160
+
1161
+ const userId = await transaction.insert('users', {
1162
+ email: userData.email,
1163
+ password: userData.hashedPassword,
1164
+ created_at: new Date()
1165
+ });
1166
+
1167
+ await transaction.insert('profiles', {
1168
+ user_id: userId,
1169
+ first_name: userData.firstName,
1170
+ last_name: userData.lastName,
1171
+ avatar: userData.avatar || null
1172
+ });
1173
+
1174
+ await transaction.insert('user_settings', {
1175
+ user_id: userId,
1176
+ theme: 'system',
1177
+ notifications: true,
1178
+ language: 'en'
1179
+ });
1180
+
1181
+ await transaction.commit();
1182
+ return userId;
1183
+ } catch (error) {
1184
+ await transaction.rollback();
1185
+ throw error;
1186
+ }
1187
+ }
1188
+
1189
+ async getUser(userId) {
1190
+ const users = await db.select({
1191
+ table: 'users',
1192
+ alias: 'u',
1193
+ fields: ['u.id', 'u.email', 'p.first_name', 'p.last_name', 'p.avatar'],
1194
+ joins: [{
1195
+ type: 'LEFT',
1196
+ table: 'profiles',
1197
+ alias: 'p',
1198
+ on: 'u.id = p.user_id'
1199
+ }],
1200
+ where: { 'u.id': userId }
1201
+ });
1202
+
1203
+ return users[0] || null;
1204
+ }
1205
+
1206
+ async getUsers(filters = {}, page = 1, limit = 20) {
1207
+ const offset = (page - 1) * limit;
1208
+
1209
+ const where = { deleted_at: null };
1210
+ if (filters.status) where.status = filters.status;
1211
+ if (filters.search) where.email = { $like: `%${filters.search}%` };
1212
+
1213
+ const users = await db.select({
1214
+ table: 'users',
1215
+ fields: ['id', 'email', 'status', 'created_at'],
1216
+ where,
1217
+ orderBy: 'created_at DESC',
1218
+ limit,
1219
+ offset
1220
+ });
1221
+
1222
+ const total = await db.findCount(
1223
+ 'SELECT COUNT(*) as count FROM users WHERE deleted_at IS NULL'
1224
+ );
1225
+
1226
+ return {
1227
+ data: users,
1228
+ pagination: {
1229
+ page,
1230
+ limit,
1231
+ total,
1232
+ pages: Math.ceil(total / limit)
1233
+ }
1234
+ };
1235
+ }
1236
+
1237
+ async updateUser(userId, updates) {
1238
+ return await db.updateWhere({
1239
+ table: 'users',
1240
+ data: {
1241
+ ...updates,
1242
+ updated_at: new Date()
1243
+ },
1244
+ where: { id: userId }
1245
+ });
1246
+ }
1247
+
1248
+ async deleteUser(userId) {
1249
+ // Soft delete
1250
+ return await db.updateWhere({
1251
+ table: 'users',
1252
+ data: {
1253
+ deleted_at: new Date(),
1254
+ status: 'deleted'
1255
+ },
1256
+ where: { id: userId }
1257
+ });
1258
+ }
1259
+ }
1260
+
1261
+ module.exports = new UserService();
1262
+ ```
1263
+
1264
+ ### Express.js Integration
1265
+
1266
+ ```javascript
1267
+ const express = require('express');
1268
+ const db = require('mysql-orm-lite');
1269
+
1270
+ const app = express();
1271
+ app.use(express.json());
1272
+
1273
+ // Initialize database
1274
+ db.connectionManager.init({
1275
+ host: process.env.DB_HOST,
1276
+ user: process.env.DB_USER,
1277
+ password: process.env.DB_PASSWORD,
1278
+ database: process.env.DB_NAME
1279
+ });
1280
+
1281
+ // Routes
1282
+ app.get('/users', async (req, res) => {
1283
+ try {
1284
+ const { page = 1, limit = 20, status } = req.query;
1285
+
1286
+ const where = {};
1287
+ if (status) where.status = status;
1288
+
1289
+ const users = await db.select({
1290
+ table: 'users',
1291
+ fields: ['id', 'name', 'email', 'status'],
1292
+ where,
1293
+ orderBy: 'created_at DESC',
1294
+ limit: parseInt(limit),
1295
+ offset: (parseInt(page) - 1) * parseInt(limit)
1296
+ });
1297
+
1298
+ res.json({ success: true, data: users });
1299
+ } catch (error) {
1300
+ res.status(500).json({ success: false, error: error.message });
1301
+ }
1302
+ });
1303
+
1304
+ app.post('/users', async (req, res) => {
1305
+ try {
1306
+ const id = await db.insert('users', req.body);
1307
+ res.status(201).json({ success: true, id });
1308
+ } catch (error) {
1309
+ if (error.code === 'ER_DUP_ENTRY') {
1310
+ res.status(409).json({ success: false, error: 'User already exists' });
1311
+ } else {
1312
+ res.status(500).json({ success: false, error: error.message });
1313
+ }
1314
+ }
1315
+ });
1316
+
1317
+ // Graceful shutdown
1318
+ const server = app.listen(3000);
1319
+
1320
+ process.on('SIGTERM', async () => {
1321
+ server.close();
1322
+ await db.connectionManager.closeAllPools();
1323
+ process.exit(0);
1324
+ });
1325
+ ```
1326
+
1327
+ ---
1328
+
1329
+ ## API Quick Reference
1330
+
1331
+ ### Main Exports
1332
+
1333
+ ```javascript
1334
+ const db = require('mysql-orm-lite');
1335
+
1336
+ // Connection
1337
+ db.connectionManager.init(config, logger?)
1338
+ db.connectionManager.setLogger(logger)
1339
+ db.connectionManager.getPool(config?)
1340
+ db.connectionManager.closePool(config)
1341
+ db.connectionManager.closeAllPools()
1342
+ db.connectionManager.getLogger()
1343
+
1344
+ // CRUD
1345
+ db.insert(table, data, dbConfig?, debug?, isIgnore?)
1346
+ db.update(table, data, whereClause, dbConfig?, debug?)
1347
+ db.delete(whereClause, table, dbConfig?)
1348
+ db.find(query, params?, dbConfig?)
1349
+ db.findCount(query, params?, dbConfig?)
1350
+
1351
+ // Query Builder
1352
+ db.select(options, dbConfig?) // alias: findWhere, query
1353
+ db.buildAndExecuteSelectQuery(options, dbConfig?)
1354
+ db.updateWhere(options, dbConfig?) // alias: updateQuery
1355
+ db.buildAndExecuteUpdateQuery(options, dbConfig?)
1356
+ db.deleteWhere(options, dbConfig?) // alias: remove
1357
+ db.buildAndExecuteDeleteQuery(options, dbConfig?)
1358
+
1359
+ // Transactions
1360
+ db.createTransaction()
1361
+ db.TransactionCRUD
1362
+ db.transactionManager.setInstance(transaction)
1363
+ db.transactionManager.getInstance()
1364
+ db.transactionManager.clearInstance()
1365
+ db.transactionManager.hasActiveTransaction()
1366
+
1367
+ // Utilities
1368
+ db.utils
1369
+ ```
1370
+
1371
+ ### TransactionCRUD Methods
1372
+
1373
+ ```javascript
1374
+ const transaction = db.createTransaction();
1375
+
1376
+ // Lifecycle
1377
+ await transaction.init(dbConfig?)
1378
+ await transaction.commit()
1379
+ await transaction.rollback()
1380
+
1381
+ // Raw query
1382
+ await transaction.executeQuery(query, params?)
1383
+ await transaction.find(query, params?)
1384
+
1385
+ // CRUD
1386
+ await transaction.insert(table, data)
1387
+ await transaction.update(table, data, whereClause)
1388
+ await transaction.delete(whereClause, table)
1389
+
1390
+ // Query Builder (verbose)
1391
+ await transaction.buildAndExecuteSelectQuery(options)
1392
+ await transaction.buildAndExecuteUpdateQuery(options)
1393
+ await transaction.buildAndExecuteDeleteQuery(options)
1394
+
1395
+ // Query Builder (aliases)
1396
+ await transaction.select(options)
1397
+ await transaction.findWhere(options)
1398
+ await transaction.query(options)
1399
+ await transaction.updateWhere(options)
1400
+ await transaction.updateQuery(options)
1401
+ await transaction.deleteWhere(options)
1402
+ await transaction.remove(options)
1403
+ ```
1404
+
1405
+ ---
1406
+
1407
+ ## License
1408
+
1409
+ MIT License - see [LICENSE](LICENSE) file for details.
1410
+
1411
+ ## Contributing
1412
+
1413
+ Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
1414
+
1415
+ ## Support
1416
+
1417
+ For issues and questions: [GitHub Issues](https://github.com/Manikandan-Thonthiraj/mysql-orm-lite/issues)
1418
+
1419
+ ---
1420
+
1421
+ Made with âĪïļ for the Node.js community