stabilize-orm 1.2.0 → 1.3.2
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/CHANGELOG.md +33 -5
- package/README.md +210 -129
- package/client.ts +7 -82
- package/hooks.ts +65 -22
- package/index.ts +7 -39
- package/migrations.ts +23 -38
- package/model.ts +177 -0
- package/package.json +3 -9
- package/query-builder.ts +27 -4
- package/repository.ts +71 -66
- package/types.ts +1 -0
- package/decorators.ts +0 -170
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
- Further features and improvements coming soon.
|
|
8
|
+
|
|
9
|
+
## [1.3.0] - 2025-10-18
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Introduced programmatic `defineModel` API for model definitions, eliminating the need for decorators (`model.ts`).
|
|
13
|
+
- Added `MetadataStorage` class to manage model configurations without `reflect-metadata`.
|
|
14
|
+
- Added support for defining lifecycle hooks in `ModelConfig` or as class methods (`hooks.ts`).
|
|
15
|
+
- Added `example.ts` to demonstrate the new programmatic API usage.
|
|
16
|
+
- Extended `ModelConfig` interface to support columns, relations, hooks, versioning, and soft deletes (`types.ts`).
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Replaced decorator-based model definitions with `defineModel` API, removing dependency on `reflect-metadata` and TypeScript experimental features (`experimentalDecorators`, `emitDecoratorMetadata`).
|
|
20
|
+
- Updated `stabilize.ts` to export `defineModel` and remove `reflect-metadata` import.
|
|
21
|
+
- Modified `repository.ts` to use `MetadataStorage` for table names, columns, relations, validators, and soft delete fields.
|
|
22
|
+
- Rewrote `hooks.ts` to support hooks via `ModelConfig` and class methods, integrated with `MetadataStorage`.
|
|
23
|
+
- Updated `migrations.ts` to generate schemas using `MetadataStorage` instead of decorator metadata.
|
|
24
|
+
- Revised `types.ts` to remove decorator-related types and add `ModelConfig`, `ColumnConfig`, and `RelationConfig` interfaces.
|
|
25
|
+
- Updated `README.md` to reflect the new programmatic API, remove decorator references, and update examples.
|
|
26
|
+
- Ensured compatibility with `verbatimModuleSyntax` by using `export type` for type exports in `stabilize.ts`.
|
|
27
|
+
|
|
28
|
+
### Removed
|
|
29
|
+
- Deleted `decorators.ts` as decorators are no longer used.
|
|
30
|
+
- Removed dependency on `reflect-metadata` from the project.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- Fixed TypeScript type errors in `repository.ts` for `columns` and `relations` by mapping `MetadataStorage` outputs to match expected types.
|
|
34
|
+
- Corrected `runHooks` in `repository.ts` to call `hook.callback(entity)` instead of `hook()`.
|
|
35
|
+
- Fixed TypeScript `verbatimModuleSyntax` error in `stabilize.ts` by separating type and value exports.
|
|
36
|
+
|
|
5
37
|
## [1.1.2] - 2025-10-14
|
|
6
38
|
|
|
7
39
|
### Added
|
|
@@ -16,8 +48,4 @@ All notable changes to this project will be documented in this file.
|
|
|
16
48
|
- Enhanced documentation for open source best practices.
|
|
17
49
|
|
|
18
50
|
### Fixed
|
|
19
|
-
- Various bug fixes for connection handling and retry logic.
|
|
20
|
-
|
|
21
|
-
## [Unreleased]
|
|
22
|
-
|
|
23
|
-
- Further features and improvements coming soon.
|
|
51
|
+
- Various bug fixes for connection handling and retry logic.
|
package/README.md
CHANGED
|
@@ -1,43 +1,44 @@
|
|
|
1
1
|
# Stabilize ORM
|
|
2
2
|
|
|
3
|
-
_A Modern, Type-Safe, and Expressive ORM for
|
|
3
|
+
_A Modern, Type-Safe, and Expressive ORM for Bun_
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
**Stabilize** is a lightweight, feature-rich ORM designed for performance and developer experience. It provides a unified, database-agnostic API for **PostgreSQL**, **MySQL**, and **SQLite**. Powered by a robust query builder,
|
|
7
|
+
**Stabilize** is a lightweight, feature-rich ORM designed for performance and developer experience. It provides a unified, database-agnostic API for **PostgreSQL**, **MySQL**, and **SQLite**. Powered by a robust query builder, programmatic model definitions, automatic versioning, and a full-featured command-line interface, Stabilize is built to scale with your app.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## 🚀 Features
|
|
12
12
|
|
|
13
13
|
- **Unified API**: Write once, run on PostgreSQL, MySQL, or SQLite.
|
|
14
|
-
- **
|
|
15
|
-
- **Full-Featured CLI**: Generate models, manage migrations, seed data, and reset your database from the command line.
|
|
14
|
+
- **Programmatic Model Definitions**: Define models and columns using the `defineModel` API with the `DataTypes` enum for database-agnostic schemas.
|
|
15
|
+
- **Full-Featured CLI**: Generate models, manage migrations, seed data, and reset your database from the command line with [stabilize-cli](https://github.com/ElectronSz/stabilize-cli).
|
|
16
16
|
- **Automatic Migrations**: Generate database-specific SQL schemas directly from your model definitions.
|
|
17
|
-
- **Versioned Models & Time-Travel**:
|
|
17
|
+
- **Versioned Models & Time-Travel**: Enable versioning in your model configuration for automatic history tables and snapshot queries.
|
|
18
18
|
- **Retry Logic**: Automatic exponential backoff for database queries to handle transient connection issues.
|
|
19
19
|
- **Connection Pooling**: Efficient connection management for PostgreSQL and MySQL.
|
|
20
20
|
- **Transactional Integrity**: Built-in support for atomic transactions with automatic rollback on failure.
|
|
21
21
|
- **Advanced Query Builder**: Fluent, chainable API for building complex queries, including joins, filters, ordering, and pagination.
|
|
22
|
-
- **Model Relationships**:
|
|
23
|
-
- **Soft Deletes**:
|
|
24
|
-
- **Lifecycle Hooks**:
|
|
22
|
+
- **Model Relationships**: Define `OneToOne`, `ManyToOne`, `OneToMany`, and `ManyToMany` relationships in the model configuration.
|
|
23
|
+
- **Soft Deletes**: Enable soft deletes in the model configuration for transparent "deleted" flags and safe row removal.
|
|
24
|
+
- **Lifecycle Hooks**: Define hooks in the model configuration or as class methods for lifecycle events like `beforeCreate`, `afterUpdate`, etc.
|
|
25
25
|
- **Pluggable Logging**: Includes a robust `ConsoleLogger` with support for file-based, rotating logs.
|
|
26
26
|
- **Custom Errors**: `StabilizeError` provides clear, consistent error handling.
|
|
27
27
|
- **Caching Layer**: Optional Redis-backed caching with `cache-aside` and `write-through` strategies.
|
|
28
|
+
- **Custom Query Scopes**: Define reusable query conditions (scopes) in models for simplified, reusable filtering logic.
|
|
28
29
|
|
|
29
30
|
---
|
|
30
31
|
|
|
31
32
|
## 📦 Installation
|
|
32
33
|
|
|
33
|
-
Stabilize ORM requires a modern JavaScript runtime (Bun v1.
|
|
34
|
+
Stabilize ORM requires a modern JavaScript runtime (Bun v1.3+).
|
|
34
35
|
|
|
35
36
|
```bash
|
|
36
37
|
# Using Bun
|
|
37
|
-
bun add stabilize-orm
|
|
38
|
+
bun add stabilize-orm
|
|
38
39
|
|
|
39
40
|
# Using npm
|
|
40
|
-
npm install stabilize-orm
|
|
41
|
+
npm install stabilize-orm
|
|
41
42
|
```
|
|
42
43
|
|
|
43
44
|
---
|
|
@@ -56,14 +57,14 @@ npm install stabilize-orm reflect-metadata
|
|
|
56
57
|
|
|
57
58
|
## ⚙️ Configuration
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
Create a database configuration file.
|
|
60
61
|
|
|
61
62
|
```typescript
|
|
62
63
|
// config/database.ts
|
|
63
64
|
import { DBType, type DBConfig } from "stabilize-orm";
|
|
64
65
|
|
|
65
66
|
const dbConfig: DBConfig = {
|
|
66
|
-
type: DBType.Postgres,
|
|
67
|
+
type: DBType.Postgres,
|
|
67
68
|
connectionString: process.env.DATABASE_URL || "postgres://user:password@localhost:5432/mydb",
|
|
68
69
|
retryAttempts: 3,
|
|
69
70
|
retryDelay: 1000,
|
|
@@ -72,11 +73,10 @@ const dbConfig: DBConfig = {
|
|
|
72
73
|
export default dbConfig;
|
|
73
74
|
```
|
|
74
75
|
|
|
75
|
-
Next, create a central ORM instance
|
|
76
|
+
Next, create a central ORM instance for your application.
|
|
76
77
|
|
|
77
78
|
```typescript
|
|
78
79
|
// db.ts
|
|
79
|
-
import 'reflect-metadata';
|
|
80
80
|
import { Stabilize, type CacheConfig, type LoggerConfig, LogLevel } from "stabilize-orm";
|
|
81
81
|
import dbConfig from "./database";
|
|
82
82
|
|
|
@@ -88,7 +88,7 @@ const cacheConfig: CacheConfig = {
|
|
|
88
88
|
|
|
89
89
|
const loggerConfig: LoggerConfig = {
|
|
90
90
|
level: LogLevel.Info,
|
|
91
|
-
filePath:
|
|
91
|
+
filePath: "logs/stabilize.log",
|
|
92
92
|
maxFileSize: 5 * 1024 * 1024, // 5MB
|
|
93
93
|
maxFiles: 3,
|
|
94
94
|
};
|
|
@@ -100,80 +100,95 @@ export const orm = new Stabilize(dbConfig, cacheConfig, loggerConfig);
|
|
|
100
100
|
|
|
101
101
|
## 🏗️ Models & Relationships
|
|
102
102
|
|
|
103
|
-
Define your tables as classes using
|
|
103
|
+
Define your tables as classes using the `defineModel` function. The `DataTypes` enum ensures database-agnostic schemas.
|
|
104
104
|
|
|
105
105
|
### Example: Users and Roles (Many-to-Many) with Versioning
|
|
106
106
|
|
|
107
107
|
```typescript
|
|
108
108
|
// models/User.ts
|
|
109
|
-
import
|
|
110
|
-
import {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
109
|
+
import { defineModel, DataTypes, RelationType } from "stabilize-orm";
|
|
110
|
+
import { UserRole } from "./UserRole";
|
|
111
|
+
|
|
112
|
+
const User = defineModel({
|
|
113
|
+
tableName: "users",
|
|
114
|
+
versioned: true,
|
|
115
|
+
columns: {
|
|
116
|
+
id: { type: DataTypes.Integer, required: true },
|
|
117
|
+
email: { type: DataTypes.String, length: 100, required: true, unique: true },
|
|
118
|
+
},
|
|
119
|
+
relations: [
|
|
120
|
+
{
|
|
121
|
+
type: RelationType.OneToMany,
|
|
122
|
+
target: () => UserRole,
|
|
123
|
+
property: "roles",
|
|
124
|
+
foreignKey: "userId",
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
hooks: {
|
|
128
|
+
beforeCreate: (entity) => console.log(`Creating user: ${entity.email}`),
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Add a hook as a class method
|
|
133
|
+
User.prototype.afterCreate = async function () {
|
|
134
|
+
console.log(`Created user with ID: ${this.id}`);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export { User };
|
|
126
138
|
```
|
|
127
139
|
|
|
128
140
|
```typescript
|
|
129
141
|
// models/Role.ts
|
|
130
|
-
import
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
142
|
+
import { defineModel, DataTypes } from "stabilize-orm";
|
|
143
|
+
|
|
144
|
+
const Role = defineModel({
|
|
145
|
+
tableName: "roles",
|
|
146
|
+
columns: {
|
|
147
|
+
id: { type: DataTypes.Integer, required: true },
|
|
148
|
+
name: { type: DataTypes.String, length: 50, required: true, unique: true },
|
|
149
|
+
},
|
|
150
|
+
});
|
|
137
151
|
|
|
138
|
-
|
|
139
|
-
@Required() @Unique()
|
|
140
|
-
name!: string;
|
|
141
|
-
}
|
|
152
|
+
export { Role };
|
|
142
153
|
```
|
|
143
154
|
|
|
144
155
|
```typescript
|
|
145
156
|
// models/UserRole.ts
|
|
146
|
-
import
|
|
147
|
-
import {
|
|
148
|
-
import {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
157
|
+
import { defineModel, DataTypes, RelationType } from "stabilize-orm";
|
|
158
|
+
import { User } from "./User";
|
|
159
|
+
import { Role } from "./Role";
|
|
160
|
+
|
|
161
|
+
const UserRole = defineModel({
|
|
162
|
+
tableName: "user_roles",
|
|
163
|
+
columns: {
|
|
164
|
+
id: { type: DataTypes.Integer, required: true },
|
|
165
|
+
userId: { type: DataTypes.Integer, required: true, index: "idx_user_id" },
|
|
166
|
+
roleId: { type: DataTypes.Integer, required: true, index: "idx_role_id" },
|
|
167
|
+
},
|
|
168
|
+
relations: [
|
|
169
|
+
{
|
|
170
|
+
type: RelationType.ManyToOne,
|
|
171
|
+
target: () => User,
|
|
172
|
+
property: "user",
|
|
173
|
+
foreignKey: "userId",
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: RelationType.ManyToOne,
|
|
177
|
+
target: () => Role,
|
|
178
|
+
property: "role",
|
|
179
|
+
foreignKey: "roleId",
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
});
|
|
166
183
|
|
|
167
|
-
|
|
168
|
-
role?: Role;
|
|
169
|
-
}
|
|
184
|
+
export { UserRole };
|
|
170
185
|
```
|
|
171
186
|
|
|
172
187
|
---
|
|
173
188
|
|
|
174
189
|
## ⏳ Versioning & Auditing
|
|
175
190
|
|
|
176
|
-
Enable automatic history tracking and time-travel queries by
|
|
191
|
+
Enable automatic history tracking and time-travel queries by setting `versioned: true` in your model configuration.
|
|
177
192
|
|
|
178
193
|
- Each change is recorded in a `<table>_history` table with version, operation, and audit columns.
|
|
179
194
|
- Supports snapshot queries, rollbacks, audits, and time-travel.
|
|
@@ -181,15 +196,16 @@ Enable automatic history tracking and time-travel queries by adding `@Versioned(
|
|
|
181
196
|
### **Versioning Example**
|
|
182
197
|
|
|
183
198
|
```typescript
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
199
|
+
import { defineModel, DataTypes } from "stabilize-orm";
|
|
200
|
+
|
|
201
|
+
const User = defineModel({
|
|
202
|
+
tableName: "users",
|
|
203
|
+
versioned: true,
|
|
204
|
+
columns: {
|
|
205
|
+
id: { type: DataTypes.Integer, required: true },
|
|
206
|
+
name: { type: DataTypes.String, length: 100 },
|
|
207
|
+
},
|
|
208
|
+
});
|
|
193
209
|
|
|
194
210
|
// --- Using versioning features:
|
|
195
211
|
|
|
@@ -199,7 +215,7 @@ const userRepository = orm.getRepository(User);
|
|
|
199
215
|
await userRepository.rollback(1, 3); // roll back user with id=1 to version 3
|
|
200
216
|
|
|
201
217
|
// Get a snapshot as of a specific date
|
|
202
|
-
const userAsOf = await userRepository.asOf(1, new Date(
|
|
218
|
+
const userAsOf = await userRepository.asOf(1, new Date("2025-01-01T00:00:00Z"));
|
|
203
219
|
console.log(userAsOf);
|
|
204
220
|
|
|
205
221
|
// View full version history
|
|
@@ -211,95 +227,92 @@ console.log(history);
|
|
|
211
227
|
|
|
212
228
|
## 🔄 Model Lifecycle Hooks
|
|
213
229
|
|
|
214
|
-
Stabilize ORM supports lifecycle hooks
|
|
215
|
-
You can run logic before/after create, update, delete, or save.
|
|
230
|
+
Stabilize ORM supports lifecycle hooks defined in the model configuration or as class methods. You can run logic before/after create, update, delete, or save.
|
|
216
231
|
|
|
217
232
|
### **Hooks Example**
|
|
218
233
|
|
|
219
234
|
```typescript
|
|
220
|
-
import {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
235
|
+
import { defineModel, DataTypes } from "stabilize-orm";
|
|
236
|
+
|
|
237
|
+
const User = defineModel({
|
|
238
|
+
tableName: "users",
|
|
239
|
+
columns: {
|
|
240
|
+
id: { type: DataTypes.Integer, required: true },
|
|
241
|
+
name: { type: DataTypes.String, length: 100 },
|
|
242
|
+
createdAt: { type: DataTypes.DateTime },
|
|
243
|
+
updatedAt: { type: DataTypes.DateTime },
|
|
244
|
+
},
|
|
245
|
+
hooks: {
|
|
246
|
+
beforeCreate: (entity) => {
|
|
247
|
+
entity.createdAt = new Date();
|
|
248
|
+
},
|
|
249
|
+
beforeUpdate: (entity) => {
|
|
250
|
+
entity.updatedAt = new Date();
|
|
251
|
+
},
|
|
252
|
+
afterCreate: (entity) => {
|
|
253
|
+
console.log(`User created: ${entity.name}`);
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
});
|
|
240
257
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
258
|
+
// Add a hook as a class method
|
|
259
|
+
User.prototype.afterUpdate = async function () {
|
|
260
|
+
console.log(`Updated user: ${this.name}`);
|
|
261
|
+
};
|
|
245
262
|
|
|
246
|
-
|
|
247
|
-
logCreate() {
|
|
248
|
-
console.log(`User created: ${this.name}`);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
263
|
+
export { User };
|
|
251
264
|
```
|
|
252
265
|
|
|
253
|
-
|
|
266
|
+
Supported hooks: `beforeCreate`, `afterCreate`, `beforeUpdate`, `afterUpdate`, `beforeDelete`, `afterDelete`, `beforeSave`, `afterSave`.
|
|
254
267
|
|
|
255
268
|
---
|
|
256
269
|
|
|
257
270
|
## 💻 Command-Line Interface (CLI)
|
|
258
271
|
|
|
259
|
-
Stabilize includes a powerful CLI for managing your workflow.
|
|
272
|
+
Stabilize includes a powerful CLI for managing your workflow. See: [stabilize-cli on GitHub](https://github.com/ElectronSz/stabilize-cli)
|
|
260
273
|
|
|
261
274
|
### Generating Files
|
|
262
275
|
|
|
263
276
|
- **Generate a model**:
|
|
264
277
|
```bash
|
|
265
|
-
|
|
278
|
+
stabilize-cli generate model Product
|
|
266
279
|
```
|
|
267
280
|
|
|
268
281
|
- **Generate a migration from a model**:
|
|
269
282
|
```bash
|
|
270
|
-
|
|
283
|
+
stabilize-cli generate migration User
|
|
271
284
|
```
|
|
272
285
|
|
|
273
286
|
- **Generate a seed file**:
|
|
274
287
|
```bash
|
|
275
|
-
|
|
288
|
+
stabilize-cli generate seed InitialRoles
|
|
276
289
|
```
|
|
277
290
|
|
|
278
291
|
### Database & Migration Management
|
|
279
292
|
|
|
280
293
|
- **Run all pending migrations**:
|
|
281
294
|
```bash
|
|
282
|
-
|
|
295
|
+
stabilize-cli migrate
|
|
283
296
|
```
|
|
284
297
|
|
|
285
298
|
- **Roll back the last migration**:
|
|
286
299
|
```bash
|
|
287
|
-
|
|
300
|
+
stabilize-cli migrate:rollback
|
|
288
301
|
```
|
|
289
302
|
|
|
290
303
|
- **Run all pending seeds (in dependency order)**:
|
|
291
304
|
```bash
|
|
292
|
-
|
|
305
|
+
stabilize-cli seed
|
|
293
306
|
```
|
|
294
307
|
|
|
295
308
|
- **Check the status of migrations and seeds**:
|
|
296
309
|
```bash
|
|
297
|
-
|
|
310
|
+
stabilize-cli status
|
|
298
311
|
```
|
|
299
312
|
|
|
300
313
|
- **Reset the database (drop, migrate, seed)**:
|
|
301
314
|
```bash
|
|
302
|
-
|
|
315
|
+
stabilize-cli db:reset
|
|
303
316
|
```
|
|
304
317
|
|
|
305
318
|
---
|
|
@@ -309,21 +322,22 @@ Stabilize includes a powerful CLI for managing your workflow.
|
|
|
309
322
|
### Basic CRUD with Repositories
|
|
310
323
|
|
|
311
324
|
```typescript
|
|
312
|
-
import { orm } from
|
|
313
|
-
import { User } from
|
|
325
|
+
import { orm } from "./db";
|
|
326
|
+
import { User } from "./models/User";
|
|
314
327
|
|
|
315
328
|
const userRepository = orm.getRepository(User);
|
|
316
329
|
|
|
317
|
-
const newUser = await userRepository.create({ email:
|
|
330
|
+
const newUser = await userRepository.create({ email: "lwazicd@icloud.com" });
|
|
318
331
|
const foundUser = await userRepository.findOne(newUser.id);
|
|
319
|
-
const updatedUser = await userRepository.update(newUser.id, { email:
|
|
332
|
+
const updatedUser = await userRepository.update(newUser.id, { email: "admin@offbytesecure.com" });
|
|
320
333
|
await userRepository.delete(newUser.id);
|
|
321
334
|
```
|
|
322
335
|
|
|
323
336
|
### Advanced Queries with the Query Builder
|
|
324
337
|
|
|
325
338
|
```typescript
|
|
326
|
-
const activeAdmins = await orm
|
|
339
|
+
const activeAdmins = await orm
|
|
340
|
+
.getRepository(UserRole)
|
|
327
341
|
.find()
|
|
328
342
|
.join("users", "user_roles.user_id = users.id")
|
|
329
343
|
.join("roles", "user_roles.role_id = roles.id")
|
|
@@ -345,18 +359,85 @@ console.log(activeAdmins);
|
|
|
345
359
|
orderBy(clause: string): QueryBuilder<User>;
|
|
346
360
|
limit(limit: number): QueryBuilder<User>;
|
|
347
361
|
offset(offset: number): QueryBuilder<User>;
|
|
362
|
+
scope(name: string, ...args: any[]): QueryBuilder<User>;
|
|
348
363
|
build(): { query: string; params: any[] };
|
|
349
364
|
execute(client?: DBClient, cache?: Cache, cacheKey?: string): Promise<User[]>;
|
|
350
365
|
}
|
|
351
366
|
```
|
|
352
367
|
|
|
368
|
+
### Custom Query Scopes
|
|
369
|
+
|
|
370
|
+
Define reusable query conditions (scopes) in your model configuration to simplify and reuse common filtering logic. Scopes are applied via the `scope` method on `Repository` or `QueryBuilder`, allowing you to chain them with other query operations.
|
|
371
|
+
|
|
372
|
+
#### **Scopes Example**
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
import { defineModel, DataTypes } from "stabilize-orm";
|
|
376
|
+
import { orm } from "./db";
|
|
377
|
+
|
|
378
|
+
const User = defineModel({
|
|
379
|
+
tableName: "users",
|
|
380
|
+
columns: {
|
|
381
|
+
id: { type: DataTypes.Integer, required: true },
|
|
382
|
+
email: { type: DataTypes.String, length: 100, required: true },
|
|
383
|
+
isActive: { type: DataTypes.Boolean, required: true },
|
|
384
|
+
createdAt: { type: DataTypes.DateTime },
|
|
385
|
+
},
|
|
386
|
+
scopes: {
|
|
387
|
+
active: (qb) => qb.where("isActive = ?", true),
|
|
388
|
+
recent: (qb, days: number) => qb.where("createdAt >= ?", new Date(Date.now() - days * 24 * 60 * 60 * 1000)),
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
const userRepository = orm.getRepository(User);
|
|
393
|
+
|
|
394
|
+
// Fetch active users
|
|
395
|
+
const activeUsers = await userRepository.scope("active").execute();
|
|
396
|
+
|
|
397
|
+
// Fetch users created in the last 7 days
|
|
398
|
+
const recentUsers = await userRepository.scope("recent", 7).execute();
|
|
399
|
+
|
|
400
|
+
// Combine scopes with other query operations
|
|
401
|
+
const recentActiveUsers = await userRepository
|
|
402
|
+
.scope("active")
|
|
403
|
+
.scope("recent", 7)
|
|
404
|
+
.orderBy("createdAt DESC")
|
|
405
|
+
.limit(10)
|
|
406
|
+
.execute();
|
|
407
|
+
|
|
408
|
+
console.log(recentActiveUsers);
|
|
409
|
+
```
|
|
410
|
+
|
|
353
411
|
---
|
|
354
412
|
|
|
355
413
|
## 🗑️ Soft Deletes
|
|
356
414
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
- Use `
|
|
415
|
+
Enable soft deletes by setting `softDelete: true` and marking a column (e.g., `deletedAt`) with `softDelete: true` in the model configuration.
|
|
416
|
+
|
|
417
|
+
- Use `repository.delete(id)` to mark an entity as deleted.
|
|
418
|
+
- Use `repository.recover(id)` to restore a soft-deleted entity.
|
|
419
|
+
- Queries automatically exclude soft-deleted rows unless specified otherwise.
|
|
420
|
+
|
|
421
|
+
### **Soft Delete Example**
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
import { defineModel, DataTypes } from "stabilize-orm";
|
|
425
|
+
|
|
426
|
+
const User = defineModel({
|
|
427
|
+
tableName: "users",
|
|
428
|
+
softDelete: true,
|
|
429
|
+
columns: {
|
|
430
|
+
id: { type: DataTypes.Integer, required: true },
|
|
431
|
+
email: { type: DataTypes.String, length: 100, required: true },
|
|
432
|
+
deletedAt: { type: DataTypes.DateTime, softDelete: true },
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
const userRepository = orm.getRepository(User);
|
|
437
|
+
await userRepository.create({ email: "lwazicd@icloud.com" });
|
|
438
|
+
await userRepository.delete(1); // Soft delete
|
|
439
|
+
await userRepository.recover(1); // Recover
|
|
440
|
+
```
|
|
360
441
|
|
|
361
442
|
---
|
|
362
443
|
|
|
@@ -416,6 +497,6 @@ Licensed under the MIT License. See [LICENSE.md](./LICENSE.md) for details.
|
|
|
416
497
|
|
|
417
498
|
Created with ❤️ by **ElectronSz**
|
|
418
499
|
<br/>
|
|
419
|
-
<em>File last updated: 2025-10-
|
|
500
|
+
<em>File last updated: 2025-10-19 10:24:00 SAST</em>
|
|
420
501
|
|
|
421
502
|
</div>
|