@dreamtree-org/korm-js 1.0.39 → 1.0.40

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.
Files changed (2) hide show
  1. package/README.md +720 -158
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,55 +1,58 @@
1
1
  # KORM-JS
2
2
 
3
- **Knowledge Object-Relational Mapping** - A powerful, modular ORM system for Node.js with dynamic database operations, complex queries, relationships, and nested requests.
3
+ **Knowledge Object-Relational Mapping** - A powerful, modular ORM system for Node.js with dynamic database operations, complex queries, relationships, nested requests, and soft delete support.
4
4
 
5
- [![npm version](https://badge.fury.io/js/korm-js.svg)](https://badge.fury.io/js/korm-js)
5
+ [![npm version](https://badge.fury.io/js/@dreamtree-org%2Fkorm-js.svg)](https://badge.fury.io/js/@dreamtree-org%2Fkorm-js)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
8
  ## Features
9
9
 
10
10
  - 🚀 **Multi-Database Support**: MySQL, PostgreSQL, SQLite
11
- - 🔧 **Dynamic Operations**: Create, Read, Update, Delete with ease
12
- - 🔍 **Advanced Querying**: Complex queries with relationships
11
+ - 🔧 **Dynamic Operations**: Create, Read, Update, Delete, Replace, Upsert, Sync
12
+ - 🔍 **Advanced Querying**: Complex queries with relationships, joins, and filtering
13
13
  - 🛡️ **Data Validation**: Built-in request validation with custom rules
14
14
  - 🔄 **Auto-Sync**: Automatic table schema synchronization
15
15
  - 📦 **Modular Design**: Use only what you need
16
16
  - 🎯 **Express.js Ready**: Perfect integration with Express applications
17
17
  - 🔄 **Schema Generation**: Auto-generate schemas from existing databases
18
+ - 🗑️ **Soft Delete**: Built-in soft delete support with automatic filtering
19
+ - 🎣 **Model Hooks**: Before, After, Validate, and Custom action hooks
18
20
 
19
21
  ## Installation
20
22
 
21
23
  ```bash
22
- npm install korm-js
24
+ npm install @dreamtree-org/korm-js
23
25
  ```
24
26
 
25
- ## Quick Start with Express.js and PostgreSQL
27
+ ## Quick Start with Express.js and MySQL
26
28
 
27
29
  ### 1. Basic Setup
28
30
 
29
31
  ```javascript
30
32
  require('dotenv').config();
31
33
  const express = require('express');
32
- const { initializeKORM, validate, helperUtility } = require('korm-js');
34
+ const { initializeKORM, validate, helperUtility } = require('@dreamtree-org/korm-js');
33
35
  const knex = require('knex');
34
36
 
35
37
  const app = express();
36
38
  app.use(express.json());
37
39
 
38
- // Database configuration
40
+ // MySQL database configuration
39
41
  const db = knex({
40
- client: 'pg',
42
+ client: 'mysql2',
41
43
  connection: {
42
44
  host: process.env.DB_HOST || 'localhost',
43
- user: process.env.DB_USER || 'your_user',
44
- password: process.env.DB_PASS || 'your_pass',
45
- database: process.env.DB_NAME || 'your_db'
45
+ user: process.env.DB_USER || 'root',
46
+ password: process.env.DB_PASS || 'password',
47
+ database: process.env.DB_NAME || 'my_database',
48
+ port: process.env.DB_PORT || 3306
46
49
  }
47
50
  });
48
51
 
49
- // Initialize KORM
52
+ // Initialize KORM with MySQL
50
53
  const korm = initializeKORM({
51
54
  db: db,
52
- dbClient: 'pg'
55
+ dbClient: 'mysql'
53
56
  });
54
57
 
55
58
  app.listen(3000, () => {
@@ -63,9 +66,11 @@ app.listen(3000, () => {
63
66
  async function initApp() {
64
67
  // Load existing schema if available
65
68
  let syncSchema = helperUtility.file.readJSON('schema/sync.json');
66
- korm.setSchema(syncSchema);
69
+ if (syncSchema) {
70
+ korm.setSchema(syncSchema);
71
+ }
67
72
 
68
- // Sync database with schema
73
+ // Sync database with schema (creates tables if they don't exist)
69
74
  await korm.syncDatabase().then(() => {
70
75
  console.log('✅ Database synced');
71
76
  });
@@ -103,7 +108,9 @@ app.get('/health', (req, res) => {
103
108
  });
104
109
  ```
105
110
 
106
- ### 4. Example API Usage
111
+ ## Complete CRUD Operations Guide
112
+
113
+ ### 1. Create Operation
107
114
 
108
115
  ```javascript
109
116
  // Create a new record
@@ -114,11 +121,36 @@ POST /api/Users/crud
114
121
  "username": "john_doe",
115
122
  "email": "john@example.com",
116
123
  "first_name": "John",
117
- "last_name": "Doe"
124
+ "last_name": "Doe",
125
+ "age": 30,
126
+ "is_active": true
118
127
  }
119
128
  }
120
129
 
121
- // Get all records
130
+ // Response
131
+ {
132
+ "message": "Record created successfully",
133
+ "data": [
134
+ {
135
+ "id": 1,
136
+ "username": "john_doe",
137
+ "email": "john@example.com",
138
+ "first_name": "John",
139
+ "last_name": "Doe",
140
+ "age": 30,
141
+ "is_active": true,
142
+ "created_at": "2024-01-01T00:00:00.000Z",
143
+ "updated_at": "2024-01-01T00:00:00.000Z"
144
+ }
145
+ ],
146
+ "success": true
147
+ }
148
+ ```
149
+
150
+ ### 2. List Operation (Read Multiple Records)
151
+
152
+ ```javascript
153
+ // Get all records with basic filtering
122
154
  POST /api/Users/crud
123
155
  {
124
156
  "action": "list",
@@ -128,103 +160,491 @@ POST /api/Users/crud
128
160
  "limit": 10
129
161
  }
130
162
 
131
- // Get single record
163
+ // List with pagination
164
+ POST /api/Users/crud
165
+ {
166
+ "action": "list",
167
+ "select": ["id", "username", "email"],
168
+ "sort": [["created_at", "desc"]],
169
+ "limit": 10,
170
+ "offset": 20
171
+ }
172
+
173
+ // List with complex conditions
174
+ POST /api/Users/crud
175
+ {
176
+ "action": "list",
177
+ "where": {
178
+ "first_name": { "like": "%John%" },
179
+ "email": { "like": "%@example.com" },
180
+ "age": { ">=": 18, "<=": 65 },
181
+ "is_active": true
182
+ },
183
+ "select": ["id", "username", "email", "first_name", "last_name"],
184
+ "sort": [["created_at", "desc"], ["last_name", "asc"]],
185
+ "limit": 10
186
+ }
187
+
188
+ // Response
189
+ {
190
+ "data": [
191
+ {
192
+ "id": 1,
193
+ "username": "john_doe",
194
+ "email": "john@example.com",
195
+ "first_name": "John",
196
+ "last_name": "Doe"
197
+ }
198
+ ],
199
+ "total": 1,
200
+ "page": 1,
201
+ "limit": 10
202
+ }
203
+ ```
204
+
205
+ ### 3. Show Operation (Read Single Record)
206
+
207
+ ```javascript
208
+ // Get single record by ID
132
209
  POST /api/Users/crud
133
210
  {
134
211
  "action": "show",
135
212
  "where": { "id": 1 }
136
213
  }
137
214
 
138
- // Update record
215
+ // Get single record with multiple conditions
216
+ POST /api/Users/crud
217
+ {
218
+ "action": "show",
219
+ "where": {
220
+ "username": "john_doe",
221
+ "is_active": true
222
+ }
223
+ }
224
+
225
+ // Response
226
+ {
227
+ "id": 1,
228
+ "username": "john_doe",
229
+ "email": "john@example.com",
230
+ "first_name": "John",
231
+ "last_name": "Doe",
232
+ "age": 30,
233
+ "is_active": true,
234
+ "created_at": "2024-01-01T00:00:00.000Z",
235
+ "updated_at": "2024-01-01T00:00:00.000Z"
236
+ }
237
+ ```
238
+
239
+ ### 4. Update Operation
240
+
241
+ ```javascript
242
+ // Update record by ID
139
243
  POST /api/Users/crud
140
244
  {
141
245
  "action": "update",
142
246
  "where": { "id": 1 },
143
247
  "data": {
144
248
  "first_name": "Jane",
145
- "updated_at": "2024-01-01T00:00:00Z"
249
+ "last_name": "Smith",
250
+ "updated_at": new Date().toISOString()
251
+ }
252
+ }
253
+
254
+ // Update multiple records
255
+ POST /api/Users/crud
256
+ {
257
+ "action": "update",
258
+ "where": { "is_active": false },
259
+ "data": {
260
+ "is_active": true,
261
+ "updated_at": new Date().toISOString()
146
262
  }
147
263
  }
148
264
 
149
- // Delete record
265
+ // Response
266
+ {
267
+ "message": "Record updated successfully",
268
+ "data": [
269
+ {
270
+ "id": 1,
271
+ "first_name": "Jane",
272
+ "last_name": "Smith",
273
+ "updated_at": "2024-01-01T00:00:00.000Z"
274
+ }
275
+ ],
276
+ "success": true
277
+ }
278
+ ```
279
+
280
+ ### 5. Delete Operation
281
+
282
+ ```javascript
283
+ // Hard delete (permanent deletion)
150
284
  POST /api/Users/crud
151
285
  {
152
286
  "action": "delete",
153
287
  "where": { "id": 1 }
154
288
  }
155
289
 
156
- // Count records
290
+ // Delete multiple records
291
+ POST /api/Users/crud
292
+ {
293
+ "action": "delete",
294
+ "where": { "is_active": false }
295
+ }
296
+
297
+ // Response
298
+ {
299
+ "message": "Record deleted successfully",
300
+ "data": [/* deleted records */],
301
+ "success": true
302
+ }
303
+ ```
304
+
305
+ ### 6. Count Operation
306
+
307
+ ```javascript
308
+ // Count all records
309
+ POST /api/Users/crud
310
+ {
311
+ "action": "count"
312
+ }
313
+
314
+ // Count with conditions
157
315
  POST /api/Users/crud
158
316
  {
159
317
  "action": "count",
160
- "where": { "is_active": true }
318
+ "where": {
319
+ "is_active": true,
320
+ "created_at": { ">=": "2024-01-01" }
321
+ }
161
322
  }
323
+
324
+ // Response
325
+ 42 // Number of matching records
162
326
  ```
163
327
 
164
- ### 5. Advanced Querying Examples
328
+ ### 7. Replace Operation
165
329
 
166
330
  ```javascript
167
- // Complex search with multiple conditions
331
+ // Replace record (MySQL specific - replaces entire row)
332
+ POST /api/Users/crud
333
+ {
334
+ "action": "replace",
335
+ "data": {
336
+ "id": 1,
337
+ "username": "john_doe_updated",
338
+ "email": "john.updated@example.com",
339
+ "first_name": "John",
340
+ "last_name": "Doe Updated"
341
+ }
342
+ }
343
+
344
+ // Response
345
+ {
346
+ "message": "Record replaced successfully",
347
+ "data": [/* replaced record */],
348
+ "success": true
349
+ }
350
+ ```
351
+
352
+ ### 8. Upsert Operation (Insert or Update)
353
+
354
+ ```javascript
355
+ // Upsert record (insert if not exists, update if exists)
356
+ POST /api/Users/crud
357
+ {
358
+ "action": "upsert",
359
+ "data": {
360
+ "username": "john_doe",
361
+ "email": "john@example.com",
362
+ "first_name": "John",
363
+ "last_name": "Doe"
364
+ },
365
+ "conflict": ["username"] // Conflict columns for MySQL
366
+ }
367
+
368
+ // Response
369
+ {
370
+ "message": "Record upserted successfully",
371
+ "data": [/* upserted record */],
372
+ "success": true
373
+ }
374
+ ```
375
+
376
+ ### 9. Sync Operation (Upsert + Delete)
377
+
378
+ ```javascript
379
+ // Sync operation: upsert records and delete others matching where clause
380
+ POST /api/Users/crud
381
+ {
382
+ "action": "sync",
383
+ "data": {
384
+ "username": "john_doe",
385
+ "email": "john@example.com",
386
+ "first_name": "John",
387
+ "last_name": "Doe"
388
+ },
389
+ "conflict": ["username"],
390
+ "where": {
391
+ "created_at": { "<": "2024-01-01" }
392
+ }
393
+ }
394
+
395
+ // Response
396
+ {
397
+ "message": "Record synced successfully",
398
+ "data": {
399
+ "insertOrUpdateQuery": [/* upserted records */],
400
+ "deleteQuery": [/* deleted records */]
401
+ },
402
+ "success": true
403
+ }
404
+ ```
405
+
406
+ ## Advanced Querying
407
+
408
+ ### Complex Where Conditions
409
+
410
+ ```javascript
411
+ // Multiple operators
168
412
  POST /api/Users/crud
169
413
  {
170
414
  "action": "list",
171
415
  "where": {
172
- "first_name": { "like": "%John%" },
416
+ "age": { ">=": 18, "<=": 65 },
173
417
  "email": { "like": "%@example.com" },
174
- "is_active": true
175
- },
176
- "select": ["id", "username", "email", "first_name", "last_name"],
177
- "sort": [["created_at", "desc"]],
178
- "limit": 10
418
+ "first_name": { "in": ["John", "Jane", "Bob"] },
419
+ "is_active": true,
420
+ "created_at": { ">=": "2024-01-01", "<=": "2024-12-31" }
421
+ }
179
422
  }
180
423
 
181
- // Pagination with offset
424
+ // OR conditions (using arrays)
182
425
  POST /api/Users/crud
183
426
  {
184
427
  "action": "list",
185
- "select": ["id", "username", "email", "first_name", "last_name"],
186
- "sort": [["created_at", "desc"]],
187
- "limit": 10,
188
- "offset": 20
428
+ "where": {
429
+ "or": [
430
+ { "first_name": "John" },
431
+ { "last_name": "Doe" }
432
+ ]
433
+ }
189
434
  }
190
435
 
191
- // Count with conditions
436
+ // NULL checks
192
437
  POST /api/Users/crud
193
438
  {
194
- "action": "count",
439
+ "action": "list",
195
440
  "where": {
196
- "is_active": true,
197
- "created_at": { ">=": "2024-01-01" }
441
+ "deleted_at": null
198
442
  }
199
443
  }
444
+ ```
200
445
 
201
- // Join queries (if relationships are defined)
446
+ ### Sorting
447
+
448
+ ```javascript
449
+ // Single sort
202
450
  POST /api/Users/crud
203
451
  {
204
452
  "action": "list",
205
- "select": ["id", "username", "email"],
453
+ "sort": [["created_at", "desc"]]
454
+ }
455
+
456
+ // Multiple sorts
457
+ POST /api/Users/crud
458
+ {
459
+ "action": "list",
460
+ "sort": [
461
+ ["is_active", "desc"],
462
+ ["created_at", "desc"],
463
+ ["last_name", "asc"]
464
+ ]
465
+ }
466
+ ```
467
+
468
+ ### Joins
469
+
470
+ ```javascript
471
+ // Inner join
472
+ POST /api/Users/crud
473
+ {
474
+ "action": "list",
475
+ "select": ["users.id", "users.username", "profiles.bio"],
206
476
  "join": [
207
477
  {
208
478
  "table": "user_profiles",
209
- "on": "users.id = user_profiles.user_id"
479
+ "as": "profiles",
480
+ "on": "users.id = profiles.user_id",
481
+ "type": "inner"
210
482
  }
211
483
  ],
212
484
  "where": { "users.is_active": true }
213
485
  }
486
+
487
+ // Left join
488
+ POST /api/Users/crud
489
+ {
490
+ "action": "list",
491
+ "select": ["users.*", "profiles.bio"],
492
+ "join": [
493
+ {
494
+ "table": "user_profiles",
495
+ "as": "profiles",
496
+ "on": "users.id = profiles.user_id",
497
+ "type": "left"
498
+ }
499
+ ]
500
+ }
214
501
  ```
215
502
 
216
- ### 6. Data Validation Example
503
+ ## Soft Delete Support
504
+
505
+ ### Enabling Soft Delete
506
+
507
+ Create a model file at `models/Users.model.js`:
217
508
 
218
509
  ```javascript
219
- const { validate } = require('korm-js');
510
+ class Users {
511
+ // Enable soft delete for this model
512
+ hasSoftDelete = true;
513
+
514
+ // Optional: Custom soft delete hook
515
+ beforeDelete({ model, action, request, context, db, utils, controller }) {
516
+ // Custom logic before soft delete
517
+ console.log('Soft deleting user:', request.where);
518
+ }
519
+
520
+ // Optional: Custom hook after soft delete
521
+ afterDelete({ model, action, data, request, context, db, utils, controller }) {
522
+ // Custom logic after soft delete
523
+ console.log('User soft deleted:', data);
524
+ }
525
+ }
526
+
527
+ module.exports = Users;
528
+ ```
529
+
530
+ ### Using Soft Delete
531
+
532
+ ```javascript
533
+ // When soft delete is enabled, delete action automatically sets deleted_at
534
+ POST /api/Users/crud
535
+ {
536
+ "action": "delete",
537
+ "where": { "id": 1 }
538
+ }
539
+
540
+ // List operation automatically filters out soft-deleted records
541
+ POST /api/Users/crud
542
+ {
543
+ "action": "list",
544
+ "where": { "is_active": true }
545
+ // Automatically adds: deleted_at IS NULL
546
+ }
547
+
548
+ // To see soft-deleted records, you need to query directly
549
+ // (soft delete only affects list and delete operations)
550
+ ```
551
+
552
+ ## Model Hooks
553
+
554
+ ### Creating Model Hooks
555
+
556
+ Create a model file at `models/Users.model.js`:
557
+
558
+ ```javascript
559
+ class Users {
560
+ // Validation hook - runs before any action
561
+ async validate({ model, action, request, context, db, utils, controller }) {
562
+ if (action === 'create' || action === 'update') {
563
+ if (!request.data.email) {
564
+ throw new Error('Email is required');
565
+ }
566
+ // Check if email already exists
567
+ const existing = await db('users')
568
+ .where('email', request.data.email)
569
+ .first();
570
+ if (existing && existing.id !== request.where?.id) {
571
+ throw new Error('Email already exists');
572
+ }
573
+ }
574
+ }
575
+
576
+ // Before hook - runs before the action executes
577
+ async beforeCreate({ model, action, request, context, db, utils, controller }) {
578
+ // Add timestamps
579
+ request.data.created_at = new Date();
580
+ request.data.updated_at = new Date();
581
+ return request.data;
582
+ }
583
+
584
+ async beforeUpdate({ model, action, request, context, db, utils, controller }) {
585
+ // Update timestamp
586
+ request.data.updated_at = new Date();
587
+ return request.data;
588
+ }
589
+
590
+ // After hook - runs after the action executes
591
+ async afterCreate({ model, action, data, request, context, db, utils, controller }) {
592
+ // Log creation
593
+ console.log('User created:', data);
594
+ // Send welcome email, etc.
595
+ return data;
596
+ }
597
+
598
+ async afterUpdate({ model, action, data, request, context, db, utils, controller }) {
599
+ // Log update
600
+ console.log('User updated:', data);
601
+ return data;
602
+ }
603
+
604
+ // Custom action hook
605
+ async onCustomAction({ model, action, request, context, db, utils, controller }) {
606
+ // Handle custom actions like 'activate', 'deactivate', etc.
607
+ if (action === 'activate') {
608
+ return await db('users')
609
+ .where(request.where)
610
+ .update({ is_active: true, updated_at: new Date() });
611
+ }
612
+ throw new Error(`Unknown custom action: ${action}`);
613
+ }
614
+
615
+ // Soft delete support
616
+ hasSoftDelete = true;
617
+ }
618
+
619
+ module.exports = Users;
620
+ ```
621
+
622
+ ### Using Custom Actions
623
+
624
+ ```javascript
625
+ // Call custom action
626
+ POST /api/Users/crud
627
+ {
628
+ "action": "activate",
629
+ "where": { "id": 1 }
630
+ }
631
+ ```
632
+
633
+ ## Data Validation
634
+
635
+ ### Basic Validation
636
+
637
+ ```javascript
638
+ const { validate } = require('@dreamtree-org/korm-js');
220
639
 
221
640
  // Define validation rules
222
641
  const userValidationRules = {
223
- username: 'required|type:string|maxLen:50',
642
+ username: 'required|type:string|minLen:3|maxLen:50',
224
643
  email: 'required|type:string|maxLen:255',
225
644
  first_name: 'required|type:string|maxLen:100',
226
645
  last_name: 'required|type:string|maxLen:100',
227
- age: 'type:number|min:0|max:150'
646
+ age: 'type:number|min:0|max:150',
647
+ is_active: 'type:boolean'
228
648
  };
229
649
 
230
650
  // Validate and create user
@@ -235,24 +655,20 @@ app.post('/api/users/validate', async (req, res) => {
235
655
 
236
656
  const result = await korm.processRequest({
237
657
  action: 'create',
238
- data: {
239
- ...validatedData,
240
- created_at: new Date(),
241
- updated_at: new Date()
242
- }
658
+ data: validatedData
243
659
  }, 'Users');
244
660
 
245
661
  res.status(201).json({
246
662
  success: true,
247
663
  message: 'User created successfully',
248
- data: result.data
664
+ data: result
249
665
  });
250
666
  } catch (error) {
251
667
  if (error.name === 'ValidationError') {
252
668
  return res.status(400).json({
253
669
  success: false,
254
670
  message: 'Validation failed',
255
- errors: error.message.details
671
+ errors: error.message
256
672
  });
257
673
  }
258
674
 
@@ -265,28 +681,102 @@ app.post('/api/users/validate', async (req, res) => {
265
681
  });
266
682
  ```
267
683
 
268
- ### 7. Schema Structure Example
684
+ ### Advanced Validation
685
+
686
+ ```javascript
687
+ const { validate } = require('@dreamtree-org/korm-js');
688
+
689
+ // Advanced validation rules
690
+ const advancedRules = {
691
+ username: 'required|type:string|regex:username',
692
+ email: 'required|type:string|regex:email',
693
+ phone: 'regex:phone',
694
+ password: 'required|type:string|minLen:8',
695
+ confirm_password: 'required|type:string|match:password',
696
+ status: 'in:active,inactive,pending',
697
+ user_id: 'exists:users,id'
698
+ };
699
+
700
+ const customRegex = {
701
+ username: /^[a-zA-Z0-9_]+$/,
702
+ email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
703
+ phone: /^\+?[\d\s-()]{10,15}$/
704
+ };
705
+
706
+ const validatedData = await validate(data, advancedRules, { customRegex });
707
+ ```
708
+
709
+ ## Nested Requests
710
+
711
+ ```javascript
712
+ // Process multiple related requests in one call
713
+ POST /api/Users/crud
714
+ {
715
+ "action": "show",
716
+ "where": { "id": 1 },
717
+ "other_requests": {
718
+ "Posts": {
719
+ "action": "list",
720
+ "where": { "user_id": 1 },
721
+ "limit": 10
722
+ },
723
+ "Comments": {
724
+ "action": "list",
725
+ "where": { "user_id": 1 },
726
+ "limit": 5
727
+ }
728
+ }
729
+ }
730
+
731
+ // Response includes nested data
732
+ {
733
+ "id": 1,
734
+ "username": "john_doe",
735
+ "email": "john@example.com",
736
+ "other_responses": {
737
+ "Posts": {
738
+ "data": [/* posts */],
739
+ "total": 10
740
+ },
741
+ "Comments": {
742
+ "data": [/* comments */],
743
+ "total": 5
744
+ }
745
+ }
746
+ }
747
+ ```
748
+
749
+ ## Schema Structure
750
+
751
+ ### Example Schema (Auto-generated from MySQL)
269
752
 
270
753
  ```javascript
271
- // Example schema structure (auto-generated)
272
754
  {
273
755
  "Users": {
274
756
  "table": "users",
275
757
  "alias": "Users",
758
+ "modelName": "Users",
276
759
  "columns": {
277
- "id": "bigint|size:8|default:nextval('users_id_seq'::regclass)",
278
- "username": "character varying|size:255",
279
- "email": "character varying|size:255",
280
- "first_name": "character varying|size:255|nullable",
281
- "last_name": "character varying|size:255|nullable",
282
- "is_active": "boolean|default:true",
283
- "created_at": "timestamp with time zone|default:now()",
284
- "updated_at": "timestamp with time zone|default:now()"
760
+ "id": "bigint|size:8|unsigned|auto_increment",
761
+ "username": "varchar|size:255",
762
+ "email": "varchar|size:255",
763
+ "first_name": "varchar|size:255|nullable",
764
+ "last_name": "varchar|size:255|nullable",
765
+ "age": "int|size:4|nullable",
766
+ "is_active": "tinyint|size:1|default:1",
767
+ "created_at": "timestamp|default:CURRENT_TIMESTAMP",
768
+ "updated_at": "timestamp|default:CURRENT_TIMESTAMP|onUpdate:CURRENT_TIMESTAMP",
769
+ "deleted_at": "timestamp|nullable"
285
770
  },
286
- "modelName": "Users",
287
771
  "seed": [],
288
772
  "hasRelations": {},
289
- "indexes": []
773
+ "indexes": [
774
+ {
775
+ "name": "idx_users_email",
776
+ "columns": ["email"],
777
+ "unique": true
778
+ }
779
+ ]
290
780
  }
291
781
  }
292
782
  ```
@@ -298,12 +788,15 @@ app.post('/api/users/validate', async (req, res) => {
298
788
  ```javascript
299
789
  const korm = initializeKORM({
300
790
  db: db,
301
- dbClient: 'pg' // or 'mysql', 'sqlite'
791
+ dbClient: 'mysql' // or 'pg', 'sqlite'
302
792
  });
303
793
 
304
794
  // Process any CRUD request
305
795
  const result = await korm.processRequest(requestBody, modelName);
306
796
 
797
+ // Process request with nested requests
798
+ const result = await korm.processRequestWithOthers(requestBody, modelName);
799
+
307
800
  // Set schema manually
308
801
  korm.setSchema(schemaObject);
309
802
 
@@ -314,139 +807,100 @@ await korm.syncDatabase();
314
807
  const schema = await korm.generateSchema();
315
808
  ```
316
809
 
317
- ### ProcessRequest Actions
318
-
319
- ```javascript
320
- // List records
321
- {
322
- "action": "list",
323
- "where": { "is_active": true },
324
- "select": ["id", "name", "email"],
325
- "sort": [["created_at", "desc"]],
326
- "limit": 10,
327
- "offset": 0
328
- }
329
-
330
- // Show single record
331
- {
332
- "action": "show",
333
- "where": { "id": 1 }
334
- }
335
-
336
- // Create record
337
- {
338
- "action": "create",
339
- "data": { "name": "John", "email": "john@example.com" }
340
- }
341
-
342
- // Update record
343
- {
344
- "action": "update",
345
- "where": { "id": 1 },
346
- "data": { "name": "Jane" }
347
- }
348
-
349
- // Delete record
350
- {
351
- "action": "delete",
352
- "where": { "id": 1 }
353
- }
354
-
355
- // Count records
356
- {
357
- "action": "count",
358
- "where": { "is_active": true }
359
- }
360
- ```
361
-
362
- ### Validation Rules
810
+ ### ControllerWrapper Static Methods
363
811
 
364
812
  ```javascript
365
- const { validate } = require('korm-js');
366
-
367
- // Basic validation rules
368
- const rules = {
369
- username: 'required|type:string|minLen:3|maxLen:30',
370
- email: 'required|type:string|maxLen:255',
371
- age: 'type:number|min:0|max:150',
372
- status: 'in:active,inactive,pending'
373
- };
813
+ const { ControllerWrapper } = require('@dreamtree-org/korm-js');
374
814
 
375
- // Advanced validation with custom patterns
376
- const advancedRules = {
377
- username: 'required|type:string|regex:username',
378
- email: 'required|type:string|regex:email',
379
- phone: 'regex:phone',
380
- user_id: 'exists:users,id'
381
- };
815
+ // Load model class
816
+ const ModelClass = ControllerWrapper.loadModelClass('Users');
382
817
 
383
- const customRegex = {
384
- username: /^[a-zA-Z0-9_]+$/,
385
- email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
386
- phone: /^\+?[\d\s-()]{10,15}$/
387
- };
818
+ // Get model instance
819
+ const modelInstance = ControllerWrapper.getModelInstance(model);
388
820
 
389
- const validatedData = await validate(data, rules, { customRegex });
821
+ // Generate schema
822
+ const schema = await ControllerWrapper.generateSchema();
390
823
  ```
391
824
 
392
- ### Helper Utilities
825
+ ### ProcessRequest Actions Summary
393
826
 
394
- ```javascript
395
- const { helperUtility } = require('korm-js');
827
+ | Action | Description | Required Fields |
828
+ |--------|-------------|----------------|
829
+ | `create` | Create new record | `data` |
830
+ | `list` | Get multiple records | None (optional: `where`, `select`, `sort`, `limit`, `offset`) |
831
+ | `show` | Get single record | `where` |
832
+ | `update` | Update record(s) | `where`, `data` |
833
+ | `delete` | Delete record(s) | `where` |
834
+ | `count` | Count records | None (optional: `where`) |
835
+ | `replace` | Replace record (MySQL) | `data` |
836
+ | `upsert` | Insert or update | `data`, `conflict` |
837
+ | `sync` | Upsert + delete | `data`, `conflict`, `where` |
396
838
 
397
- // File operations
398
- helperUtility.file.readJSON('path/to/file.json');
399
- helperUtility.file.writeJSON('path/to/file.json', data);
400
- helperUtility.file.createDirectory('path/to/directory');
839
+ ### Validation Rules
401
840
 
402
- // Other utilities available through helperUtility
403
- ```
841
+ | Rule | Description | Example |
842
+ |------|-------------|---------|
843
+ | `required` | Field is required | `username: 'required'` |
844
+ | `type:string` | Field must be string | `name: 'type:string'` |
845
+ | `type:number` | Field must be number | `age: 'type:number'` |
846
+ | `type:boolean` | Field must be boolean | `is_active: 'type:boolean'` |
847
+ | `minLen:n` | Minimum length | `username: 'minLen:3'` |
848
+ | `maxLen:n` | Maximum length | `email: 'maxLen:255'` |
849
+ | `min:n` | Minimum value | `age: 'min:0'` |
850
+ | `max:n` | Maximum value | `age: 'max:150'` |
851
+ | `in:val1,val2` | Value must be in list | `status: 'in:active,inactive'` |
852
+ | `regex:name` | Custom regex pattern | `email: 'regex:email'` |
853
+ | `match:field` | Must match another field | `confirm_password: 'match:password'` |
854
+ | `exists:table,column` | Value must exist in table | `user_id: 'exists:users,id'` |
404
855
 
405
856
  ## Database Support
406
857
 
407
- ### PostgreSQL (Recommended)
858
+ ### MySQL (Current Guide)
859
+
408
860
  ```javascript
409
861
  const knex = require('knex');
410
862
 
411
863
  const db = knex({
412
- client: 'pg',
864
+ client: 'mysql2',
413
865
  connection: {
414
866
  host: process.env.DB_HOST || 'localhost',
415
- user: process.env.DB_USER || 'username',
867
+ user: process.env.DB_USER || 'root',
416
868
  password: process.env.DB_PASS || 'password',
417
869
  database: process.env.DB_NAME || 'database_name',
418
- port: process.env.DB_PORT || 5432
870
+ port: process.env.DB_PORT || 3306
419
871
  }
420
872
  });
421
873
 
422
874
  const korm = initializeKORM({
423
875
  db: db,
424
- dbClient: 'pg'
876
+ dbClient: 'mysql'
425
877
  });
426
878
  ```
427
879
 
428
- ### MySQL
880
+ ### PostgreSQL
881
+
429
882
  ```javascript
430
883
  const knex = require('knex');
431
884
 
432
885
  const db = knex({
433
- client: 'mysql2',
886
+ client: 'pg',
434
887
  connection: {
435
888
  host: process.env.DB_HOST || 'localhost',
436
889
  user: process.env.DB_USER || 'username',
437
890
  password: process.env.DB_PASS || 'password',
438
891
  database: process.env.DB_NAME || 'database_name',
439
- port: process.env.DB_PORT || 3306
892
+ port: process.env.DB_PORT || 5432
440
893
  }
441
894
  });
442
895
 
443
896
  const korm = initializeKORM({
444
897
  db: db,
445
- dbClient: 'mysql'
898
+ dbClient: 'pg'
446
899
  });
447
900
  ```
448
901
 
449
902
  ### SQLite
903
+
450
904
  ```javascript
451
905
  const knex = require('knex');
452
906
 
@@ -466,15 +920,123 @@ const korm = initializeKORM({
466
920
  ## Error Handling
467
921
 
468
922
  ```javascript
923
+ // Global error handler
469
924
  app.use((error, req, res, next) => {
470
925
  console.error('KORM Error:', error);
471
926
 
472
- res.status(500).json({
927
+ res.status(error.status || 500).json({
473
928
  success: false,
474
929
  message: 'Internal server error',
475
- error: process.env.NODE_ENV === 'development' ? error.message : 'Something went wrong'
930
+ error: process.env.NODE_ENV === 'development' ? error.message : 'Something went wrong',
931
+ stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
476
932
  });
477
933
  });
934
+
935
+ // Route-specific error handling
936
+ app.post('/api/:model/crud', async (req, res) => {
937
+ try {
938
+ const { model } = req.params;
939
+ const result = await korm.processRequest(req.body, model);
940
+ res.json(result);
941
+ } catch (error) {
942
+ // Handle validation errors
943
+ if (error.name === 'ValidationError') {
944
+ return res.status(400).json({
945
+ success: false,
946
+ message: 'Validation failed',
947
+ errors: error.message
948
+ });
949
+ }
950
+
951
+ // Handle not found errors
952
+ if (error.message.includes('not found')) {
953
+ return res.status(404).json({
954
+ success: false,
955
+ message: error.message
956
+ });
957
+ }
958
+
959
+ // Handle other errors
960
+ res.status(400).json({
961
+ success: false,
962
+ message: error.message
963
+ });
964
+ }
965
+ });
966
+ ```
967
+
968
+ ## Complete Example Application
969
+
970
+ ```javascript
971
+ require('dotenv').config();
972
+ const express = require('express');
973
+ const { initializeKORM, validate, helperUtility } = require('@dreamtree-org/korm-js');
974
+ const knex = require('knex');
975
+
976
+ const app = express();
977
+ app.use(express.json());
978
+
979
+ // MySQL configuration
980
+ const db = knex({
981
+ client: 'mysql2',
982
+ connection: {
983
+ host: process.env.DB_HOST || 'localhost',
984
+ user: process.env.DB_USER || 'root',
985
+ password: process.env.DB_PASS || 'password',
986
+ database: process.env.DB_NAME || 'my_database',
987
+ port: process.env.DB_PORT || 3306
988
+ }
989
+ });
990
+
991
+ // Initialize KORM
992
+ const korm = initializeKORM({
993
+ db: db,
994
+ dbClient: 'mysql'
995
+ });
996
+
997
+ // Initialize app
998
+ async function initApp() {
999
+ try {
1000
+ // Load or generate schema
1001
+ let schema = helperUtility.file.readJSON('schema/schema.json');
1002
+ if (schema) {
1003
+ korm.setSchema(schema);
1004
+ } else {
1005
+ schema = await korm.generateSchema();
1006
+ helperUtility.file.createDirectory('schema');
1007
+ helperUtility.file.writeJSON('schema/schema.json', schema);
1008
+ }
1009
+
1010
+ // Sync database
1011
+ await korm.syncDatabase();
1012
+ console.log('✅ Database synced');
1013
+ } catch (error) {
1014
+ console.error('❌ Initialization error:', error);
1015
+ }
1016
+ }
1017
+
1018
+ // Generic CRUD endpoint
1019
+ app.post('/api/:model/crud', async (req, res) => {
1020
+ try {
1021
+ const { model } = req.params;
1022
+ const result = await korm.processRequest(req.body, model);
1023
+ res.json(result);
1024
+ } catch (error) {
1025
+ res.status(400).json({ error: error.message });
1026
+ }
1027
+ });
1028
+
1029
+ // Health check
1030
+ app.get('/health', (req, res) => {
1031
+ res.send('OK');
1032
+ });
1033
+
1034
+ // Start server
1035
+ const PORT = process.env.PORT || 3000;
1036
+ app.listen(PORT, async () => {
1037
+ console.log(`🚀 Server running on http://localhost:${PORT}`);
1038
+ await initApp();
1039
+ });
478
1040
  ```
479
1041
 
480
1042
  ## Contributing
@@ -491,7 +1053,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
491
1053
 
492
1054
  ## Support
493
1055
 
494
- If you have any questions or need help, please open an issue on GitHub or contact us at [partha.preetham.krishna@gmail.com](mailto:partha.preetham.krishna@gmail.com).
1056
+ If you have any questions or need help, please open an issue on GitHub or contact us at [partha.preetham.krishna@gmail.com](mailto:partha-preetham.krishna@gmail.com).
495
1057
 
496
1058
  ---
497
1059
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreamtree-org/korm-js",
3
- "version": "1.0.39",
3
+ "version": "1.0.40",
4
4
  "description": "Knowledge Object-Relational Mapping - A powerful, modular ORM system for Node.js with dynamic database operations, complex queries, relationships, and nested requests",
5
5
  "author": {
6
6
  "name": "Partha Preetham Krishna",