@dreamtree-org/korm-js 1.0.53 → 1.0.54
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/BaseHelperUtility.js +1 -1
- package/ControllerWrapper.js +1 -1
- package/Emitter.js +1 -1
- package/README.md +263 -226
- package/build.js +1 -1
- package/cli.js +2 -0
- package/clients/mysql/BaseUtility.js +1 -1
- package/clients/mysql/CurdTable.js +1 -1
- package/clients/mysql/DataTypeMap.js +1 -1
- package/clients/mysql/HookService.js +1 -1
- package/clients/mysql/QueryBuilder.js +1 -1
- package/clients/mysql/QueryService.js +1 -1
- package/clients/mysql/SyncTable.js +1 -1
- package/clients/pg/BaseUtility.js +1 -1
- package/clients/pg/CurdTable.js +1 -1
- package/clients/pg/HookService.js +1 -1
- package/clients/pg/QueryBuilder.js +1 -1
- package/clients/pg/QueryService.js +1 -1
- package/clients/pg/SyncTable.js +1 -1
- package/clients/sqlite/BaseUtility.js +1 -1
- package/clients/sqlite/CurdTable.js +1 -1
- package/clients/sqlite/HookService.js +1 -1
- package/clients/sqlite/QueryBuilder.js +1 -1
- package/clients/sqlite/QueryService.js +1 -1
- package/clients/sqlite/SyncTable.js +1 -1
- package/helpers/files.js +1 -1
- package/jest.config.js +1 -1
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -29,6 +29,35 @@
|
|
|
29
29
|
npm install @dreamtree-org/korm-js
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
## AI assistant skills (`init --ai`)
|
|
33
|
+
|
|
34
|
+
KORM-JS ships a canonical reference doc designed for AI coding assistants. After installing the package (or via `npx` without installing), drop the skill into the location your assistant picks up:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx @dreamtree-org/korm-js init --ai claude
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Supported providers:
|
|
41
|
+
|
|
42
|
+
| `--ai` value | Target file |
|
|
43
|
+
| ----------------------------------- | ----------------------------------------------------------- |
|
|
44
|
+
| `claude` | `CLAUDE.md` (idempotent block insert) |
|
|
45
|
+
| `openai` (alias: `codex`) | `AGENTS.md` (idempotent block insert) |
|
|
46
|
+
| `gemini` | `GEMINI.md` (idempotent block insert) |
|
|
47
|
+
| `copilot` (alias: `github-copilot`) | `.github/copilot-instructions.md` (idempotent block insert) |
|
|
48
|
+
| `kiro` | `.kiro/steering/korm-js.md` |
|
|
49
|
+
| `windsurf` | `.windsurf/rules/korm-js.md` |
|
|
50
|
+
| `cursor` | `.cursor/rules/korm-js.mdc` (with MDC frontmatter) |
|
|
51
|
+
| `all` | Installs every provider above |
|
|
52
|
+
|
|
53
|
+
Flags:
|
|
54
|
+
|
|
55
|
+
- `--cwd <dir>` — install into a directory other than the current working directory.
|
|
56
|
+
- `--force` — overwrite dedicated-file targets (Cursor / Windsurf / Kiro) that already exist.
|
|
57
|
+
- `--dry-run` — print what would change without writing.
|
|
58
|
+
|
|
59
|
+
Block-insert targets (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `.github/copilot-instructions.md`) wrap the skill in `<!-- BEGIN korm-js skill --> ... <!-- END korm-js skill -->` markers. Re-running the command after a library upgrade updates the block in place without touching the rest of the file.
|
|
60
|
+
|
|
32
61
|
## Quick Start with Express.js and MySQL
|
|
33
62
|
|
|
34
63
|
### 1. Basic Setup
|
|
@@ -50,15 +79,15 @@ const db = knex({
|
|
|
50
79
|
user: process.env.DB_USER || 'root',
|
|
51
80
|
password: process.env.DB_PASS || 'password',
|
|
52
81
|
database: process.env.DB_NAME || 'my_database',
|
|
53
|
-
port: process.env.DB_PORT || 3306
|
|
54
|
-
}
|
|
82
|
+
port: process.env.DB_PORT || 3306,
|
|
83
|
+
},
|
|
55
84
|
});
|
|
56
85
|
|
|
57
86
|
// Initialize KORM with MySQL
|
|
58
87
|
const korm = initializeKORM({
|
|
59
88
|
db: db,
|
|
60
89
|
dbClient: 'mysql',
|
|
61
|
-
debug: false
|
|
90
|
+
debug: false, // Set to true for SQL debugging
|
|
62
91
|
});
|
|
63
92
|
|
|
64
93
|
app.listen(3000, () => {
|
|
@@ -75,14 +104,14 @@ async function initApp() {
|
|
|
75
104
|
if (syncSchema) {
|
|
76
105
|
korm.setSchema(syncSchema);
|
|
77
106
|
}
|
|
78
|
-
|
|
107
|
+
|
|
79
108
|
// Sync database with schema (creates tables if they don't exist)
|
|
80
109
|
await korm.syncDatabase().then(() => {
|
|
81
110
|
console.log('✅ Database synced');
|
|
82
111
|
});
|
|
83
|
-
|
|
112
|
+
|
|
84
113
|
// Generate schema from existing database
|
|
85
|
-
await korm.generateSchema().then(schema => {
|
|
114
|
+
await korm.generateSchema().then((schema) => {
|
|
86
115
|
korm.setSchema(schema); // Set the schema to the korm instance
|
|
87
116
|
helperUtility.file.createDirectory('schema');
|
|
88
117
|
helperUtility.file.writeJSON('schema/schema.json', schema);
|
|
@@ -367,6 +396,7 @@ POST /api/Employee/crud
|
|
|
367
396
|
```
|
|
368
397
|
|
|
369
398
|
**Notes:**
|
|
399
|
+
|
|
370
400
|
- Use **either** `sumColumn` or `sumFormula`, not both. If both are provided, `sumColumn` is used.
|
|
371
401
|
- In `sumFormula`, column names are written as `{columnName}` and are safely quoted; only letters, numbers, and underscore are allowed in names.
|
|
372
402
|
- **Expression guards:** `sumFormula` allows only BODMAS-safe content: numbers (including decimals), operators `+` `-` `*` `/`, parentheses `(` `)`, and `{columnName}` placeholders. Any other character (e.g. commas, quotes, SQL) is rejected. Parentheses must be balanced and correctly ordered.
|
|
@@ -599,21 +629,21 @@ POST /api/Users/crud
|
|
|
599
629
|
|
|
600
630
|
### Where Operators Reference
|
|
601
631
|
|
|
602
|
-
| Operator
|
|
603
|
-
|
|
604
|
-
| `=` (default) | Equals
|
|
605
|
-
| `>=`
|
|
606
|
-
| `<=`
|
|
607
|
-
| `>`
|
|
608
|
-
| `<`
|
|
609
|
-
| `!=`
|
|
610
|
-
| `like`
|
|
611
|
-
| `in`
|
|
612
|
-
| `notIn`
|
|
613
|
-
| `between`
|
|
614
|
-
| `notBetween`
|
|
615
|
-
| `null`
|
|
616
|
-
| `Or:column`
|
|
632
|
+
| Operator | Description | Syntax | Example |
|
|
633
|
+
| ------------- | ----------------------- | -------------- | ------------------------------- |
|
|
634
|
+
| `=` (default) | Equals | `value` | `"status": "active"` |
|
|
635
|
+
| `>=` | Greater than or equal | `>=value` | `"age": ">=18"` |
|
|
636
|
+
| `<=` | Less than or equal | `<=value` | `"age": "<=65"` |
|
|
637
|
+
| `>` | Greater than | `>value` | `"price": ">100"` |
|
|
638
|
+
| `<` | Less than | `<value` | `"price": "<500"` |
|
|
639
|
+
| `!=` | Not equal | `!value` | `"status": "!deleted"` |
|
|
640
|
+
| `like` | Pattern matching (auto) | `%value%` | `"name": "%john%"` |
|
|
641
|
+
| `in` | Value in list | `[]val1,val2` | `"status": "[]active,pending"` |
|
|
642
|
+
| `notIn` | Value not in list | `![]val1,val2` | `"role": "![]banned,suspended"` |
|
|
643
|
+
| `between` | Range (inclusive) | `><min,max` | `"age": "><18,65"` |
|
|
644
|
+
| `notBetween` | Outside range | `<>min,max` | `"score": "<>0,50"` |
|
|
645
|
+
| `null` | IS NULL check | `null` | `"deleted_at": null` |
|
|
646
|
+
| `Or:column` | OR condition prefix | `Or:column` | `"Or:name": "John"` |
|
|
617
647
|
|
|
618
648
|
**Examples:**
|
|
619
649
|
|
|
@@ -630,7 +660,7 @@ POST /api/Users/crud
|
|
|
630
660
|
"score": "><50,100"
|
|
631
661
|
}
|
|
632
662
|
}
|
|
633
|
-
// SQL: WHERE age >= 18 AND status != 'deleted' AND name LIKE '%john%'
|
|
663
|
+
// SQL: WHERE age >= 18 AND status != 'deleted' AND name LIKE '%john%'
|
|
634
664
|
// AND role IN ('admin', 'moderator', 'editor') AND score BETWEEN 50 AND 100
|
|
635
665
|
```
|
|
636
666
|
|
|
@@ -721,6 +751,7 @@ POST /api/Users/crud
|
|
|
721
751
|
```
|
|
722
752
|
|
|
723
753
|
**Available join types:**
|
|
754
|
+
|
|
724
755
|
- `join` - Regular join
|
|
725
756
|
- `innerJoin` - Inner join
|
|
726
757
|
- `leftJoin` - Left join
|
|
@@ -795,6 +826,7 @@ For many-to-many relationships, use the `through` key to specify the join/pivot
|
|
|
795
826
|
```
|
|
796
827
|
|
|
797
828
|
**Many-to-Many Structure:**
|
|
829
|
+
|
|
798
830
|
- `type`: `"many"` (always use "many" for many-to-many)
|
|
799
831
|
- `table`: The related table name (e.g., `"roles"`)
|
|
800
832
|
- `localKey`: The primary key in the current model (e.g., `"id"`)
|
|
@@ -865,7 +897,7 @@ POST /api/Post/crud
|
|
|
865
897
|
},
|
|
866
898
|
"with": ["User", "User.UserRole", "User.UserRole.Role"]
|
|
867
899
|
}
|
|
868
|
-
// SQL: WHERE EXISTS (SELECT * FROM users WHERE users.id = posts.user_id
|
|
900
|
+
// SQL: WHERE EXISTS (SELECT * FROM users WHERE users.id = posts.user_id
|
|
869
901
|
// AND EXISTS (SELECT * FROM user_roles WHERE user_roles.user_id = users.id
|
|
870
902
|
// AND EXISTS (SELECT * FROM roles WHERE roles.id = user_roles.role_id AND name = 'admin')))
|
|
871
903
|
```
|
|
@@ -884,7 +916,7 @@ POST /api/Post/crud
|
|
|
884
916
|
},
|
|
885
917
|
"with": ["User"]
|
|
886
918
|
}
|
|
887
|
-
// SQL: WHERE EXISTS (SELECT * FROM users WHERE users.id = posts.user_id
|
|
919
|
+
// SQL: WHERE EXISTS (SELECT * FROM users WHERE users.id = posts.user_id
|
|
888
920
|
// AND NOT EXISTS (SELECT * FROM user_roles WHERE user_roles.user_id = users.id))
|
|
889
921
|
|
|
890
922
|
// Get users with no posts
|
|
@@ -912,12 +944,13 @@ POST /api/Post/crud
|
|
|
912
944
|
|
|
913
945
|
**NOT EXISTS Syntax Summary:**
|
|
914
946
|
|
|
915
|
-
| Pattern
|
|
916
|
-
|
|
917
|
-
| `"!RelName": true`
|
|
918
|
-
| `"Parent.!Child": true`
|
|
919
|
-
| `"Parent.!Child.column": value` | NOT EXISTS with condition
|
|
920
|
-
|
|
947
|
+
| Pattern | Meaning | Use Case |
|
|
948
|
+
| ------------------------------- | ----------------------------- | ---------------------------------- |
|
|
949
|
+
| `"!RelName": true` | NOT EXISTS on direct relation | Users with no posts |
|
|
950
|
+
| `"Parent.!Child": true` | NOT EXISTS on nested relation | Posts where user has no roles |
|
|
951
|
+
| `"Parent.!Child.column": value` | NOT EXISTS with condition | Posts where user has no admin role |
|
|
952
|
+
|
|
953
|
+
````
|
|
921
954
|
|
|
922
955
|
#### Filtering Eager Loaded Relations with `withWhere`
|
|
923
956
|
|
|
@@ -979,15 +1012,15 @@ POST /api/User/crud
|
|
|
979
1012
|
"Or:Post.is_featured": true
|
|
980
1013
|
}
|
|
981
1014
|
}
|
|
982
|
-
|
|
1015
|
+
````
|
|
983
1016
|
|
|
984
1017
|
**Key differences between `where` and `withWhere`:**
|
|
985
1018
|
|
|
986
|
-
| Feature
|
|
987
|
-
|
|
988
|
-
| Affects parent query | Yes (filters parent rows)
|
|
989
|
-
| Purpose
|
|
990
|
-
| SQL generated
|
|
1019
|
+
| Feature | `where` (Nested) | `withWhere` |
|
|
1020
|
+
| -------------------- | ------------------------------------------- | ------------------------------------ |
|
|
1021
|
+
| Affects parent query | Yes (filters parent rows) | No (only filters related rows) |
|
|
1022
|
+
| Purpose | Filter parents that have matching relations | Filter which related rows are loaded |
|
|
1023
|
+
| SQL generated | `WHERE EXISTS (subquery)` | Additional `WHERE` on relation query |
|
|
991
1024
|
|
|
992
1025
|
**Example showing the difference:**
|
|
993
1026
|
|
|
@@ -1012,6 +1045,7 @@ POST /api/User/crud
|
|
|
1012
1045
|
```
|
|
1013
1046
|
|
|
1014
1047
|
**withWhere supports all operators:**
|
|
1048
|
+
|
|
1015
1049
|
- Comparison: `">=18"`, `"<=100"`, `">50"`, `"<10"`, `"!deleted"`
|
|
1016
1050
|
- LIKE: `"%pattern%"`
|
|
1017
1051
|
- IN: `"[]val1,val2,val3"`
|
|
@@ -1085,6 +1119,7 @@ Example complete relationship structure:
|
|
|
1085
1119
|
### Custom Relation Hooks
|
|
1086
1120
|
|
|
1087
1121
|
For complex relationships that can't be defined in the schema, you can create custom relation hooks in your model class. This is useful for:
|
|
1122
|
+
|
|
1088
1123
|
- Virtual/computed relations
|
|
1089
1124
|
- Cross-database relations
|
|
1090
1125
|
- Complex aggregations
|
|
@@ -1100,35 +1135,33 @@ class Users {
|
|
|
1100
1135
|
// rows = parent rows to attach relation data to
|
|
1101
1136
|
// db = Knex database instance
|
|
1102
1137
|
// qb = QueryBuilder instance
|
|
1103
|
-
|
|
1138
|
+
|
|
1104
1139
|
for (const row of rows) {
|
|
1105
1140
|
// Fetch custom data for each row
|
|
1106
|
-
const stats = await db('user_statistics')
|
|
1107
|
-
|
|
1108
|
-
.first();
|
|
1109
|
-
|
|
1141
|
+
const stats = await db('user_statistics').where('user_id', row.id).first();
|
|
1142
|
+
|
|
1110
1143
|
// Attach to row
|
|
1111
1144
|
row.Statistics = stats || { posts: 0, comments: 0, likes: 0 };
|
|
1112
1145
|
}
|
|
1113
1146
|
}
|
|
1114
|
-
|
|
1147
|
+
|
|
1115
1148
|
// Custom relation with aggregation
|
|
1116
1149
|
async getPostCountRelation({ rows, db }) {
|
|
1117
|
-
const userIds = rows.map(r => r.id);
|
|
1118
|
-
|
|
1150
|
+
const userIds = rows.map((r) => r.id);
|
|
1151
|
+
|
|
1119
1152
|
const counts = await db('posts')
|
|
1120
1153
|
.select('user_id')
|
|
1121
1154
|
.count('* as count')
|
|
1122
1155
|
.whereIn('user_id', userIds)
|
|
1123
1156
|
.groupBy('user_id');
|
|
1124
|
-
|
|
1125
|
-
const countMap = new Map(counts.map(c => [c.user_id, c.count]));
|
|
1126
|
-
|
|
1157
|
+
|
|
1158
|
+
const countMap = new Map(counts.map((c) => [c.user_id, c.count]));
|
|
1159
|
+
|
|
1127
1160
|
for (const row of rows) {
|
|
1128
1161
|
row.PostCount = countMap.get(row.id) || 0;
|
|
1129
1162
|
}
|
|
1130
1163
|
}
|
|
1131
|
-
|
|
1164
|
+
|
|
1132
1165
|
// Custom relation from external API or different database
|
|
1133
1166
|
async getExternalProfileRelation({ rows }) {
|
|
1134
1167
|
for (const row of rows) {
|
|
@@ -1155,16 +1188,16 @@ POST /api/Users/crud
|
|
|
1155
1188
|
|
|
1156
1189
|
**Custom Relation Hook Arguments:**
|
|
1157
1190
|
|
|
1158
|
-
| Argument
|
|
1159
|
-
|
|
1160
|
-
| `rows`
|
|
1161
|
-
| `relName`
|
|
1162
|
-
| `model`
|
|
1163
|
-
| `withTree`
|
|
1164
|
-
| `controller` | ControllerWrapper instance
|
|
1165
|
-
| `relation`
|
|
1166
|
-
| `qb`
|
|
1167
|
-
| `db`
|
|
1191
|
+
| Argument | Description |
|
|
1192
|
+
| ------------ | ----------------------------------------------------------------------- |
|
|
1193
|
+
| `rows` | Parent rows to attach relation data to (modify in place) |
|
|
1194
|
+
| `relName` | The relation name being fetched |
|
|
1195
|
+
| `model` | Model definition object |
|
|
1196
|
+
| `withTree` | Nested relations tree for further loading |
|
|
1197
|
+
| `controller` | ControllerWrapper instance |
|
|
1198
|
+
| `relation` | Relation definition from schema (may be undefined for custom relations) |
|
|
1199
|
+
| `qb` | QueryBuilder instance for building queries |
|
|
1200
|
+
| `db` | Knex database instance for direct queries |
|
|
1168
1201
|
|
|
1169
1202
|
### Important Notes
|
|
1170
1203
|
|
|
@@ -1187,13 +1220,13 @@ Create a model file at `models/Users.model.js`:
|
|
|
1187
1220
|
class Users {
|
|
1188
1221
|
// Enable soft delete for this model
|
|
1189
1222
|
hasSoftDelete = true;
|
|
1190
|
-
|
|
1223
|
+
|
|
1191
1224
|
// Optional: Custom soft delete hook
|
|
1192
1225
|
beforeDelete({ model, action, request, context, db, utils, controller }) {
|
|
1193
1226
|
// Custom logic before soft delete
|
|
1194
1227
|
console.log('Soft deleting user:', request.where);
|
|
1195
1228
|
}
|
|
1196
|
-
|
|
1229
|
+
|
|
1197
1230
|
// Optional: Custom hook after soft delete
|
|
1198
1231
|
afterDelete({ model, action, data, request, context, db, utils, controller }) {
|
|
1199
1232
|
// Custom logic after soft delete
|
|
@@ -1244,15 +1277,13 @@ class Users {
|
|
|
1244
1277
|
throw new Error('Email is required');
|
|
1245
1278
|
}
|
|
1246
1279
|
// Check if email already exists
|
|
1247
|
-
const existing = await db('users')
|
|
1248
|
-
.where('email', request.data.email)
|
|
1249
|
-
.first();
|
|
1280
|
+
const existing = await db('users').where('email', request.data.email).first();
|
|
1250
1281
|
if (existing && existing.id !== request.where?.id) {
|
|
1251
1282
|
throw new Error('Email already exists');
|
|
1252
1283
|
}
|
|
1253
1284
|
}
|
|
1254
1285
|
}
|
|
1255
|
-
|
|
1286
|
+
|
|
1256
1287
|
// Before hooks - run before the action executes
|
|
1257
1288
|
// Method naming: before{Action} (e.g., beforeCreate, beforeUpdate, beforeList, beforeDelete)
|
|
1258
1289
|
async beforeCreate({ model, action, request, context, db, utils, controller }) {
|
|
@@ -1261,18 +1292,18 @@ class Users {
|
|
|
1261
1292
|
request.data.updated_at = new Date();
|
|
1262
1293
|
return request.data;
|
|
1263
1294
|
}
|
|
1264
|
-
|
|
1295
|
+
|
|
1265
1296
|
async beforeUpdate({ model, action, request, context, db, utils, controller }) {
|
|
1266
1297
|
// Modify request data before update
|
|
1267
1298
|
request.data.updated_at = new Date();
|
|
1268
1299
|
return request.data;
|
|
1269
1300
|
}
|
|
1270
|
-
|
|
1301
|
+
|
|
1271
1302
|
async beforeDelete({ model, action, request, context, db, utils, controller }) {
|
|
1272
1303
|
// Logic before delete (works with both hard and soft delete)
|
|
1273
1304
|
console.log('Deleting user:', request.where);
|
|
1274
1305
|
}
|
|
1275
|
-
|
|
1306
|
+
|
|
1276
1307
|
// After hooks - run after the action executes
|
|
1277
1308
|
// Method naming: after{Action} (e.g., afterCreate, afterUpdate, afterList, afterDelete)
|
|
1278
1309
|
async afterCreate({ model, action, data, request, context, db, utils, controller }) {
|
|
@@ -1281,17 +1312,17 @@ class Users {
|
|
|
1281
1312
|
// Send welcome email, trigger notifications, etc.
|
|
1282
1313
|
return data;
|
|
1283
1314
|
}
|
|
1284
|
-
|
|
1315
|
+
|
|
1285
1316
|
async afterUpdate({ model, action, data, request, context, db, utils, controller }) {
|
|
1286
1317
|
console.log('User updated:', data);
|
|
1287
1318
|
return data;
|
|
1288
1319
|
}
|
|
1289
|
-
|
|
1320
|
+
|
|
1290
1321
|
async afterList({ model, action, data, request, context, db, utils, controller }) {
|
|
1291
1322
|
// Modify list results before returning
|
|
1292
1323
|
return data;
|
|
1293
1324
|
}
|
|
1294
|
-
|
|
1325
|
+
|
|
1295
1326
|
// Custom action hooks
|
|
1296
1327
|
// Method naming: on{Action}Action (e.g., onActivateAction, onDeactivateAction)
|
|
1297
1328
|
async onActivateAction({ model, action, request, context, db, utils, controller }) {
|
|
@@ -1299,7 +1330,7 @@ class Users {
|
|
|
1299
1330
|
.where(request.where)
|
|
1300
1331
|
.update({ is_active: true, updated_at: new Date() });
|
|
1301
1332
|
}
|
|
1302
|
-
|
|
1333
|
+
|
|
1303
1334
|
async onDeactivateAction({ model, action, request, context, db, utils, controller }) {
|
|
1304
1335
|
return await db('users')
|
|
1305
1336
|
.where(request.where)
|
|
@@ -1312,29 +1343,30 @@ module.exports = Users;
|
|
|
1312
1343
|
|
|
1313
1344
|
### Hook Arguments Reference
|
|
1314
1345
|
|
|
1315
|
-
| Argument
|
|
1316
|
-
|
|
1317
|
-
| `model`
|
|
1318
|
-
| `action`
|
|
1319
|
-
| `request`
|
|
1320
|
-
| `context`
|
|
1321
|
-
| `db`
|
|
1322
|
-
| `utils`
|
|
1323
|
-
| `controller` | ControllerWrapper instance
|
|
1324
|
-
| `data`
|
|
1346
|
+
| Argument | Description |
|
|
1347
|
+
| ------------ | ------------------------------------------------------------- |
|
|
1348
|
+
| `model` | Model definition object with table, columns, relations |
|
|
1349
|
+
| `action` | Current action being performed (create, update, delete, etc.) |
|
|
1350
|
+
| `request` | The request object containing where, data, etc. |
|
|
1351
|
+
| `context` | Custom context passed from the controller |
|
|
1352
|
+
| `db` | Knex database instance for direct queries |
|
|
1353
|
+
| `utils` | Utility functions |
|
|
1354
|
+
| `controller` | ControllerWrapper instance |
|
|
1355
|
+
| `data` | (After hooks only) Result of the action |
|
|
1325
1356
|
|
|
1326
1357
|
### Complete Hook Types Reference
|
|
1327
1358
|
|
|
1328
|
-
| Hook Type
|
|
1329
|
-
|
|
1330
|
-
| Validate
|
|
1331
|
-
| Before
|
|
1332
|
-
| After
|
|
1333
|
-
| Custom Action
|
|
1334
|
-
| Soft Delete
|
|
1335
|
-
| Custom Relation | `get{RelName}Relation` | During eager loading
|
|
1359
|
+
| Hook Type | Method Naming | When Called | Use Case |
|
|
1360
|
+
| --------------- | ---------------------- | ---------------------- | --------------------------------------- |
|
|
1361
|
+
| Validate | `validate` | Before any action | Input validation, authorization |
|
|
1362
|
+
| Before | `before{Action}` | Before action executes | Modify request data, add timestamps |
|
|
1363
|
+
| After | `after{Action}` | After action executes | Transform results, trigger side effects |
|
|
1364
|
+
| Custom Action | `on{Action}Action` | For custom actions | Implement business logic |
|
|
1365
|
+
| Soft Delete | `hasSoftDelete = true` | During delete/list | Enable soft delete |
|
|
1366
|
+
| Custom Relation | `get{RelName}Relation` | During eager loading | Custom data fetching |
|
|
1336
1367
|
|
|
1337
1368
|
**Available Before/After hooks:**
|
|
1369
|
+
|
|
1338
1370
|
- `beforeCreate` / `afterCreate`
|
|
1339
1371
|
- `beforeUpdate` / `afterUpdate`
|
|
1340
1372
|
- `beforeDelete` / `afterDelete`
|
|
@@ -1357,7 +1389,7 @@ POST /api/Users/crud
|
|
|
1357
1389
|
|
|
1358
1390
|
POST /api/Users/crud
|
|
1359
1391
|
{
|
|
1360
|
-
"action": "deactivate",
|
|
1392
|
+
"action": "deactivate",
|
|
1361
1393
|
"where": { "id": 1 }
|
|
1362
1394
|
}
|
|
1363
1395
|
|
|
@@ -1384,7 +1416,7 @@ const userValidationRules = {
|
|
|
1384
1416
|
first_name: 'required|type:string|maxLen:100',
|
|
1385
1417
|
last_name: 'required|type:string|maxLen:100',
|
|
1386
1418
|
age: 'type:number|min:0|max:150',
|
|
1387
|
-
is_active: 'type:boolean'
|
|
1419
|
+
is_active: 'type:boolean',
|
|
1388
1420
|
};
|
|
1389
1421
|
|
|
1390
1422
|
// Validate and create user
|
|
@@ -1392,30 +1424,33 @@ app.post('/api/users/validate', async (req, res) => {
|
|
|
1392
1424
|
try {
|
|
1393
1425
|
// Validate request data
|
|
1394
1426
|
const validatedData = await validate(req.body, userValidationRules);
|
|
1395
|
-
|
|
1396
|
-
const result = await korm.processRequest(
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1427
|
+
|
|
1428
|
+
const result = await korm.processRequest(
|
|
1429
|
+
{
|
|
1430
|
+
action: 'create',
|
|
1431
|
+
data: validatedData,
|
|
1432
|
+
},
|
|
1433
|
+
'Users'
|
|
1434
|
+
);
|
|
1435
|
+
|
|
1401
1436
|
res.status(201).json({
|
|
1402
1437
|
success: true,
|
|
1403
1438
|
message: 'User created successfully',
|
|
1404
|
-
data: result
|
|
1439
|
+
data: result,
|
|
1405
1440
|
});
|
|
1406
1441
|
} catch (error) {
|
|
1407
1442
|
if (error.name === 'ValidationError') {
|
|
1408
1443
|
return res.status(400).json({
|
|
1409
1444
|
success: false,
|
|
1410
1445
|
message: 'Validation failed',
|
|
1411
|
-
errors: error.message
|
|
1446
|
+
errors: error.message,
|
|
1412
1447
|
});
|
|
1413
1448
|
}
|
|
1414
|
-
|
|
1449
|
+
|
|
1415
1450
|
res.status(500).json({
|
|
1416
1451
|
success: false,
|
|
1417
1452
|
message: 'Error creating user',
|
|
1418
|
-
error: error.message
|
|
1453
|
+
error: error.message,
|
|
1419
1454
|
});
|
|
1420
1455
|
}
|
|
1421
1456
|
});
|
|
@@ -1433,13 +1468,13 @@ const advancedRules = {
|
|
|
1433
1468
|
phone: 'regex:phone',
|
|
1434
1469
|
password: 'required|type:string|minLen:8',
|
|
1435
1470
|
status: 'in:active,inactive,pending',
|
|
1436
|
-
user_id: 'exists:users,id'
|
|
1471
|
+
user_id: 'exists:users,id',
|
|
1437
1472
|
};
|
|
1438
1473
|
|
|
1439
1474
|
const customRegex = {
|
|
1440
1475
|
username: /^[a-zA-Z0-9_]+$/,
|
|
1441
1476
|
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
1442
|
-
phone: /^\+?[\d\s-()]{10,15}
|
|
1477
|
+
phone: /^\+?[\d\s-()]{10,15}$/,
|
|
1443
1478
|
};
|
|
1444
1479
|
|
|
1445
1480
|
const validatedData = await validate(data, advancedRules, { customRegex });
|
|
@@ -1530,20 +1565,21 @@ type|modifier1|modifier2|...
|
|
|
1530
1565
|
|
|
1531
1566
|
**Column Modifiers:**
|
|
1532
1567
|
|
|
1533
|
-
| Modifier
|
|
1534
|
-
|
|
1535
|
-
| `size:n`
|
|
1536
|
-
| `unsigned`
|
|
1537
|
-
| `primaryKey`
|
|
1538
|
-
| `autoIncrement`
|
|
1539
|
-
| `notNull`
|
|
1540
|
-
| `unique`
|
|
1541
|
-
| `default:value`
|
|
1542
|
-
| `onUpdate:value`
|
|
1543
|
-
| `comment:text`
|
|
1544
|
-
| `foreignKey:table:column` | Foreign key
|
|
1568
|
+
| Modifier | Description | Example |
|
|
1569
|
+
| ------------------------- | ------------------ | ---------- | --------------------------- | -------------- |
|
|
1570
|
+
| `size:n` | Column size | `varchar | size:255` |
|
|
1571
|
+
| `unsigned` | Unsigned integer | `int | unsigned` |
|
|
1572
|
+
| `primaryKey` | Primary key column | `bigint | primaryKey` |
|
|
1573
|
+
| `autoIncrement` | Auto increment | `bigint | primaryKey | autoIncrement` |
|
|
1574
|
+
| `notNull` | Not nullable | `varchar | size:255 | notNull` |
|
|
1575
|
+
| `unique` | Unique constraint | `varchar | unique` |
|
|
1576
|
+
| `default:value` | Default value | `tinyint | default:1` |
|
|
1577
|
+
| `onUpdate:value` | On update value | `timestamp | onUpdate:CURRENT_TIMESTAMP` |
|
|
1578
|
+
| `comment:text` | Column comment | `varchar | comment:User email address` |
|
|
1579
|
+
| `foreignKey:table:column` | Foreign key | `int | foreignKey:users:id` |
|
|
1545
1580
|
|
|
1546
1581
|
**Special Default Values:**
|
|
1582
|
+
|
|
1547
1583
|
- `now` or `now()` → `CURRENT_TIMESTAMP`
|
|
1548
1584
|
|
|
1549
1585
|
**Example Column Definitions:**
|
|
@@ -1591,11 +1627,11 @@ Seed data is automatically inserted when `syncDatabase()` is called and the tabl
|
|
|
1591
1627
|
const { initializeKORM } = require('@dreamtree-org/korm-js');
|
|
1592
1628
|
|
|
1593
1629
|
const korm = initializeKORM({
|
|
1594
|
-
db: db,
|
|
1595
|
-
dbClient: 'mysql',
|
|
1596
|
-
schema: null,
|
|
1597
|
-
resolverPath: null,
|
|
1598
|
-
debug: false
|
|
1630
|
+
db: db, // Knex database instance
|
|
1631
|
+
dbClient: 'mysql', // 'mysql', 'mysql2', 'pg', 'postgresql', 'sqlite', 'sqlite3'
|
|
1632
|
+
schema: null, // Optional: initial schema object
|
|
1633
|
+
resolverPath: null, // Optional: path to models directory (default: process.cwd())
|
|
1634
|
+
debug: false, // Optional: enable SQL debugging (default: false)
|
|
1599
1635
|
});
|
|
1600
1636
|
|
|
1601
1637
|
// Process any CRUD request (automatically handles other_requests if present)
|
|
@@ -1622,65 +1658,66 @@ const modelInstance = korm.getModelInstance(modelDef);
|
|
|
1622
1658
|
|
|
1623
1659
|
### ProcessRequest Options
|
|
1624
1660
|
|
|
1625
|
-
| Parameter
|
|
1626
|
-
|
|
1627
|
-
| `action`
|
|
1628
|
-
| `where`
|
|
1629
|
-
| `data`
|
|
1630
|
-
| `select`
|
|
1631
|
-
| `orderBy`
|
|
1632
|
-
| `limit`
|
|
1633
|
-
| `offset`
|
|
1634
|
-
| `page`
|
|
1635
|
-
| `with`
|
|
1636
|
-
| `withWhere`
|
|
1637
|
-
| `groupBy`
|
|
1638
|
-
| `having`
|
|
1639
|
-
| `distinct`
|
|
1640
|
-
| `join`
|
|
1641
|
-
| `leftJoin`
|
|
1642
|
-
| `rightJoin`
|
|
1643
|
-
| `innerJoin`
|
|
1644
|
-
| `conflict`
|
|
1645
|
-
| `other_requests` | object
|
|
1661
|
+
| Parameter | Type | Description |
|
|
1662
|
+
| ---------------- | -------------------- | ------------------------------------------------------------------------------------ |
|
|
1663
|
+
| `action` | string | Action to perform (list, show, create, update, delete, count, replace, upsert, sync) |
|
|
1664
|
+
| `where` | object/array | Filter conditions |
|
|
1665
|
+
| `data` | object/array | Data for create/update operations |
|
|
1666
|
+
| `select` | array/string | Columns to select |
|
|
1667
|
+
| `orderBy` | object/array/string | Sorting configuration |
|
|
1668
|
+
| `limit` | number | Maximum records to return |
|
|
1669
|
+
| `offset` | number | Records to skip |
|
|
1670
|
+
| `page` | number | Page number (alternative to offset) |
|
|
1671
|
+
| `with` | array | Related models to eager load |
|
|
1672
|
+
| `withWhere` | object | Filter conditions for eager loaded relations |
|
|
1673
|
+
| `groupBy` | array/string | Group by columns |
|
|
1674
|
+
| `having` | object | Having conditions |
|
|
1675
|
+
| `distinct` | boolean/array/string | Distinct results |
|
|
1676
|
+
| `join` | object/array | Join configuration |
|
|
1677
|
+
| `leftJoin` | object/array | Left join configuration |
|
|
1678
|
+
| `rightJoin` | object/array | Right join configuration |
|
|
1679
|
+
| `innerJoin` | object/array | Inner join configuration |
|
|
1680
|
+
| `conflict` | array | Conflict columns for upsert |
|
|
1681
|
+
| `other_requests` | object | Nested requests for related models |
|
|
1646
1682
|
|
|
1647
1683
|
### ProcessRequest Actions Summary
|
|
1648
1684
|
|
|
1649
|
-
| Action
|
|
1650
|
-
|
|
1651
|
-
| `list`
|
|
1652
|
-
| `show`
|
|
1653
|
-
| `create`
|
|
1654
|
-
| `update`
|
|
1655
|
-
| `delete`
|
|
1656
|
-
| `count`
|
|
1657
|
-
| `sum`
|
|
1658
|
-
| `replace` | Replace record (MySQL)
|
|
1659
|
-
| `upsert`
|
|
1660
|
-
| `sync`
|
|
1685
|
+
| Action | Description | Required Fields |
|
|
1686
|
+
| --------- | ------------------------ | ---------------------------------------------------------------- |
|
|
1687
|
+
| `list` | Get multiple records | None (optional: `where`, `select`, `orderBy`, `limit`, `offset`) |
|
|
1688
|
+
| `show` | Get single record | `where` |
|
|
1689
|
+
| `create` | Create new record | `data` |
|
|
1690
|
+
| `update` | Update record(s) | `where`, `data` |
|
|
1691
|
+
| `delete` | Delete record(s) | `where` |
|
|
1692
|
+
| `count` | Count records | None (optional: `where`) |
|
|
1693
|
+
| `sum` | Sum column or expression | `data.sumColumn` or `data.sumFormula` (optional: `where`) |
|
|
1694
|
+
| `replace` | Replace record (MySQL) | `data` |
|
|
1695
|
+
| `upsert` | Insert or update | `data`, `conflict` |
|
|
1696
|
+
| `sync` | Upsert + delete | `data`, `conflict`, `where` |
|
|
1661
1697
|
|
|
1662
1698
|
### Validation Rules
|
|
1663
1699
|
|
|
1664
|
-
| Rule
|
|
1665
|
-
|
|
1666
|
-
| `required`
|
|
1667
|
-
| `type:string`
|
|
1668
|
-
| `type:number`
|
|
1669
|
-
| `type:boolean`
|
|
1670
|
-
| `type:array`
|
|
1671
|
-
| `type:object`
|
|
1672
|
-
| `type:longText`
|
|
1673
|
-
| `minLen:n`
|
|
1674
|
-
| `maxLen:n`
|
|
1675
|
-
| `min:n`
|
|
1676
|
-
| `max:n`
|
|
1677
|
-
| `in:val1,val2`
|
|
1678
|
-
| `regex:name`
|
|
1679
|
-
| `call:name`
|
|
1680
|
-
| `exists:table,column` | Value must exist in database table
|
|
1681
|
-
| `default:value`
|
|
1700
|
+
| Rule | Description | Example |
|
|
1701
|
+
| --------------------- | -------------------------------------------- | ------------------------------ |
|
|
1702
|
+
| `required` | Field is required | `'required'` |
|
|
1703
|
+
| `type:string` | Field must be string | `'type:string'` |
|
|
1704
|
+
| `type:number` | Field must be number | `'type:number'` |
|
|
1705
|
+
| `type:boolean` | Field must be boolean | `'type:boolean'` |
|
|
1706
|
+
| `type:array` | Field must be array | `'type:array'` |
|
|
1707
|
+
| `type:object` | Field must be object | `'type:object'` |
|
|
1708
|
+
| `type:longText` | Field must be string > 255 chars | `'type:longText'` |
|
|
1709
|
+
| `minLen:n` | Minimum string/array length | `'minLen:3'` |
|
|
1710
|
+
| `maxLen:n` | Maximum string/array length | `'maxLen:255'` |
|
|
1711
|
+
| `min:n` | Minimum numeric value | `'min:0'` |
|
|
1712
|
+
| `max:n` | Maximum numeric value | `'max:150'` |
|
|
1713
|
+
| `in:val1,val2` | Value must be in list | `'in:active,inactive,pending'` |
|
|
1714
|
+
| `regex:name` | Custom regex pattern (define in options) | `'regex:email'` |
|
|
1715
|
+
| `call:name` | Custom callback function (define in options) | `'call:myValidator'` |
|
|
1716
|
+
| `exists:table,column` | Value must exist in database table | `'exists:users,id'` |
|
|
1717
|
+
| `default:value` | Default value if not provided | `'default:active'` |
|
|
1682
1718
|
|
|
1683
1719
|
**Rule Chaining:** Combine multiple rules with `|` pipe character:
|
|
1720
|
+
|
|
1684
1721
|
```javascript
|
|
1685
1722
|
{
|
|
1686
1723
|
username: 'required|type:string|minLen:3|maxLen:50',
|
|
@@ -1693,14 +1730,14 @@ const modelInstance = korm.getModelInstance(modelDef);
|
|
|
1693
1730
|
### Library Exports
|
|
1694
1731
|
|
|
1695
1732
|
```javascript
|
|
1696
|
-
const {
|
|
1697
|
-
initializeKORM,
|
|
1698
|
-
helperUtility,
|
|
1699
|
-
emitter,
|
|
1700
|
-
validate,
|
|
1701
|
-
logger,
|
|
1702
|
-
lib,
|
|
1703
|
-
LibClasses
|
|
1733
|
+
const {
|
|
1734
|
+
initializeKORM, // Initialize KORM with database connection
|
|
1735
|
+
helperUtility, // Utility functions (file operations, string manipulation)
|
|
1736
|
+
emitter, // Event emitter instance
|
|
1737
|
+
validate, // Validation function
|
|
1738
|
+
logger, // Logger utility instance
|
|
1739
|
+
lib, // Additional utilities
|
|
1740
|
+
LibClasses, // Library classes (Emitter)
|
|
1704
1741
|
} = require('@dreamtree-org/korm-js');
|
|
1705
1742
|
|
|
1706
1743
|
// lib contains:
|
|
@@ -1736,14 +1773,14 @@ logger.error('Error message');
|
|
|
1736
1773
|
|
|
1737
1774
|
### Log Levels
|
|
1738
1775
|
|
|
1739
|
-
| Level
|
|
1740
|
-
|
|
1741
|
-
| `NONE`
|
|
1742
|
-
| `ERROR` | 1
|
|
1743
|
-
| `WARN`
|
|
1744
|
-
| `INFO`
|
|
1745
|
-
| `LOG`
|
|
1746
|
-
| `DEBUG` | 5
|
|
1776
|
+
| Level | Value | Description |
|
|
1777
|
+
| ------- | ----- | ----------------------------- |
|
|
1778
|
+
| `NONE` | 0 | Disable all logging |
|
|
1779
|
+
| `ERROR` | 1 | Only errors |
|
|
1780
|
+
| `WARN` | 2 | Warnings and errors (default) |
|
|
1781
|
+
| `INFO` | 3 | Info, warnings, and errors |
|
|
1782
|
+
| `LOG` | 4 | General logs and above |
|
|
1783
|
+
| `DEBUG` | 5 | All messages (most verbose) |
|
|
1747
1784
|
|
|
1748
1785
|
### Configuration
|
|
1749
1786
|
|
|
@@ -1751,14 +1788,14 @@ logger.error('Error message');
|
|
|
1751
1788
|
const { logger } = require('@dreamtree-org/korm-js');
|
|
1752
1789
|
|
|
1753
1790
|
// Set log level programmatically
|
|
1754
|
-
logger.setLevel('debug');
|
|
1755
|
-
logger.setLevel('warn');
|
|
1756
|
-
logger.setLevel('error');
|
|
1757
|
-
logger.setLevel('none');
|
|
1791
|
+
logger.setLevel('debug'); // Show all logs
|
|
1792
|
+
logger.setLevel('warn'); // Only warnings and errors (default)
|
|
1793
|
+
logger.setLevel('error'); // Only errors
|
|
1794
|
+
logger.setLevel('none'); // Disable all logging
|
|
1758
1795
|
|
|
1759
1796
|
// Enable/disable logging
|
|
1760
|
-
logger.disable();
|
|
1761
|
-
logger.enable();
|
|
1797
|
+
logger.disable(); // Temporarily disable all logging
|
|
1798
|
+
logger.enable(); // Re-enable logging
|
|
1762
1799
|
```
|
|
1763
1800
|
|
|
1764
1801
|
### Environment Variable
|
|
@@ -1787,10 +1824,10 @@ const { logger } = require('@dreamtree-org/korm-js');
|
|
|
1787
1824
|
|
|
1788
1825
|
// Create a child logger for a specific module
|
|
1789
1826
|
const authLogger = logger.child('[Auth]');
|
|
1790
|
-
authLogger.info('User logged in');
|
|
1827
|
+
authLogger.info('User logged in'); // Output: [KORM][Auth] [INFO] User logged in
|
|
1791
1828
|
|
|
1792
1829
|
const dbLogger = logger.child('[DB]');
|
|
1793
|
-
dbLogger.debug('Query executed');
|
|
1830
|
+
dbLogger.debug('Query executed'); // Output: [KORM][DB] [DEBUG] Query executed
|
|
1794
1831
|
```
|
|
1795
1832
|
|
|
1796
1833
|
### Logger Options
|
|
@@ -1800,10 +1837,10 @@ const { Logger } = require('@dreamtree-org/korm-js').logger;
|
|
|
1800
1837
|
|
|
1801
1838
|
// Create a custom logger instance
|
|
1802
1839
|
const customLogger = new Logger({
|
|
1803
|
-
prefix: '[MyApp]',
|
|
1804
|
-
enabled: true,
|
|
1805
|
-
level: 'debug',
|
|
1806
|
-
timestamps: true
|
|
1840
|
+
prefix: '[MyApp]', // Custom prefix (default: '[KORM]')
|
|
1841
|
+
enabled: true, // Enable/disable logging (default: true)
|
|
1842
|
+
level: 'debug', // Log level (default: 'warn' or KORM_LOG_LEVEL)
|
|
1843
|
+
timestamps: true, // Include timestamps (default: false)
|
|
1807
1844
|
});
|
|
1808
1845
|
|
|
1809
1846
|
customLogger.info('Application started');
|
|
@@ -1841,7 +1878,7 @@ const { initializeKORM } = require('@dreamtree-org/korm-js');
|
|
|
1841
1878
|
const korm = initializeKORM({
|
|
1842
1879
|
db: db,
|
|
1843
1880
|
dbClient: 'mysql',
|
|
1844
|
-
debug: true
|
|
1881
|
+
debug: true, // Enable SQL debugging
|
|
1845
1882
|
});
|
|
1846
1883
|
```
|
|
1847
1884
|
|
|
@@ -1883,10 +1920,10 @@ POST /api/Post/crud
|
|
|
1883
1920
|
|
|
1884
1921
|
### What's Included in sqlDebug
|
|
1885
1922
|
|
|
1886
|
-
| Index | Query Type
|
|
1887
|
-
|
|
1888
|
-
| 0
|
|
1889
|
-
| 1
|
|
1923
|
+
| Index | Query Type | Description |
|
|
1924
|
+
| ----- | ----------- | ---------------------------------------------------------------- |
|
|
1925
|
+
| 0 | Main Query | The primary SELECT query with all WHERE, ORDER BY, LIMIT clauses |
|
|
1926
|
+
| 1 | Count Query | The COUNT query used for pagination (only when limit > 0) |
|
|
1890
1927
|
|
|
1891
1928
|
### Debug Mode Best Practices
|
|
1892
1929
|
|
|
@@ -1895,14 +1932,14 @@ POST /api/Post/crud
|
|
|
1895
1932
|
const korm = initializeKORM({
|
|
1896
1933
|
db: db,
|
|
1897
1934
|
dbClient: 'mysql',
|
|
1898
|
-
debug: process.env.NODE_ENV === 'development'
|
|
1935
|
+
debug: process.env.NODE_ENV === 'development',
|
|
1899
1936
|
});
|
|
1900
1937
|
|
|
1901
1938
|
// Or use environment variable
|
|
1902
1939
|
const korm = initializeKORM({
|
|
1903
1940
|
db: db,
|
|
1904
1941
|
dbClient: 'mysql',
|
|
1905
|
-
debug: process.env.KORM_DEBUG === 'true'
|
|
1942
|
+
debug: process.env.KORM_DEBUG === 'true',
|
|
1906
1943
|
});
|
|
1907
1944
|
```
|
|
1908
1945
|
|
|
@@ -1922,14 +1959,14 @@ const db = knex({
|
|
|
1922
1959
|
user: process.env.DB_USER || 'root',
|
|
1923
1960
|
password: process.env.DB_PASS || 'password',
|
|
1924
1961
|
database: process.env.DB_NAME || 'database_name',
|
|
1925
|
-
port: process.env.DB_PORT || 3306
|
|
1926
|
-
}
|
|
1962
|
+
port: process.env.DB_PORT || 3306,
|
|
1963
|
+
},
|
|
1927
1964
|
});
|
|
1928
1965
|
|
|
1929
1966
|
const korm = initializeKORM({
|
|
1930
1967
|
db: db,
|
|
1931
1968
|
dbClient: 'mysql',
|
|
1932
|
-
debug: process.env.NODE_ENV === 'development'
|
|
1969
|
+
debug: process.env.NODE_ENV === 'development', // Enable SQL debugging in development
|
|
1933
1970
|
});
|
|
1934
1971
|
```
|
|
1935
1972
|
|
|
@@ -1945,14 +1982,14 @@ const db = knex({
|
|
|
1945
1982
|
user: process.env.DB_USER || 'username',
|
|
1946
1983
|
password: process.env.DB_PASS || 'password',
|
|
1947
1984
|
database: process.env.DB_NAME || 'database_name',
|
|
1948
|
-
port: process.env.DB_PORT || 5432
|
|
1949
|
-
}
|
|
1985
|
+
port: process.env.DB_PORT || 5432,
|
|
1986
|
+
},
|
|
1950
1987
|
});
|
|
1951
1988
|
|
|
1952
1989
|
const korm = initializeKORM({
|
|
1953
1990
|
db: db,
|
|
1954
1991
|
dbClient: 'pg',
|
|
1955
|
-
debug: process.env.NODE_ENV === 'development'
|
|
1992
|
+
debug: process.env.NODE_ENV === 'development',
|
|
1956
1993
|
});
|
|
1957
1994
|
```
|
|
1958
1995
|
|
|
@@ -1964,14 +2001,14 @@ const knex = require('knex');
|
|
|
1964
2001
|
const db = knex({
|
|
1965
2002
|
client: 'sqlite3',
|
|
1966
2003
|
connection: {
|
|
1967
|
-
filename: process.env.DB_FILE || './database.sqlite'
|
|
1968
|
-
}
|
|
2004
|
+
filename: process.env.DB_FILE || './database.sqlite',
|
|
2005
|
+
},
|
|
1969
2006
|
});
|
|
1970
2007
|
|
|
1971
2008
|
const korm = initializeKORM({
|
|
1972
2009
|
db: db,
|
|
1973
2010
|
dbClient: 'sqlite',
|
|
1974
|
-
debug: process.env.NODE_ENV === 'development'
|
|
2011
|
+
debug: process.env.NODE_ENV === 'development',
|
|
1975
2012
|
});
|
|
1976
2013
|
```
|
|
1977
2014
|
|
|
@@ -1981,12 +2018,12 @@ const korm = initializeKORM({
|
|
|
1981
2018
|
// Global error handler
|
|
1982
2019
|
app.use((error, req, res, next) => {
|
|
1983
2020
|
console.error('KORM Error:', error);
|
|
1984
|
-
|
|
2021
|
+
|
|
1985
2022
|
res.status(error.status || 500).json({
|
|
1986
2023
|
success: false,
|
|
1987
2024
|
message: 'Internal server error',
|
|
1988
2025
|
error: process.env.NODE_ENV === 'development' ? error.message : 'Something went wrong',
|
|
1989
|
-
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
|
2026
|
+
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined,
|
|
1990
2027
|
});
|
|
1991
2028
|
});
|
|
1992
2029
|
|
|
@@ -2002,22 +2039,22 @@ app.post('/api/:model/crud', async (req, res) => {
|
|
|
2002
2039
|
return res.status(400).json({
|
|
2003
2040
|
success: false,
|
|
2004
2041
|
message: 'Validation failed',
|
|
2005
|
-
errors: error.message
|
|
2042
|
+
errors: error.message,
|
|
2006
2043
|
});
|
|
2007
2044
|
}
|
|
2008
|
-
|
|
2045
|
+
|
|
2009
2046
|
// Handle not found errors
|
|
2010
2047
|
if (error.message.includes('not found')) {
|
|
2011
2048
|
return res.status(404).json({
|
|
2012
2049
|
success: false,
|
|
2013
|
-
message: error.message
|
|
2050
|
+
message: error.message,
|
|
2014
2051
|
});
|
|
2015
2052
|
}
|
|
2016
|
-
|
|
2053
|
+
|
|
2017
2054
|
// Handle other errors
|
|
2018
2055
|
res.status(400).json({
|
|
2019
2056
|
success: false,
|
|
2020
|
-
message: error.message
|
|
2057
|
+
message: error.message,
|
|
2021
2058
|
});
|
|
2022
2059
|
}
|
|
2023
2060
|
});
|
|
@@ -2042,15 +2079,15 @@ const db = knex({
|
|
|
2042
2079
|
user: process.env.DB_USER || 'root',
|
|
2043
2080
|
password: process.env.DB_PASS || 'password',
|
|
2044
2081
|
database: process.env.DB_NAME || 'my_database',
|
|
2045
|
-
port: process.env.DB_PORT || 3306
|
|
2046
|
-
}
|
|
2082
|
+
port: process.env.DB_PORT || 3306,
|
|
2083
|
+
},
|
|
2047
2084
|
});
|
|
2048
2085
|
|
|
2049
2086
|
// Initialize KORM
|
|
2050
2087
|
const korm = initializeKORM({
|
|
2051
2088
|
db: db,
|
|
2052
2089
|
dbClient: 'mysql',
|
|
2053
|
-
debug: process.env.NODE_ENV === 'development'
|
|
2090
|
+
debug: process.env.NODE_ENV === 'development', // SQL debugging in dev mode
|
|
2054
2091
|
});
|
|
2055
2092
|
|
|
2056
2093
|
// Initialize app
|
|
@@ -2065,7 +2102,7 @@ async function initApp() {
|
|
|
2065
2102
|
helperUtility.file.createDirectory('schema');
|
|
2066
2103
|
helperUtility.file.writeJSON('schema/schema.json', schema);
|
|
2067
2104
|
}
|
|
2068
|
-
|
|
2105
|
+
|
|
2069
2106
|
// Sync database
|
|
2070
2107
|
await korm.syncDatabase();
|
|
2071
2108
|
console.log('✅ Database synced');
|