forge-sql-orm 1.0.1
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/LICENSE +21 -0
- package/README.md +629 -0
- package/dist/core/ForgeSQLCrudOperations.d.ts +45 -0
- package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -0
- package/dist/core/ForgeSQLORM.d.ts +31 -0
- package/dist/core/ForgeSQLORM.d.ts.map +1 -0
- package/dist/core/ForgeSQLQueryBuilder.d.ts +83 -0
- package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -0
- package/dist/core/ForgeSQLSelectOperations.d.ts +25 -0
- package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +332 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +321 -0
- package/dist/index.mjs.map +1 -0
- package/dist/knex/index.d.ts +4 -0
- package/dist/knex/index.d.ts.map +1 -0
- package/dist/utils/sqlUtils.d.ts +8 -0
- package/dist/utils/sqlUtils.d.ts.map +1 -0
- package/package.json +92 -0
- package/scripts/actions/PatchPostinstall.ts +201 -0
- package/scripts/actions/generate-models.ts +65 -0
- package/scripts/actions/migrations-create.ts +192 -0
- package/scripts/actions/migrations-update.ts +200 -0
- package/scripts/cli.js +4 -0
- package/scripts/cli.ts +221 -0
- package/src/core/ForgeSQLCrudOperations.ts +164 -0
- package/src/core/ForgeSQLORM.ts +150 -0
- package/src/core/ForgeSQLQueryBuilder.ts +100 -0
- package/src/core/ForgeSQLSelectOperations.ts +72 -0
- package/src/index.ts +9 -0
- package/src/knex/index.ts +4 -0
- package/src/utils/sqlUtils.ts +25 -0
- package/tsconfig.json +24 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Vasiliy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
# Forge SQL ORM
|
|
2
|
+
|
|
3
|
+
**Forge-SQL-ORM** is an ORM designed for working with [@forge/sql](https://developer.atlassian.com/platform/forge/storage-reference/sql-tutorial/) in **Atlassian Forge**. It is built on top of [MikroORM](https://mikro-orm.io/docs/query-builder) and provides advanced capabilities for working with relational databases inside Forge.
|
|
4
|
+
|
|
5
|
+
## Key Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Supports foreign keys** at the entity level.
|
|
8
|
+
- ✅ **Supports complex SQL queries** with joins and filtering.
|
|
9
|
+
- ✅ **Batch insert support** with duplicate key handling.
|
|
10
|
+
- ✅ **Schema migration support**, allowing automatic schema evolution.
|
|
11
|
+
- ✅ **Automatic entity generation** from MySQL/tidb databases.
|
|
12
|
+
- ✅ **Automatic migration generation** from MySQL/tidb databases.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
Forge-SQL-ORM is designed to work with @forge/sql and requires some additional setup to ensure compatibility within Atlassian Forge.
|
|
16
|
+
|
|
17
|
+
✅ Step 1: Install Dependencies
|
|
18
|
+
```sh
|
|
19
|
+
npm install forge-sql-orm -S
|
|
20
|
+
npm install @forge/sql -S
|
|
21
|
+
```
|
|
22
|
+
This will:
|
|
23
|
+
|
|
24
|
+
Install Forge-SQL-ORM (the ORM for @forge/sql).
|
|
25
|
+
Install @forge/sql, the Forge database layer.
|
|
26
|
+
|
|
27
|
+
✅ Step 2: Configure Post-Installation Patch
|
|
28
|
+
By default, MikroORM and Knex include some features that are not compatible with Forge's restricted runtime.
|
|
29
|
+
To fix this, we need to patch these libraries after installation.
|
|
30
|
+
|
|
31
|
+
Run:
|
|
32
|
+
```sh
|
|
33
|
+
npm pkg set scripts.postinstall="forge-sql-orm patch:mikroorm"
|
|
34
|
+
```
|
|
35
|
+
✅ Step 3: Apply the Patch
|
|
36
|
+
After setting up the postinstall script, run:
|
|
37
|
+
```sh
|
|
38
|
+
npm i
|
|
39
|
+
```
|
|
40
|
+
This will:
|
|
41
|
+
|
|
42
|
+
Trigger the postinstall hook, which applies the necessary patches to MikroORM and Knex.
|
|
43
|
+
Ensure everything is correctly configured for running inside Forge.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
🔧 Why is the Patch Required?
|
|
47
|
+
Atlassian Forge has a restricted execution environment, which does not allow:
|
|
48
|
+
|
|
49
|
+
- Dynamic import(id) calls, commonly used in MikroORM.
|
|
50
|
+
- Direct file system access, which MikroORM sometimes relies on.
|
|
51
|
+
- Unsupported database dialects, such as PostgreSQL or SQLite.
|
|
52
|
+
- The patch removes these unsupported features to ensure full compatibility.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## Step-by-Step Migration Workflow
|
|
56
|
+
|
|
57
|
+
1. **Generate initial entity models from an existing database**
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
npx forge-sql-orm generate:model --dbName testDb --output ./database/entities
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
_(This is done only once when setting up the project)_
|
|
64
|
+
|
|
65
|
+
2. **Create the first migration**
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
npx forge-sql-orm migrations:create --dbName testDb --entitiesPath ./database/entities --output ./database/migration
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
_(This initializes the database migration structure, also done once)_
|
|
72
|
+
|
|
73
|
+
3. **Deploy to Forge and verify that migrations work**
|
|
74
|
+
|
|
75
|
+
- Deploy your **Forge app** with migrations.
|
|
76
|
+
- Run migrations using a **Forge web trigger** or **Forge scheduler**.
|
|
77
|
+
|
|
78
|
+
4. **Modify the database (e.g., add a new column, index, etc.)**
|
|
79
|
+
|
|
80
|
+
- Use **DbSchema** or manually alter the database schema.
|
|
81
|
+
|
|
82
|
+
5. **Update the migration**
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
npx forge-sql-orm migrations:update --dbName testDb --entitiesPath ./database/entities --output ./database/migration
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- ⚠️ **Do NOT update entities before this step!**
|
|
89
|
+
- If entities are updated first, the migration will be empty!
|
|
90
|
+
|
|
91
|
+
6. **Deploy to Forge and verify that the migration runs without issues**
|
|
92
|
+
|
|
93
|
+
- Run the updated migration on Forge.
|
|
94
|
+
|
|
95
|
+
7. **Update the entity models**
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
npx forge-sql-orm generate:model --dbName testDb --output ./database/entities
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
8. **Repeat steps 4-7 as needed**
|
|
102
|
+
|
|
103
|
+
**⚠️ WARNING:**
|
|
104
|
+
|
|
105
|
+
- **Do NOT swap steps 7 and 5!** If you update models before generating a migration, the migration will be empty!
|
|
106
|
+
- Always generate the **migration first**, then update the **entities**.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# Connection to ORM
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
import ForgeSQL from "forge-sql-orm";
|
|
115
|
+
import { Orders } from "./entities/Orders";
|
|
116
|
+
import { Users } from "./entities/Users";
|
|
117
|
+
import ENTITIES from "./entities";
|
|
118
|
+
|
|
119
|
+
const forgeSQL = new ForgeSQL(ENTITIES);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- Fetch Data:
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
const formattedQuery = forgeSQL
|
|
126
|
+
.createQueryBuilder(Users)
|
|
127
|
+
.select("*")
|
|
128
|
+
.limit(limit)
|
|
129
|
+
.offset(offset)
|
|
130
|
+
.getFormattedQuery();
|
|
131
|
+
//select `u0`.* from `users` as `u0` limit 10 offset 1
|
|
132
|
+
return await forgeSQL.fetch().executeSchemaSQL(formattedQuery, UsersSchema);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- Raw Fetch Data
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
const users = await forgeSQL.fetch().executeRawSQL<Users>("SELECT * FROM users");
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- Complex Query
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
// Define schema for join result
|
|
145
|
+
class InnerJoinResult {
|
|
146
|
+
name!: string;
|
|
147
|
+
product!: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const innerJoinSchema = new EntitySchema<InnerJoinResult>({
|
|
151
|
+
class: InnerJoinResult,
|
|
152
|
+
properties: {
|
|
153
|
+
name: { type: "string", fieldName: "name" },
|
|
154
|
+
product: { type: "string", fieldName: "product" }
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
innerJoinSchema.init();
|
|
158
|
+
|
|
159
|
+
// Execute query
|
|
160
|
+
const query = forgeSQL.createQueryBuilder(Orders, "order")
|
|
161
|
+
.limit(10).offset(10)
|
|
162
|
+
.innerJoin("user", "user")
|
|
163
|
+
.select(["user.name", "order.product"])
|
|
164
|
+
.getFormattedQuery();
|
|
165
|
+
// select `user`.`name`, `order`.`product` from `orders` as `order` inner join `users` as `user` on `order`.`user_id` = `user`.`id` limit 10 offset 10
|
|
166
|
+
const results = await forgeSQL.fetch().executeSchemaSQL(query, innerJoinSchema);
|
|
167
|
+
console.log(results);
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
🛠 CRUD Operations
|
|
171
|
+
|
|
172
|
+
- Insert Data
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
// INSERT INTO users (id, name) VALUES (1,'Smith')
|
|
176
|
+
const userId = await forgeSQL.crud().insert(UsersSchema, [{ id: 1, name: "Smith" }]);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
- Insert Bulk Data
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
// INSERT INTO users (id,name) VALUES (2,'Smith'), (3,'Vasyl')
|
|
183
|
+
await forgeSQL.crud().insert(UsersSchema, [
|
|
184
|
+
{ id: 2, name: "Smith" },
|
|
185
|
+
{ id: 3, name: "Vasyl" },
|
|
186
|
+
]);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
- Insert Data with duplicates
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
// INSERT INTO users (id,name) VALUES (4,'Smith'), (4, 'Vasyl') ON DUPLICATE KEY UPDATE name = VALUES(name)
|
|
193
|
+
await forgeSQL.crud().insert(
|
|
194
|
+
UsersSchema,
|
|
195
|
+
[
|
|
196
|
+
{ id: 4, name: "Smith" },
|
|
197
|
+
{ id: 4, name: "Vasyl" },
|
|
198
|
+
],
|
|
199
|
+
true,
|
|
200
|
+
);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
- Update Data
|
|
204
|
+
|
|
205
|
+
```js
|
|
206
|
+
await forgeSQL.crud().updateById({ id: 1, name: "Smith Updated" }, UsersSchema);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
- Delete Data
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
await forgeSQL.crud().deleteById(1, UsersSchema);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Quick Start
|
|
216
|
+
|
|
217
|
+
### 1. Designing the Database
|
|
218
|
+
|
|
219
|
+
You can start by designing a **MySQL/tidb database** using tools like [DbSchema](https://dbschema.com/) or by using an existing MySQL/tidb database.
|
|
220
|
+
|
|
221
|
+
**Schema visualization:**
|
|
222
|
+

|
|
223
|
+
|
|
224
|
+
#### DDL Scripts
|
|
225
|
+
|
|
226
|
+
```sql
|
|
227
|
+
CREATE DATABASE testDb;
|
|
228
|
+
CREATE TABLE testDb.users (
|
|
229
|
+
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
230
|
+
name VARCHAR(200)
|
|
231
|
+
) engine=InnoDB;
|
|
232
|
+
|
|
233
|
+
CREATE TABLE testDb.orders (
|
|
234
|
+
id INT NOT NULL PRIMARY KEY,
|
|
235
|
+
user_id INT NOT NULL ,
|
|
236
|
+
product VARCHAR(200)
|
|
237
|
+
) engine=InnoDB;
|
|
238
|
+
|
|
239
|
+
ALTER TABLE testDb.orders ADD CONSTRAINT fk_orders_users FOREIGN KEY ( user_id ) REFERENCES testDb.users( id ) ON DELETE NO ACTION ON UPDATE NO ACTION;
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### 2. Generate Models
|
|
243
|
+
|
|
244
|
+
Run the following command to generate entity models based on your database:
|
|
245
|
+
|
|
246
|
+
```sh
|
|
247
|
+
npx forge-sql-orm generate:model --host localhost --port 3306 --user root --password secret --dbName testDb --output ./database/entities
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
This will generate **entity schemas** in the `./database/entities` directory.
|
|
251
|
+
Users Model and Schema:
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
import { EntitySchema } from 'forge-sql-orm';
|
|
255
|
+
|
|
256
|
+
export class Users {
|
|
257
|
+
id!: number;
|
|
258
|
+
name?: string;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export const UsersSchema = new EntitySchema({
|
|
262
|
+
class: Users,
|
|
263
|
+
properties: {
|
|
264
|
+
id: { primary: true, type: 'integer', unsigned: false },
|
|
265
|
+
name: { type: 'string', length: 200, nullable: true },
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Orders Model and Schema:
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
import { EntitySchema } from 'forge-sql-orm';
|
|
275
|
+
import { Users } from './Users';
|
|
276
|
+
|
|
277
|
+
export class Orders {
|
|
278
|
+
id!: number;
|
|
279
|
+
user!: Users;
|
|
280
|
+
userId!: number;
|
|
281
|
+
product?: string;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export const OrdersSchema = new EntitySchema({
|
|
285
|
+
class: Orders,
|
|
286
|
+
properties: {
|
|
287
|
+
id: { primary: true, type: 'integer', unsigned: false, autoincrement: false },
|
|
288
|
+
user: {
|
|
289
|
+
kind: 'm:1',
|
|
290
|
+
entity: () => Users,
|
|
291
|
+
fieldName: 'user_id',
|
|
292
|
+
index: 'fk_orders_users',
|
|
293
|
+
},
|
|
294
|
+
userId: {
|
|
295
|
+
type: 'integer',
|
|
296
|
+
fieldName: 'user_id',
|
|
297
|
+
persist: false,
|
|
298
|
+
index: 'fk_orders_users',
|
|
299
|
+
},
|
|
300
|
+
product: { type: 'string', length: 200, nullable: true },
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
index.ts
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
import { Orders } from "./Orders";
|
|
309
|
+
import { Users } from "./Users";
|
|
310
|
+
|
|
311
|
+
export default [Orders, Users];
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### 3. Create the First Migration
|
|
315
|
+
|
|
316
|
+
After generating the models, create the first migration file that represents the current database state:
|
|
317
|
+
|
|
318
|
+
```sh
|
|
319
|
+
npx forge-sql-orm migrations:create --dbName testDb --entitiesPath ./database/entities --output ./database/migration
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Generated migration:
|
|
323
|
+
|
|
324
|
+
```js
|
|
325
|
+
import { MigrationRunner } from "@forge/sql/out/migration";
|
|
326
|
+
|
|
327
|
+
export default (migrationRunner: MigrationRunner): MigrationRunner => {
|
|
328
|
+
return migrationRunner
|
|
329
|
+
.enqueue("v1_MIGRATION0", "create table `users` (`id` int not null auto_increment primary key, `name` varchar(200) null)")
|
|
330
|
+
.enqueue("v1_MIGRATION1", "create table `orders` (`id` int not null, `user_id` int not null, `product` varchar(200) null, primary key (`id`))")
|
|
331
|
+
.enqueue("v1_MIGRATION2", "alter table `orders` add index `fk_orders_users`(`user_id`)");
|
|
332
|
+
};
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### 4. Deploy and Run Migrations
|
|
336
|
+
|
|
337
|
+
```js
|
|
338
|
+
import migration from "./database/migration";
|
|
339
|
+
import sql, { migrationRunner } from "@forge/sql";
|
|
340
|
+
|
|
341
|
+
export const trigger = async () => {
|
|
342
|
+
console.log("Provisioning the database");
|
|
343
|
+
await sql._provision();
|
|
344
|
+
|
|
345
|
+
console.log("Running schema migrations");
|
|
346
|
+
const migrations = await migration(migrationRunner);
|
|
347
|
+
const successfulMigrations = await migrations.run();
|
|
348
|
+
console.log("Migrations applied:", successfulMigrations);
|
|
349
|
+
|
|
350
|
+
const migrationHistory = (await migrationRunner.list())
|
|
351
|
+
.map((y) => `${y.id}, ${y.name}, ${y.migratedAt.toUTCString()}`)
|
|
352
|
+
.join("\n");
|
|
353
|
+
|
|
354
|
+
console.log("Migrations history:\nid, name, migrated_at\n", migrationHistory);
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
headers: { "Content-Type": ["application/json"] },
|
|
358
|
+
statusCode: 200,
|
|
359
|
+
statusText: "OK",
|
|
360
|
+
body: "Migrations successfully executed",
|
|
361
|
+
};
|
|
362
|
+
};
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### 5. Running Queries
|
|
366
|
+
|
|
367
|
+
Once the migrations are applied, you can start working with the ORM:
|
|
368
|
+
|
|
369
|
+
```js
|
|
370
|
+
import Entities from "./entities";
|
|
371
|
+
import ForgeSQL from "forge-sql-orm";
|
|
372
|
+
import { UsersSchema, Users } from "./entities/Users";
|
|
373
|
+
|
|
374
|
+
const forgeSQL = new ForgeSQL(ENTITIES);
|
|
375
|
+
|
|
376
|
+
// Insert Data
|
|
377
|
+
const user = new Users();
|
|
378
|
+
user.name = "John Doe";
|
|
379
|
+
const userId = await forgeSQL.crud().insert(UsersSchema, [user]);
|
|
380
|
+
console.log("Inserted User ID:", userId);
|
|
381
|
+
|
|
382
|
+
// Fetch Users
|
|
383
|
+
const users = await forgeSQL.fetch().executeSchemaSQL("SELECT * FROM users", UsersSchema);
|
|
384
|
+
console.log(users);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### 6. Updating Database Schema
|
|
388
|
+
|
|
389
|
+
If you modify the database schema, you can generate an update migration:
|
|
390
|
+
|
|
391
|
+
Modify the schema in DbSchema
|
|
392
|
+

|
|
393
|
+
or manually run:
|
|
394
|
+
|
|
395
|
+
```sh
|
|
396
|
+
ALTER TABLE `users` ADD email VARCHAR(255);
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Then, generate a new migration:
|
|
400
|
+
|
|
401
|
+
```sh
|
|
402
|
+
npx forge-sql-orm migrations:update --dbName testDb --entitiesPath ./database/entities --output ./database/migration
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Generated migration:
|
|
406
|
+
|
|
407
|
+
```js
|
|
408
|
+
import { MigrationRunner } from "@forge/sql/out/migration";
|
|
409
|
+
|
|
410
|
+
export default (migrationRunner: MigrationRunner): MigrationRunner => {
|
|
411
|
+
return migrationRunner.enqueue("v2_MIGRATION0", "alter table `users` add `email` varchar(255) null");
|
|
412
|
+
};
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### 7. Updating Entities
|
|
416
|
+
|
|
417
|
+
After applying the migration, update your entity models:
|
|
418
|
+
|
|
419
|
+
```sh
|
|
420
|
+
npx forge-sql-orm generate:model --dbName testDb --output ./database/entities
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Updated Users Model and Schema:
|
|
424
|
+
|
|
425
|
+
```js
|
|
426
|
+
import { EntitySchema } from 'forge-sql-orm';
|
|
427
|
+
|
|
428
|
+
export class Users {
|
|
429
|
+
id!: number;
|
|
430
|
+
name?: string;
|
|
431
|
+
email?: string;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export const UsersSchema = new EntitySchema({
|
|
435
|
+
class: Users,
|
|
436
|
+
properties: {
|
|
437
|
+
id: { primary: true, type: 'integer', unsigned: false },
|
|
438
|
+
name: { type: 'string', length: 200, nullable: true },
|
|
439
|
+
email: { type: 'string', nullable: true },
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## Complex Queries
|
|
448
|
+
|
|
449
|
+
### INNER JOIN Example
|
|
450
|
+
|
|
451
|
+
Using **ForgeSQLORM**, you can perform complex queries with joins.
|
|
452
|
+
For example, fetching **users and their purchased products**:
|
|
453
|
+
|
|
454
|
+
```ts
|
|
455
|
+
import ForgeSQL, { EntitySchema } from "forge-sql-orm";
|
|
456
|
+
import { Orders } from "./entities/Orders";
|
|
457
|
+
import { Users } from "./entities/Users";
|
|
458
|
+
import ENTITIES from "./entities";
|
|
459
|
+
|
|
460
|
+
const forgeSQL = new ForgeSQL(ENTITIES);
|
|
461
|
+
|
|
462
|
+
// Define schema for join result
|
|
463
|
+
class InnerJoinResult {
|
|
464
|
+
name!: string;
|
|
465
|
+
product!: string;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export const innerJoinSchema = new EntitySchema<InnerJoinResult>({
|
|
469
|
+
class: InnerJoinResult,
|
|
470
|
+
properties: {
|
|
471
|
+
name: { type: "string", fieldName: "name" },
|
|
472
|
+
product: { type: "string", fieldName: "product" },
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
innerJoinSchema.init();
|
|
476
|
+
|
|
477
|
+
// Execute query
|
|
478
|
+
const query = forgeSQL
|
|
479
|
+
.createQueryBuilder(Orders, "order")
|
|
480
|
+
.limit(10)
|
|
481
|
+
.offset(0)
|
|
482
|
+
.innerJoin("user", "user")
|
|
483
|
+
.select(["user.name", "order.product"])
|
|
484
|
+
.getFormattedQuery();
|
|
485
|
+
|
|
486
|
+
const results = await forgeSQL.fetch().executeSchemaSQL(query, innerJoinSchema);
|
|
487
|
+
console.log(results);
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## Usage with MikroORM Generator
|
|
493
|
+
|
|
494
|
+
If you prefer to use MikroORM's default entity generator, configure **package.json** as follows:
|
|
495
|
+
|
|
496
|
+
```json
|
|
497
|
+
{
|
|
498
|
+
"dependencies": {
|
|
499
|
+
"@mikro-orm/core": "npm:forge-sql-orm",
|
|
500
|
+
"@mikro-orm/mysql": "npm:forge-sql-orm"
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Then manually import your entities:
|
|
506
|
+
|
|
507
|
+
```ts
|
|
508
|
+
import { UserEntity, TaskEntity } from "./entities";
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## The CLI provides commands to generate models and manage migrations.
|
|
514
|
+
|
|
515
|
+
🔹 Available Commands
|
|
516
|
+
|
|
517
|
+
```sh
|
|
518
|
+
$ npx forge-sql-orm --help
|
|
519
|
+
|
|
520
|
+
Usage: forge-sql-orm [options] [command]
|
|
521
|
+
|
|
522
|
+
Options:
|
|
523
|
+
-V, --version output the version number
|
|
524
|
+
-h, --help display help for command
|
|
525
|
+
|
|
526
|
+
Commands:
|
|
527
|
+
generate:model [options] Generate MikroORM models from the database.
|
|
528
|
+
migrations:create [options] Generate an initial migration for the entire database.
|
|
529
|
+
migrations:update [options] Generate a migration to update the database schema.
|
|
530
|
+
patch:mikroorm Patch MikroORM and Knex dependencies to work properly with Forge
|
|
531
|
+
help [command] display help for command
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
📌 Entity Generation
|
|
535
|
+
|
|
536
|
+
```sh
|
|
537
|
+
npx forge-sql-orm generate:model --host localhost --port 3306 --user root --password secret --dbName mydb --output ./src/database/entities
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
This command will:
|
|
541
|
+
|
|
542
|
+
- Connect to mydb on localhost:3306
|
|
543
|
+
- Generate MikroORM entity classes
|
|
544
|
+
- Save them in ./src/database/entities
|
|
545
|
+
- Create an index.ts file with all entities
|
|
546
|
+
|
|
547
|
+
📌 Database Migrations
|
|
548
|
+
|
|
549
|
+
```sh
|
|
550
|
+
npx forge-sql-orm migrations:create --host localhost --port 3306 --user root --password secret --dbName mydb --output ./src/database/migration --entitiesPath ./src/database/entities
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
This command will:
|
|
554
|
+
|
|
555
|
+
- Create initial migration from all detected entities
|
|
556
|
+
- Save migration files in ./src/database/migration
|
|
557
|
+
- Create migrationCount.ts to track versions
|
|
558
|
+
- Create index.ts for automatic migration execution
|
|
559
|
+
|
|
560
|
+
📌 Update Schema Migration
|
|
561
|
+
|
|
562
|
+
```sh
|
|
563
|
+
npx forge-sql-orm migrations:update --host localhost --port 3306 --user root --password secret --dbName mydb --output ./src/database/migration --entitiesPath ./src/database/entities
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
This command will:
|
|
567
|
+
|
|
568
|
+
Detect schema changes (new tables, columns, indexes)
|
|
569
|
+
|
|
570
|
+
- Generate only required migrations
|
|
571
|
+
- Increment migrationCount.ts
|
|
572
|
+
- Update index.ts to include new migrations
|
|
573
|
+
|
|
574
|
+
📌 Using the patch:mikroorm Command
|
|
575
|
+
If needed, you can manually apply the patch at any time using:
|
|
576
|
+
|
|
577
|
+
```sh
|
|
578
|
+
npx forge-sql-orm patch:mikroorm
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
This command:
|
|
582
|
+
|
|
583
|
+
- Removes unsupported database dialects (e.g., PostgreSQL, SQLite).
|
|
584
|
+
- Fixes dynamic imports to work in Forge.
|
|
585
|
+
- Ensures Knex and MikroORM work properly inside Forge.
|
|
586
|
+
|
|
587
|
+
📌 Manual Migration Execution
|
|
588
|
+
To manually execute migrations in your application:
|
|
589
|
+
|
|
590
|
+
```js
|
|
591
|
+
import migrationRunner from "./src/database/migration";
|
|
592
|
+
import { MigrationRunner } from "@forge/sql/out/migration";
|
|
593
|
+
|
|
594
|
+
const runner = new MigrationRunner();
|
|
595
|
+
await migrationRunner(runner);
|
|
596
|
+
await runner.run(); // ✅ Apply migrations
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
🔧 Configuration
|
|
600
|
+
You can define database credentials using:
|
|
601
|
+
|
|
602
|
+
1. Command-line arguments: --host, --port, etc.
|
|
603
|
+
2️. Environment variables:
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
export FORGE_SQL_ORM_HOST=localhost
|
|
607
|
+
export FORGE_SQL_ORM_PORT=3306
|
|
608
|
+
export FORGE_SQL_ORM_USER=root
|
|
609
|
+
export FORGE_SQL_ORM_PASSWORD=secret
|
|
610
|
+
export FORGE_SQL_ORM_DBNAME=mydb
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
3. use .env
|
|
614
|
+
|
|
615
|
+
```sh
|
|
616
|
+
FORGE_SQL_ORM_HOST=localhost
|
|
617
|
+
FORGE_SQL_ORM_PORT=3306
|
|
618
|
+
FORGE_SQL_ORM_USER=root
|
|
619
|
+
FORGE_SQL_ORM_PASSWORD=secret
|
|
620
|
+
FORGE_SQL_ORM_DBNAME=mydb
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
4. Interactive prompts (if missing parameters)
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
📜 **License**
|
|
628
|
+
This project is licensed under the **MIT License**.
|
|
629
|
+
Feel free to use it for commercial and personal projects.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { EntitySchema } from "..";
|
|
2
|
+
import { CRUDForgeSQL, ForgeSqlOperation } from "./ForgeSQLQueryBuilder";
|
|
3
|
+
export declare class ForgeSQLCrudOperations implements CRUDForgeSQL {
|
|
4
|
+
private readonly forgeOperations;
|
|
5
|
+
constructor(forgeSqlOperations: ForgeSqlOperation);
|
|
6
|
+
/**
|
|
7
|
+
* Generates an SQL insert query with values.
|
|
8
|
+
* @param schema - The entity schema.
|
|
9
|
+
* @param models - The list of entities to insert.
|
|
10
|
+
* @param updateIfExists - Whether to update the row if it already exists.
|
|
11
|
+
* @returns An object containing the SQL query, fields, and values.
|
|
12
|
+
*/
|
|
13
|
+
private generateInsertScript;
|
|
14
|
+
/**
|
|
15
|
+
* Inserts records into the database.
|
|
16
|
+
* @param schema - The entity schema.
|
|
17
|
+
* @param models - The list of entities to insert.
|
|
18
|
+
* @param updateIfExists - Whether to update the row if it already exists.
|
|
19
|
+
* @returns The ID of the inserted row.
|
|
20
|
+
*/
|
|
21
|
+
insert<T extends object>(schema: EntitySchema<T>, models: T[], updateIfExists?: boolean): Promise<number>;
|
|
22
|
+
/**
|
|
23
|
+
* Retrieves the primary keys for the given entity schema.
|
|
24
|
+
* @param schema - The entity schema.
|
|
25
|
+
* @returns An array of primary key properties.
|
|
26
|
+
* @throws If no primary keys are found.
|
|
27
|
+
*/
|
|
28
|
+
private getPrimaryKeys;
|
|
29
|
+
/**
|
|
30
|
+
* Deletes a record by its ID.
|
|
31
|
+
* @param id - The ID of the record to delete.
|
|
32
|
+
* @param schema - The entity schema.
|
|
33
|
+
* @returns The number of rows affected.
|
|
34
|
+
* @throws If the entity has more than one primary key.
|
|
35
|
+
*/
|
|
36
|
+
deleteById<T extends object>(id: unknown, schema: EntitySchema<T>): Promise<number>;
|
|
37
|
+
/**
|
|
38
|
+
* Updates a record by its ID.
|
|
39
|
+
* @param entity - The entity with updated values.
|
|
40
|
+
* @param schema - The entity schema.
|
|
41
|
+
* @throws If the primary key value is missing in the entity.
|
|
42
|
+
*/
|
|
43
|
+
updateById<T extends object>(entity: T, schema: EntitySchema<T>): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=ForgeSQLCrudOperations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ForgeSQLCrudOperations.d.ts","sourceRoot":"","sources":["../../src/core/ForgeSQLCrudOperations.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,YAAY,EAAE,MAAM,IAAI,CAAC;AAGlD,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEzE,qBAAa,sBAAuB,YAAW,YAAY;IACzD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoB;gBAExC,kBAAkB,EAAE,iBAAiB;IAIjD;;;;;;OAMG;YACW,oBAAoB;IA6DlC;;;;;;OAMG;IACG,MAAM,CAAC,CAAC,SAAS,MAAM,EAC3B,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,CAAC,EAAE,EACX,cAAc,GAAE,OAAe,GAC9B,OAAO,CAAC,MAAM,CAAC;IAWlB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAQtB;;;;;;OAMG;IACG,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAkBzF;;;;;OAKG;IACG,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAetF"}
|