mongodb-dynamic-api 4.14.0 → 4.14.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/CHANGELOG.md +2 -0
- package/README.md +175 -816
- package/package.json +1 -1
- package/src/version.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/README.md
CHANGED
|
@@ -1,69 +1,39 @@
|
|
|
1
|
-
<
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
</
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
</
|
|
25
|
-
</div>
|
|
26
|
-
|
|
27
|
-
<div style="text-align: center; width: 100%;">
|
|
28
|
-
<div style="display: inline-block">
|
|
29
|
-
|
|
30
|
-
[](https://sonarcloud.io/dashboard?id=MikeDev75015_mongodb-dynamic-api)
|
|
31
|
-
[](https://sonarcloud.io/dashboard?id=MikeDev75015_mongodb-dynamic-api)
|
|
32
|
-
[](https://sonarcloud.io/dashboard?id=MikeDev75015_mongodb-dynamic-api)
|
|
33
|
-
[](https://sonarcloud.io/dashboard?id=MikeDev75015_mongodb-dynamic-api)
|
|
34
|
-
[](https://sonarcloud.io/dashboard?id=MikeDev75015_mongodb-dynamic-api)
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
|
|
38
|
-
<div style="text-align: center; width: 100%;">
|
|
39
|
-
<div style="display: inline-block">
|
|
40
|
-
|
|
41
|
-

|
|
42
|
-

|
|
43
|
-

|
|
44
|
-

|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">mongodb-dynamic-api</h1>
|
|
3
|
+
<p align="center">
|
|
4
|
+
A production-ready <strong>NestJS 11</strong> module that instantly generates fully typed REST APIs + WebSockets<br/>
|
|
5
|
+
for any MongoDB collection — zero boilerplate, enterprise features included.
|
|
6
|
+
</p>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://www.npmjs.com/package/mongodb-dynamic-api"><img src="https://img.shields.io/npm/v/mongodb-dynamic-api.svg" alt="NPM version"/></a>
|
|
11
|
+
<img src="https://img.shields.io/npm/l/mongodb-dynamic-api" alt="License"/>
|
|
12
|
+
<img src="https://img.shields.io/npm/dw/mongodb-dynamic-api" alt="Weekly downloads"/>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<img src="https://img.shields.io/github/checks-status/MikeDev75015/mongodb-dynamic-api/main" alt="CI"/>
|
|
17
|
+
<a href="https://app.circleci.com/pipelines/github/MikeDev75015/mongodb-dynamic-api"><img src="https://circleci.com/gh/MikeDev75015/mongodb-dynamic-api.svg?style=shield" alt="CircleCI"/></a>
|
|
18
|
+
<img src="https://img.shields.io/sonar/quality_gate/MikeDev75015_mongodb-dynamic-api?server=https%3A%2F%2Fsonarcloud.io" alt="Sonar Quality Gate"/>
|
|
19
|
+
<img src="https://img.shields.io/sonar/coverage/MikeDev75015_mongodb-dynamic-api?server=https%3A%2F%2Fsonarcloud.io" alt="Coverage"/>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<code>npm install --save mongodb-dynamic-api</code>
|
|
24
|
+
</p>
|
|
47
25
|
|
|
48
26
|
---
|
|
49
27
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
A powerful, production-ready NestJS module that automatically generates fully functional REST APIs with WebSocket support for MongoDB collections.
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
npm install --save mongodb-dynamic-api
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
> ## ⚠️ What's new in v4.0.0 — Breaking Changes
|
|
28
|
+
> [!WARNING]
|
|
29
|
+
> **v4 — Breaking changes.** Dual-token auth (`accessToken` + `refreshToken`), new default expiry (`expiresIn: '15m'`), 2 new endpoints (`/auth/refresh-token`, `/auth/logout`).
|
|
61
30
|
>
|
|
62
|
-
>
|
|
31
|
+
> <details>
|
|
32
|
+
> <summary>📋 Full migration guide (v3 → v4)</summary>
|
|
63
33
|
>
|
|
64
|
-
> ### 🔄 Dual-token authentication
|
|
65
|
-
> Login and register now return **`{ accessToken, refreshToken }`** instead of a single token.
|
|
66
|
-
> The `/auth/refresh-token` endpoint now requires the **refresh token
|
|
34
|
+
> ### 🔄 Dual-token authentication
|
|
35
|
+
> Login and register now return **`{ accessToken, refreshToken }`** instead of a single token.
|
|
36
|
+
> The `/auth/refresh-token` endpoint now requires the **refresh token**.
|
|
67
37
|
>
|
|
68
38
|
> ### ⏱️ New default expiration times
|
|
69
39
|
> | Token | v3 | v4 |
|
|
@@ -76,169 +46,167 @@ npm install --save mongodb-dynamic-api
|
|
|
76
46
|
> ### 🆕 Two new endpoints
|
|
77
47
|
> | Endpoint | Description |
|
|
78
48
|
> |---|---|
|
|
79
|
-
> | `POST /auth/refresh-token` | Get a new token pair using the refresh token
|
|
49
|
+
> | `POST /auth/refresh-token` | Get a new token pair using the refresh token |
|
|
80
50
|
> | `POST /auth/logout` | Invalidate the refresh token server-side (204 No Content) |
|
|
81
51
|
>
|
|
82
52
|
> ### 🔒 New options in `useAuth`
|
|
83
|
-
> - **`jwt.refreshSecret`** — dedicated signing secret for refresh tokens
|
|
84
|
-
> - **`refreshToken.refreshTokenField`** — entity field storing the bcrypt hash (
|
|
85
|
-
> - **`refreshToken.useCookie`** — send/read refresh token via httpOnly cookie
|
|
53
|
+
> - **`jwt.refreshSecret`** — dedicated signing secret for refresh tokens
|
|
54
|
+
> - **`refreshToken.refreshTokenField`** — entity field storing the bcrypt hash (server-side revocation)
|
|
55
|
+
> - **`refreshToken.useCookie`** — send/read refresh token via httpOnly cookie
|
|
56
|
+
>
|
|
57
|
+
> 📖 Full details: [README/authentication.md → Migration Guide](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/authentication.md#migration-guide-v3--v4)
|
|
86
58
|
>
|
|
87
|
-
>
|
|
59
|
+
> </details>
|
|
88
60
|
|
|
89
61
|
---
|
|
90
62
|
|
|
91
|
-
|
|
63
|
+
## ✨ Features
|
|
92
64
|
|
|
93
|
-
|
|
65
|
+
<table>
|
|
66
|
+
<tr>
|
|
67
|
+
<td>
|
|
94
68
|
|
|
95
|
-
|
|
69
|
+
🚀 **Zero Boilerplate**<br/>
|
|
70
|
+
Full CRUD REST API generated from a single schema definition.
|
|
96
71
|
|
|
97
|
-
|
|
72
|
+
</td>
|
|
73
|
+
<td>
|
|
98
74
|
|
|
99
|
-
|
|
100
|
-
-
|
|
101
|
-
- [Installation](#installation)
|
|
102
|
-
- [Quick Start](#quick-start)
|
|
103
|
-
- [Advanced Features](#advanced-features)
|
|
104
|
-
- [API Reference](#api-reference)
|
|
105
|
-
- [Best Practices](#best-practices)
|
|
75
|
+
🔐 **JWT Authentication**<br/>
|
|
76
|
+
Dual-token (access + refresh), 8 built-in endpoints, cookie mode, server-side revocation.
|
|
106
77
|
|
|
107
|
-
|
|
78
|
+
</td>
|
|
79
|
+
</tr>
|
|
80
|
+
<tr>
|
|
81
|
+
<td>
|
|
108
82
|
|
|
109
|
-
|
|
83
|
+
🔓 **Passwordless / OTP** ⭐<br/>
|
|
84
|
+
Magic-link / OTP login flow with configurable token delivery.
|
|
110
85
|
|
|
111
|
-
|
|
86
|
+
</td>
|
|
87
|
+
<td>
|
|
112
88
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
- **Real-time capabilities** - Built-in WebSocket support for live updates
|
|
116
|
-
- **Enterprise-ready features** - Authentication, authorization, caching, and validation out of the box
|
|
117
|
-
- **Developer-friendly** - Swagger documentation auto-generated for all endpoints
|
|
118
|
-
- **Highly customizable** - Fine-grained control at global, controller, and route levels
|
|
89
|
+
🛡️ **Authorization**<br/>
|
|
90
|
+
Ability predicates — per-route access control in `filter` or `throw` mode.
|
|
119
91
|
|
|
120
|
-
|
|
92
|
+
</td>
|
|
93
|
+
</tr>
|
|
94
|
+
<tr>
|
|
95
|
+
<td>
|
|
121
96
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
| Feature | Description |
|
|
125
|
-
|---------|-------------|
|
|
126
|
-
| 🚀 **Zero Boilerplate** | Generate complete CRUD APIs from schema definitions |
|
|
127
|
-
| 🔐 **JWT Authentication** | Built-in dual-token authentication (access + refresh) with 8 endpoints (login, register, get/update account, reset/change password, refresh token, logout) |
|
|
128
|
-
| 🛡️ **Authorization** | Role-based access control with ability predicates |
|
|
129
|
-
| ⚡ **Smart Caching** | Global caching with automatic invalidation |
|
|
130
|
-
| ✅ **Validation** | Global and per-route validation with class-validator |
|
|
131
|
-
| 📡 **WebSockets** | Socket.IO support for calling routes via WebSocket events |
|
|
132
|
-
| 📚 **Swagger UI** | Auto-generated OpenAPI documentation |
|
|
133
|
-
| 🔄 **Versioning** | URI-based API versioning |
|
|
134
|
-
| 🗑️ **Soft Delete** | Built-in soft delete with `SoftDeletableEntity` |
|
|
135
|
-
| 🔍 **Advanced Queries** | MongoDB queries + Aggregation pipelines for complex analytics |
|
|
97
|
+
⚡ **Smart Caching**<br/>
|
|
98
|
+
Global HTTP cache with auto-invalidation, `disableCache` per route or controller.
|
|
136
99
|
|
|
137
|
-
|
|
100
|
+
</td>
|
|
101
|
+
<td>
|
|
138
102
|
|
|
139
|
-
|
|
103
|
+
✅ **Validation**<br/>
|
|
104
|
+
`class-validator` integration, configurable `ValidationPipe` globally or per route.
|
|
140
105
|
|
|
141
|
-
|
|
106
|
+
</td>
|
|
107
|
+
</tr>
|
|
108
|
+
<tr>
|
|
109
|
+
<td>
|
|
142
110
|
|
|
143
|
-
|
|
144
|
-
-
|
|
145
|
-
- MongoDB >= 4.0
|
|
111
|
+
📡 **WebSockets**<br/>
|
|
112
|
+
Socket.IO support, room-targeted broadcast, `onConnection` hook, debug mode.
|
|
146
113
|
|
|
147
|
-
|
|
114
|
+
</td>
|
|
115
|
+
<td>
|
|
148
116
|
|
|
149
|
-
|
|
117
|
+
🟢 **Presence** ⭐<br/>
|
|
118
|
+
Real-time online/offline tracking — InMemory or Redis adapter.
|
|
150
119
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
120
|
+
</td>
|
|
121
|
+
</tr>
|
|
122
|
+
<tr>
|
|
123
|
+
<td>
|
|
155
124
|
|
|
156
|
-
|
|
125
|
+
🔁 **Callbacks**<br/>
|
|
126
|
+
`beforeSave`, `afterSave`, `beforeDelete` hooks with typed context + authenticated user.
|
|
157
127
|
|
|
158
|
-
|
|
128
|
+
</td>
|
|
129
|
+
<td>
|
|
159
130
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
```
|
|
131
|
+
🌊 **Cascade Delete** ⭐<br/>
|
|
132
|
+
Cross-collection cascades with `beforeDeleteCallback` and soft-delete support.
|
|
163
133
|
|
|
164
|
-
>
|
|
165
|
-
>
|
|
166
|
-
>
|
|
167
|
-
>
|
|
168
|
-
> - `class-validator` & `class-transformer` - Validation & transformation
|
|
169
|
-
> - `@nestjs/cache-manager` & `cache-manager` - Caching
|
|
170
|
-
> - `@nestjs/websockets` & `socket.io` - Real-time support
|
|
171
|
-
>
|
|
172
|
-
> **No additional packages required** for any of the optional features (authentication, caching, websockets, validation, etc.).
|
|
134
|
+
</td>
|
|
135
|
+
</tr>
|
|
136
|
+
<tr>
|
|
137
|
+
<td>
|
|
173
138
|
|
|
174
|
-
|
|
139
|
+
🎛️ **Custom Routes** ⭐<br/>
|
|
140
|
+
Add any HTTP method with a custom service and WebSocket gateway in `forFeature`.
|
|
141
|
+
|
|
142
|
+
</td>
|
|
143
|
+
<td>
|
|
144
|
+
|
|
145
|
+
🏷️ **Field Decorators** ⭐<br/>
|
|
146
|
+
`@DerivedField` for computed values, `@ProtectedField` for runtime-stripped fields.
|
|
147
|
+
|
|
148
|
+
</td>
|
|
149
|
+
</tr>
|
|
150
|
+
<tr>
|
|
151
|
+
<td>
|
|
175
152
|
|
|
176
|
-
|
|
153
|
+
🔍 **Aggregate + Pagination**<br/>
|
|
154
|
+
MongoDB aggregation pipelines via `toPipeline`, auto-paginated with `$facet`.
|
|
177
155
|
|
|
178
|
-
|
|
156
|
+
</td>
|
|
157
|
+
<td>
|
|
179
158
|
|
|
180
|
-
|
|
159
|
+
📚 **Swagger UI**<br/>
|
|
160
|
+
Auto-generated OpenAPI documentation for every route, zero configuration.
|
|
181
161
|
|
|
182
|
-
|
|
162
|
+
</td>
|
|
163
|
+
</tr>
|
|
164
|
+
</table>
|
|
165
|
+
|
|
166
|
+
> All dependencies included — `@nestjs/mongoose`, `@nestjs/jwt`, `@nestjs/swagger`, `class-validator`, `socket.io` and more. **No extra installs.**
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## ⚡ Quick Start
|
|
171
|
+
|
|
172
|
+
### 1 — Configure the root module
|
|
183
173
|
|
|
184
174
|
```typescript
|
|
185
175
|
// src/app.module.ts
|
|
186
176
|
import { Module } from '@nestjs/common';
|
|
187
177
|
import { DynamicApiModule } from 'mongodb-dynamic-api';
|
|
188
|
-
import { AppController } from './app.controller';
|
|
189
|
-
import { AppService } from './app.service';
|
|
190
178
|
|
|
191
179
|
@Module({
|
|
192
180
|
imports: [
|
|
193
|
-
DynamicApiModule.forRoot(
|
|
194
|
-
'mongodb://localhost:27017/my-database', // MongoDB connection URI
|
|
195
|
-
),
|
|
181
|
+
DynamicApiModule.forRoot(process.env.MONGODB_URI),
|
|
196
182
|
],
|
|
197
|
-
controllers: [AppController],
|
|
198
|
-
providers: [AppService],
|
|
199
183
|
})
|
|
200
184
|
export class AppModule {}
|
|
201
185
|
```
|
|
202
186
|
|
|
203
|
-
|
|
204
|
-
> ```typescript
|
|
205
|
-
> DynamicApiModule.forRoot(process.env.MONGODB_URI)
|
|
206
|
-
> ```
|
|
207
|
-
|
|
208
|
-
### Step 2: Define Your Entity
|
|
209
|
-
|
|
210
|
-
Create your first entity using Mongoose decorators. All entities must extend either `BaseEntity` or `SoftDeletableEntity`:
|
|
187
|
+
### 2 — Define your entity
|
|
211
188
|
|
|
212
189
|
```typescript
|
|
213
190
|
// src/users/user.entity.ts
|
|
214
191
|
import { Prop, Schema } from '@nestjs/mongoose';
|
|
215
192
|
import { BaseEntity } from 'mongodb-dynamic-api';
|
|
193
|
+
import { IsEmail, IsNotEmpty } from 'class-validator';
|
|
216
194
|
|
|
217
195
|
@Schema({ collection: 'users' })
|
|
218
196
|
export class User extends BaseEntity {
|
|
197
|
+
@IsNotEmpty()
|
|
219
198
|
@Prop({ type: String, required: true })
|
|
220
199
|
name: string;
|
|
221
200
|
|
|
201
|
+
@IsEmail()
|
|
222
202
|
@Prop({ type: String, required: true, unique: true })
|
|
223
203
|
email: string;
|
|
224
|
-
|
|
225
|
-
@Prop({ type: String })
|
|
226
|
-
phone?: string;
|
|
227
|
-
|
|
228
|
-
@Prop({ type: Boolean, default: true })
|
|
229
|
-
isActive: boolean;
|
|
230
204
|
}
|
|
231
205
|
```
|
|
232
206
|
|
|
233
|
-
>
|
|
234
|
-
> - `BaseEntity` provides `id`, `createdAt`, and `updatedAt` fields automatically
|
|
235
|
-
> - Timestamps are **automatically enabled** - no need to add `timestamps: true` in `@Schema()`
|
|
236
|
-
> - `_id` and `__v` are automatically excluded from JSON responses
|
|
237
|
-
> - See [Entities documentation](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/entities.md) for more details
|
|
207
|
+
> `BaseEntity` automatically provides `id`, `createdAt`, `updatedAt` and excludes `_id` / `__v` from responses.
|
|
238
208
|
|
|
239
|
-
###
|
|
240
|
-
|
|
241
|
-
Use `DynamicApiModule.forFeature()` to generate the API endpoints:
|
|
209
|
+
### 3 — Generate the API
|
|
242
210
|
|
|
243
211
|
```typescript
|
|
244
212
|
// src/users/users.module.ts
|
|
@@ -246,682 +214,73 @@ import { Module } from '@nestjs/common';
|
|
|
246
214
|
import { DynamicApiModule } from 'mongodb-dynamic-api';
|
|
247
215
|
import { User } from './user.entity';
|
|
248
216
|
|
|
249
|
-
@Module({
|
|
250
|
-
imports: [
|
|
251
|
-
DynamicApiModule.forFeature({
|
|
252
|
-
entity: User,
|
|
253
|
-
controllerOptions: {
|
|
254
|
-
path: 'users',
|
|
255
|
-
},
|
|
256
|
-
}),
|
|
257
|
-
],
|
|
258
|
-
})
|
|
259
|
-
export class UsersModule {}
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
### Step 4: Register the Feature Module
|
|
263
|
-
|
|
264
|
-
Add `UsersModule` to the imports array in `app.module.ts`:
|
|
265
|
-
|
|
266
|
-
```typescript
|
|
267
|
-
// src/app.module.ts
|
|
268
|
-
import { Module } from '@nestjs/common';
|
|
269
|
-
import { DynamicApiModule } from 'mongodb-dynamic-api';
|
|
270
|
-
import { UsersModule } from './users/users.module';
|
|
271
|
-
|
|
272
|
-
@Module({
|
|
273
|
-
imports: [
|
|
274
|
-
DynamicApiModule.forRoot(
|
|
275
|
-
'mongodb://localhost:27017/my-database',
|
|
276
|
-
),
|
|
277
|
-
UsersModule, // Add your feature module here
|
|
278
|
-
],
|
|
279
|
-
controllers: [AppController],
|
|
280
|
-
providers: [AppService],
|
|
281
|
-
})
|
|
282
|
-
export class AppModule {}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
### Step 5: Start Your Application
|
|
286
|
-
|
|
287
|
-
```bash
|
|
288
|
-
npm run start:dev
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
**🎉 Congratulations!** Your API is now running with complete CRUD operations at `http://localhost:3000/users`
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## API Reference
|
|
296
|
-
|
|
297
|
-
Your generated API includes the following endpoints:
|
|
298
|
-
|
|
299
|
-
### Available Endpoints
|
|
300
|
-
|
|
301
|
-
| Endpoint | Method | Description | Request Body | Params | Query |
|
|
302
|
-
|:---------|:------:|:------------|:-------------|:-------|:------|
|
|
303
|
-
| `/users` | `GET` | Retrieve all users | - | - | MongoDB query object |
|
|
304
|
-
| `/users/:id` | `GET` | Retrieve a single user by ID | - | `id` | - |
|
|
305
|
-
| `/users/many` | `POST` | Create multiple users at once | `{ list: User[] }` | - | - |
|
|
306
|
-
| `/users` | `POST` | Create a single user | `User` | - | - |
|
|
307
|
-
| `/users/:id` | `PUT` | Replace a user completely | `User` | `id` | - |
|
|
308
|
-
| `/users` | `PATCH` | Update multiple users | `Partial<User>` | - | `ids[]` |
|
|
309
|
-
| `/users/:id` | `PATCH` | Update a single user partially | `Partial<User>` | `id` | - |
|
|
310
|
-
| `/users` | `DELETE` | Delete multiple users | - | - | `ids[]` |
|
|
311
|
-
| `/users/:id` | `DELETE` | Delete a single user | - | `id` | - |
|
|
312
|
-
| `/users/duplicate` | `POST` | Duplicate multiple users with updates | `Partial<User>` | - | `ids[]` |
|
|
313
|
-
| `/users/duplicate/:id` | `POST` | Duplicate a single user with updates | `Partial<User>` | `id` | - |
|
|
314
|
-
| `/users/aggregate` | `GET` | Execute MongoDB aggregation pipeline | - | - | Query DTO params |
|
|
315
|
-
|
|
316
|
-
> **💡 Note:** The `Aggregate` route requires a custom Query DTO with a `toPipeline` static method. See [Advanced Queries documentation](#advanced-queries-with-aggregate) below.
|
|
317
|
-
|
|
318
|
-
### Request Examples
|
|
319
|
-
|
|
320
|
-
#### Get Many Users
|
|
321
|
-
|
|
322
|
-
```bash
|
|
323
|
-
# Get all users
|
|
324
|
-
GET /users
|
|
325
|
-
|
|
326
|
-
# Query parameters are passed directly to MongoDB find()
|
|
327
|
-
# You can use any valid MongoDB query
|
|
328
|
-
GET /users?isActive=true&name=John
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
#### Create One User
|
|
332
|
-
|
|
333
|
-
```bash
|
|
334
|
-
POST /users
|
|
335
|
-
Content-Type: application/json
|
|
336
|
-
|
|
337
|
-
{
|
|
338
|
-
"name": "John Doe",
|
|
339
|
-
"email": "john.doe@example.com",
|
|
340
|
-
"phone": "+1234567890",
|
|
341
|
-
"isActive": true
|
|
342
|
-
}
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
**Response (201 Created):**
|
|
346
|
-
```json
|
|
347
|
-
{
|
|
348
|
-
"id": "507f1f77bcf86cd799439011",
|
|
349
|
-
"name": "John Doe",
|
|
350
|
-
"email": "john.doe@example.com",
|
|
351
|
-
"phone": "+1234567890",
|
|
352
|
-
"isActive": true,
|
|
353
|
-
"createdAt": "2026-02-21T10:30:00.000Z",
|
|
354
|
-
"updatedAt": "2026-02-21T10:30:00.000Z"
|
|
355
|
-
}
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
#### Create Many Users
|
|
359
|
-
|
|
360
|
-
```bash
|
|
361
|
-
POST /users/many
|
|
362
|
-
Content-Type: application/json
|
|
363
|
-
|
|
364
|
-
{
|
|
365
|
-
"list": [
|
|
366
|
-
{
|
|
367
|
-
"name": "Alice Smith",
|
|
368
|
-
"email": "alice@example.com"
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
"name": "Bob Johnson",
|
|
372
|
-
"email": "bob@example.com"
|
|
373
|
-
}
|
|
374
|
-
]
|
|
375
|
-
}
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
**Response (201 Created):**
|
|
379
|
-
```json
|
|
380
|
-
[
|
|
381
|
-
{
|
|
382
|
-
"id": "507f1f77bcf86cd799439011",
|
|
383
|
-
"name": "Alice Smith",
|
|
384
|
-
"email": "alice@example.com",
|
|
385
|
-
"isActive": true,
|
|
386
|
-
"createdAt": "2026-02-21T10:30:00.000Z",
|
|
387
|
-
"updatedAt": "2026-02-21T10:30:00.000Z"
|
|
388
|
-
},
|
|
389
|
-
{
|
|
390
|
-
"id": "507f1f77bcf86cd799439012",
|
|
391
|
-
"name": "Bob Johnson",
|
|
392
|
-
"email": "bob@example.com",
|
|
393
|
-
"isActive": true,
|
|
394
|
-
"createdAt": "2026-02-21T10:30:00.000Z",
|
|
395
|
-
"updatedAt": "2026-02-21T10:30:00.000Z"
|
|
396
|
-
}
|
|
397
|
-
]
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
#### Update One User
|
|
401
|
-
|
|
402
|
-
```bash
|
|
403
|
-
PATCH /users/507f1f77bcf86cd799439011
|
|
404
|
-
Content-Type: application/json
|
|
405
|
-
|
|
406
|
-
{
|
|
407
|
-
"phone": "+0987654321",
|
|
408
|
-
"isActive": false
|
|
409
|
-
}
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
**Response (200 OK):**
|
|
413
|
-
```json
|
|
414
|
-
{
|
|
415
|
-
"id": "507f1f77bcf86cd799439011",
|
|
416
|
-
"name": "John Doe",
|
|
417
|
-
"email": "john.doe@example.com",
|
|
418
|
-
"phone": "+0987654321",
|
|
419
|
-
"isActive": false,
|
|
420
|
-
"createdAt": "2026-02-21T10:30:00.000Z",
|
|
421
|
-
"updatedAt": "2026-02-21T10:35:00.000Z"
|
|
422
|
-
}
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
#### Duplicate One User
|
|
426
|
-
|
|
427
|
-
```bash
|
|
428
|
-
POST /users/duplicate/507f1f77bcf86cd799439011
|
|
429
|
-
Content-Type: application/json
|
|
430
|
-
|
|
431
|
-
{
|
|
432
|
-
"email": "john.doe.copy@example.com"
|
|
433
|
-
}
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
**Response (201 Created):**
|
|
437
|
-
```json
|
|
438
|
-
{
|
|
439
|
-
"id": "507f1f77bcf86cd799439013",
|
|
440
|
-
"name": "John Doe",
|
|
441
|
-
"email": "john.doe.copy@example.com",
|
|
442
|
-
"phone": "+0987654321",
|
|
443
|
-
"isActive": false,
|
|
444
|
-
"createdAt": "2026-02-21T10:40:00.000Z",
|
|
445
|
-
"updatedAt": "2026-02-21T10:40:00.000Z"
|
|
446
|
-
}
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
---
|
|
450
|
-
|
|
451
|
-
## Advanced Features
|
|
452
|
-
|
|
453
|
-
The module supports powerful customization options. Here are some quick examples:
|
|
454
|
-
|
|
455
|
-
### Advanced Queries with Aggregate
|
|
456
|
-
|
|
457
|
-
The `Aggregate` route type enables **MongoDB aggregation pipelines** for complex queries, analytics, and data transformations. This is perfect for:
|
|
458
|
-
- Grouping and counting data
|
|
459
|
-
- Calculating statistics (sum, average, min, max)
|
|
460
|
-
- Joining related collections with `$lookup`
|
|
461
|
-
- Complex filtering and transformations
|
|
462
|
-
- Pagination with counting
|
|
463
|
-
|
|
464
|
-
**Key Requirements:**
|
|
465
|
-
1. You **must** provide a custom Query DTO
|
|
466
|
-
2. The Query DTO **must** have a static `toPipeline` method
|
|
467
|
-
3. The method returns an array of MongoDB pipeline stages
|
|
468
|
-
4. Optionally provide a custom Presenter with `fromAggregate` method
|
|
469
|
-
|
|
470
|
-
**Example - User Statistics:**
|
|
471
|
-
|
|
472
|
-
```typescript
|
|
473
|
-
// src/users/dtos/user-stats.query.ts
|
|
474
|
-
import { IsOptional, IsString } from 'class-validator';
|
|
475
|
-
import { PipelineStage } from 'mongodb-pipeline-builder';
|
|
476
|
-
|
|
477
|
-
export class UserStatsQuery {
|
|
478
|
-
@IsOptional()
|
|
479
|
-
@IsString()
|
|
480
|
-
status?: string;
|
|
481
|
-
|
|
482
|
-
static toPipeline(query: UserStatsQuery): PipelineStage[] {
|
|
483
|
-
const pipeline: PipelineStage[] = [];
|
|
484
|
-
|
|
485
|
-
// Filter by status if provided
|
|
486
|
-
if (query.status) {
|
|
487
|
-
pipeline.push({ $match: { isActive: query.status === 'active' } });
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// Group by creation date (year-month)
|
|
491
|
-
pipeline.push({
|
|
492
|
-
$group: {
|
|
493
|
-
_id: {
|
|
494
|
-
year: { $year: '$createdAt' },
|
|
495
|
-
month: { $month: '$createdAt' },
|
|
496
|
-
},
|
|
497
|
-
count: { $sum: 1 },
|
|
498
|
-
users: { $push: { id: '$_id', name: '$name', email: '$email' } },
|
|
499
|
-
},
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
// Sort by date
|
|
503
|
-
pipeline.push({
|
|
504
|
-
$sort: { '_id.year': -1, '_id.month': -1 },
|
|
505
|
-
});
|
|
506
|
-
|
|
507
|
-
return pipeline;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
// src/users/dtos/user-stats.presenter.ts
|
|
512
|
-
export class UserStatsPresenter {
|
|
513
|
-
period: string;
|
|
514
|
-
count: number;
|
|
515
|
-
users: { id: string; name: string; email: string }[];
|
|
516
|
-
|
|
517
|
-
static fromAggregate(results: any[]): UserStatsPresenter[] {
|
|
518
|
-
return results.map(result => ({
|
|
519
|
-
period: `${result._id.year}-${String(result._id.month).padStart(2, '0')}`,
|
|
520
|
-
count: result.count,
|
|
521
|
-
users: result.users,
|
|
522
|
-
}));
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// src/users/users.module.ts
|
|
527
217
|
@Module({
|
|
528
218
|
imports: [
|
|
529
219
|
DynamicApiModule.forFeature({
|
|
530
220
|
entity: User,
|
|
531
221
|
controllerOptions: { path: 'users' },
|
|
532
|
-
routes: [
|
|
533
|
-
{
|
|
534
|
-
type: 'Aggregate',
|
|
535
|
-
subPath: 'stats',
|
|
536
|
-
dTOs: {
|
|
537
|
-
query: UserStatsQuery,
|
|
538
|
-
presenter: UserStatsPresenter,
|
|
539
|
-
},
|
|
540
|
-
},
|
|
541
|
-
],
|
|
542
222
|
}),
|
|
543
223
|
],
|
|
544
224
|
})
|
|
545
225
|
export class UsersModule {}
|
|
546
226
|
```
|
|
547
227
|
|
|
548
|
-
|
|
549
|
-
```bash
|
|
550
|
-
# Get all user statistics
|
|
551
|
-
GET /users/aggregate/stats
|
|
552
|
-
|
|
553
|
-
# Filter by active users
|
|
554
|
-
GET /users/aggregate/stats?status=active
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
**With Pagination Support:**
|
|
558
|
-
|
|
559
|
-
The Aggregate route automatically detects pagination when your pipeline starts with a `$facet` stage:
|
|
560
|
-
|
|
561
|
-
```typescript
|
|
562
|
-
import { Type } from 'class-transformer';
|
|
563
|
-
import { IsOptional, IsNumber } from 'class-validator';
|
|
564
|
-
import { PipelineBuilder, PipelineStage } from 'mongodb-pipeline-builder';
|
|
565
|
-
|
|
566
|
-
export class PaginatedUsersQuery {
|
|
567
|
-
@IsOptional()
|
|
568
|
-
@IsNumber()
|
|
569
|
-
@Type(() => Number)
|
|
570
|
-
page?: number = 1;
|
|
571
|
-
|
|
572
|
-
@IsOptional()
|
|
573
|
-
@IsNumber()
|
|
574
|
-
@Type(() => Number)
|
|
575
|
-
limit?: number = 10;
|
|
576
|
-
|
|
577
|
-
static toPipeline(query: PaginatedUsersQuery): PipelineStage[] {
|
|
578
|
-
const builder = new PipelineBuilder('paginated-users');
|
|
579
|
-
|
|
580
|
-
// Using method chaining - all methods return the builder
|
|
581
|
-
return builder
|
|
582
|
-
.Match({ isActive: true }) // Add your filters
|
|
583
|
-
.Paging(query.limit, query.page) // Paging(elementsPerPage, page)
|
|
584
|
-
.build(); // Build and return the pipeline array
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
**Response with pagination:**
|
|
590
|
-
```json
|
|
591
|
-
{
|
|
592
|
-
"list": [...],
|
|
593
|
-
"count": 150,
|
|
594
|
-
"totalPage": 15
|
|
595
|
-
}
|
|
596
|
-
```
|
|
597
|
-
|
|
598
|
-
> 📚 **Learn More:** The module uses [`mongodb-pipeline-builder`](https://www.npmjs.com/package/mongodb-pipeline-builder) (by the same author) to greatly simplify building aggregation pipelines with:
|
|
599
|
-
> - **Fluent API** - Chain methods for readable pipeline construction
|
|
600
|
-
> - **Type Safety** - Full TypeScript support with IntelliSense
|
|
601
|
-
> - **Automatic Pagination** - Built-in support for `$facet` with counting via `Paging()` method
|
|
602
|
-
> - **Stage Methods** - Pre-built methods: `Match()`, `Group()`, `Lookup()`, `Sort()`, `Project()`, `AddFields()`, etc.
|
|
603
|
-
> - **Operators** - Import operators like `$Sum()`, `$Average()`, `$First()`, `$Max()`, `$Push()`, etc.
|
|
604
|
-
> - **Helpers** - Utility functions: `LookupEqualityHelper()`, `ProjectOnlyHelper()`, `Field()`, etc.
|
|
605
|
-
> - **No manual stage objects** - Write `builder.Match({ field: value })` instead of `{ $match: { field: value } }`
|
|
606
|
-
>
|
|
607
|
-
> **Example with operators and helpers:**
|
|
608
|
-
> ```typescript
|
|
609
|
-
> import { PipelineBuilder } from 'mongodb-pipeline-builder';
|
|
610
|
-
> import { $Sum, $Average, $First, $Push } from 'mongodb-pipeline-builder/operators';
|
|
611
|
-
> import { Field, ProjectOnlyHelper, LookupEqualityHelper } from 'mongodb-pipeline-builder/helpers';
|
|
612
|
-
>
|
|
613
|
-
> const pipeline = new PipelineBuilder('my-pipeline')
|
|
614
|
-
> .Match({ status: 'active' })
|
|
615
|
-
> .Project(ProjectOnlyHelper('name', 'email', 'orders'))
|
|
616
|
-
> .Lookup(LookupEqualityHelper('orders', 'userOrders', '_id', 'userId'))
|
|
617
|
-
> .Group({
|
|
618
|
-
> _id: '$department',
|
|
619
|
-
> totalUsers: $Sum(1),
|
|
620
|
-
> avgOrderValue: $Average('$orders.total'),
|
|
621
|
-
> firstUser: $First('$name'),
|
|
622
|
-
> allEmails: $Push('$email'),
|
|
623
|
-
> })
|
|
624
|
-
> .AddFields(
|
|
625
|
-
> Field('calculatedField', { $multiply: ['$totalUsers', 10] })
|
|
626
|
-
> )
|
|
627
|
-
> .Sort({ totalUsers: -1 })
|
|
628
|
-
> .Paging(10, 1) // 10 per page, page 1
|
|
629
|
-
> .build();
|
|
630
|
-
> ```
|
|
631
|
-
>
|
|
632
|
-
> This makes complex pipelines much easier to write and maintain!
|
|
633
|
-
|
|
634
|
-
**Advanced Example - Join Collections with $lookup:**
|
|
635
|
-
|
|
636
|
-
```typescript
|
|
637
|
-
// src/orders/dtos/order-analytics.query.ts
|
|
638
|
-
import { IsOptional, IsDateString } from 'class-validator';
|
|
639
|
-
import { PipelineBuilder, PipelineStage } from 'mongodb-pipeline-builder';
|
|
640
|
-
import { $First, $Sum, $Average } from 'mongodb-pipeline-builder/operators';
|
|
641
|
-
import { LookupEqualityHelper } from 'mongodb-pipeline-builder/helpers';
|
|
642
|
-
|
|
643
|
-
export class OrderAnalyticsQuery {
|
|
644
|
-
@IsOptional()
|
|
645
|
-
@IsDateString()
|
|
646
|
-
startDate?: string;
|
|
647
|
-
|
|
648
|
-
@IsOptional()
|
|
649
|
-
@IsDateString()
|
|
650
|
-
endDate?: string;
|
|
651
|
-
|
|
652
|
-
static toPipeline(query: OrderAnalyticsQuery): PipelineStage[] {
|
|
653
|
-
// Using PipelineBuilder (simplified approach - recommended)
|
|
654
|
-
const builder = new PipelineBuilder('order-analytics');
|
|
655
|
-
|
|
656
|
-
// Filter by date range
|
|
657
|
-
if (query.startDate || query.endDate) {
|
|
658
|
-
const dateFilter: any = {};
|
|
659
|
-
if (query.startDate) dateFilter.$gte = new Date(query.startDate);
|
|
660
|
-
if (query.endDate) dateFilter.$lte = new Date(query.endDate);
|
|
661
|
-
builder.Match({ createdAt: dateFilter });
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// Using method chaining with helpers for readable pipeline construction
|
|
665
|
-
return builder
|
|
666
|
-
.Lookup(LookupEqualityHelper('users', 'user', 'userId', '_id')) // Join with users
|
|
667
|
-
.Unwind('$user') // Unwind user array
|
|
668
|
-
.Lookup(LookupEqualityHelper('products', 'products', 'items.productId', '_id')) // Join with products
|
|
669
|
-
.Group({ // Group by user and calculate totals using operators
|
|
670
|
-
_id: '$userId',
|
|
671
|
-
userName: $First('$user.name'),
|
|
672
|
-
userEmail: $First('$user.email'),
|
|
673
|
-
orderCount: $Sum(1),
|
|
674
|
-
totalAmount: $Sum('$total'),
|
|
675
|
-
avgOrderValue: $Average('$total'),
|
|
676
|
-
})
|
|
677
|
-
.Sort({ totalAmount: -1 }) // Sort by total amount descending
|
|
678
|
-
.build(); // Build and return the pipeline
|
|
679
|
-
|
|
680
|
-
/* Manual approach (without PipelineBuilder and helpers):
|
|
681
|
-
const pipeline: PipelineStage[] = [];
|
|
682
|
-
|
|
683
|
-
if (query.startDate || query.endDate) {
|
|
684
|
-
const dateFilter: any = {};
|
|
685
|
-
if (query.startDate) dateFilter.$gte = new Date(query.startDate);
|
|
686
|
-
if (query.endDate) dateFilter.$lte = new Date(query.endDate);
|
|
687
|
-
pipeline.push({ $match: { createdAt: dateFilter } });
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
pipeline.push({
|
|
691
|
-
$lookup: {
|
|
692
|
-
from: 'users',
|
|
693
|
-
localField: 'userId',
|
|
694
|
-
foreignField: '_id',
|
|
695
|
-
as: 'user',
|
|
696
|
-
},
|
|
697
|
-
});
|
|
698
|
-
|
|
699
|
-
pipeline.push({ $unwind: '$user' });
|
|
700
|
-
|
|
701
|
-
pipeline.push({
|
|
702
|
-
$lookup: {
|
|
703
|
-
from: 'products',
|
|
704
|
-
localField: 'items.productId',
|
|
705
|
-
foreignField: '_id',
|
|
706
|
-
as: 'products',
|
|
707
|
-
},
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
pipeline.push({
|
|
711
|
-
$group: {
|
|
712
|
-
_id: '$userId',
|
|
713
|
-
userName: { $first: '$user.name' },
|
|
714
|
-
userEmail: { $first: '$user.email' },
|
|
715
|
-
orderCount: { $sum: 1 },
|
|
716
|
-
totalAmount: { $sum: '$total' },
|
|
717
|
-
avgOrderValue: { $avg: '$total' },
|
|
718
|
-
},
|
|
719
|
-
});
|
|
720
|
-
|
|
721
|
-
pipeline.push({ $sort: { totalAmount: -1 } });
|
|
722
|
-
|
|
723
|
-
return pipeline;
|
|
724
|
-
|
|
725
|
-
Compare with PipelineBuilder approach:
|
|
726
|
-
- LookupEqualityHelper() vs verbose $lookup object
|
|
727
|
-
- $First(), $Sum(), $Average() vs raw MongoDB operators
|
|
728
|
-
- Method chaining vs array.push()
|
|
729
|
-
- Much more readable and maintainable!
|
|
730
|
-
*/
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
// src/orders/dtos/order-analytics.presenter.ts
|
|
735
|
-
export class OrderAnalyticsPresenter {
|
|
736
|
-
userId: string;
|
|
737
|
-
userName: string;
|
|
738
|
-
userEmail: string;
|
|
739
|
-
orderCount: number;
|
|
740
|
-
totalAmount: number;
|
|
741
|
-
avgOrderValue: number;
|
|
742
|
-
|
|
743
|
-
static fromAggregate(results: any[]): OrderAnalyticsPresenter[] {
|
|
744
|
-
return results.map(result => ({
|
|
745
|
-
userId: result._id,
|
|
746
|
-
userName: result.userName,
|
|
747
|
-
userEmail: result.userEmail,
|
|
748
|
-
orderCount: result.orderCount,
|
|
749
|
-
totalAmount: Math.round(result.totalAmount * 100) / 100,
|
|
750
|
-
avgOrderValue: Math.round(result.avgOrderValue * 100) / 100,
|
|
751
|
-
}));
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
// Usage
|
|
756
|
-
GET /orders/aggregate/analytics?startDate=2026-01-01&endDate=2026-12-31
|
|
757
|
-
```
|
|
758
|
-
|
|
759
|
-
**Response:**
|
|
760
|
-
```json
|
|
761
|
-
[
|
|
762
|
-
{
|
|
763
|
-
"userId": "507f1f77bcf86cd799439011",
|
|
764
|
-
"userName": "John Doe",
|
|
765
|
-
"userEmail": "john@example.com",
|
|
766
|
-
"orderCount": 15,
|
|
767
|
-
"totalAmount": 2450.50,
|
|
768
|
-
"avgOrderValue": 163.37
|
|
769
|
-
},
|
|
770
|
-
{
|
|
771
|
-
"userId": "507f1f77bcf86cd799439012",
|
|
772
|
-
"userName": "Jane Smith",
|
|
773
|
-
"userEmail": "jane@example.com",
|
|
774
|
-
"orderCount": 12,
|
|
775
|
-
"totalAmount": 1850.75,
|
|
776
|
-
"avgOrderValue": 154.23
|
|
777
|
-
}
|
|
778
|
-
]
|
|
779
|
-
```
|
|
780
|
-
|
|
781
|
-
### Route Configuration
|
|
782
|
-
|
|
783
|
-
Control which routes are generated and customize individual routes:
|
|
784
|
-
|
|
785
|
-
```typescript
|
|
786
|
-
DynamicApiModule.forFeature({
|
|
787
|
-
entity: User,
|
|
788
|
-
controllerOptions: {
|
|
789
|
-
path: 'users',
|
|
790
|
-
apiTag: 'Users Management',
|
|
791
|
-
},
|
|
792
|
-
routes: [
|
|
793
|
-
{ type: 'GetMany' },
|
|
794
|
-
{ type: 'GetOne' },
|
|
795
|
-
{ type: 'CreateOne', validationPipeOptions: { whitelist: true } },
|
|
796
|
-
{ type: 'UpdateOne' },
|
|
797
|
-
{ type: 'DeleteOne' },
|
|
798
|
-
// Exclude specific routes
|
|
799
|
-
{ type: 'DeleteMany', excluded: true },
|
|
800
|
-
],
|
|
801
|
-
})
|
|
802
|
-
```
|
|
803
|
-
|
|
804
|
-
### Enable Soft Delete
|
|
805
|
-
|
|
806
|
-
Use `SoftDeletableEntity` for soft delete functionality:
|
|
807
|
-
|
|
808
|
-
```typescript
|
|
809
|
-
import { SoftDeletableEntity } from 'mongodb-dynamic-api';
|
|
810
|
-
|
|
811
|
-
@Schema({ collection: 'users' })
|
|
812
|
-
export class User extends SoftDeletableEntity {
|
|
813
|
-
@Prop({ type: String, required: true })
|
|
814
|
-
name: string;
|
|
815
|
-
// ... other fields
|
|
816
|
-
}
|
|
817
|
-
```
|
|
818
|
-
|
|
819
|
-
Deleted entities are marked with `isDeleted: true` instead of being removed.
|
|
820
|
-
|
|
821
|
-
> 📚 **Learn More:** See detailed guides in the [Optional Features](#optional-features-all-dependencies-included) section below.
|
|
228
|
+
Register `UsersModule` in `AppModule`, run `npm run start:dev` — your API is live at `http://localhost:3000/users`. 🎉
|
|
822
229
|
|
|
823
230
|
---
|
|
824
231
|
|
|
825
|
-
##
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
export class User extends BaseEntity {
|
|
844
|
-
@IsNotEmpty()
|
|
845
|
-
@Length(2, 100)
|
|
846
|
-
@Prop({ type: String, required: true })
|
|
847
|
-
name: string;
|
|
848
|
-
|
|
849
|
-
@IsEmail()
|
|
850
|
-
@Prop({ type: String, required: true, unique: true })
|
|
851
|
-
email: string;
|
|
852
|
-
}
|
|
853
|
-
```
|
|
854
|
-
|
|
855
|
-
### 3. Optimize with Indexes
|
|
856
|
-
|
|
857
|
-
Define indexes for frequently queried fields:
|
|
858
|
-
|
|
859
|
-
```typescript
|
|
860
|
-
import { DynamicAPISchemaOptions } from 'mongodb-dynamic-api';
|
|
861
|
-
|
|
862
|
-
@DynamicAPISchemaOptions({
|
|
863
|
-
indexes: [
|
|
864
|
-
{ fields: { email: 1 }, options: { unique: true } },
|
|
865
|
-
{ fields: { createdAt: -1 } },
|
|
866
|
-
],
|
|
867
|
-
})
|
|
868
|
-
@Schema({ collection: 'users' })
|
|
869
|
-
export class User extends BaseEntity {
|
|
870
|
-
// ...
|
|
871
|
-
}
|
|
872
|
-
```
|
|
873
|
-
|
|
874
|
-
> 📚 **Learn More:** See [Schema Options](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/schema-options.md) and [Validation](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/validation.md) guides.
|
|
232
|
+
## 📡 Generated Endpoints
|
|
233
|
+
|
|
234
|
+
| Route Type | Method | Path | Description |
|
|
235
|
+
|:-----------|:------:|:-----|:------------|
|
|
236
|
+
| `GetMany` | `GET` | `/users` | List all — supports MongoDB query params |
|
|
237
|
+
| `GetOne` | `GET` | `/users/:id` | Get a single document by ID |
|
|
238
|
+
| `CreateMany` | `POST` | `/users/many` | Bulk create — body: `{ list: User[] }` |
|
|
239
|
+
| `CreateOne` | `POST` | `/users` | Create a single document |
|
|
240
|
+
| `ReplaceOne` | `PUT` | `/users/:id` | Full replacement |
|
|
241
|
+
| `UpdateMany` | `PATCH` | `/users` | Partial update — query: `?ids[]=` |
|
|
242
|
+
| `UpdateOne` | `PATCH` | `/users/:id` | Partial update by ID |
|
|
243
|
+
| `DeleteMany` | `DELETE` | `/users` | Delete multiple — query: `?ids[]=` |
|
|
244
|
+
| `DeleteOne` | `DELETE` | `/users/:id` | Delete by ID |
|
|
245
|
+
| `DuplicateMany` | `POST` | `/users/duplicate` | Clone multiple with field overrides |
|
|
246
|
+
| `DuplicateOne` | `POST` | `/users/duplicate/:id` | Clone one with field overrides |
|
|
247
|
+
| `Aggregate` | `GET` | `/users/aggregate` | Custom aggregation pipeline (requires Query DTO) |
|
|
248
|
+
|
|
249
|
+
> Use the `routes` array in `forFeature` to cherry-pick, exclude, or fine-tune any route individually.
|
|
875
250
|
|
|
876
251
|
---
|
|
877
252
|
|
|
878
|
-
##
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
|
890
|
-
|
|
891
|
-
|
|
|
892
|
-
|
|
|
893
|
-
|
|
|
894
|
-
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
- **Timestamps**: Automatically enabled when entity extends `BaseEntity` (provides `createdAt` and `updatedAt`)
|
|
906
|
-
- **Soft Delete**: Use `SoftDeletableEntity` to add `isDeleted` and `deletedAt` fields
|
|
907
|
-
- **Version Format**: Must be numeric strings (`'1'`, `'2'`), not semantic versioning
|
|
908
|
-
- **WebSocket Events**:
|
|
909
|
-
- Auth events have fixed names: `auth-login`, `auth-register`, `auth-get-account`, `auth-update-account`, `auth-reset-password`, `auth-change-password`, `auth-refresh-token`, `auth-logout`
|
|
910
|
-
- CRUD events are generated from entity name or `apiTag`: `kebabCase(routeType + '/' + displayedName)`
|
|
911
|
-
- **Ability Predicates**: Signature varies by context:
|
|
912
|
-
- Auth routes: `(user, body?) => boolean`
|
|
913
|
-
- CRUD routes: `(entity, user) => boolean`
|
|
253
|
+
## 📚 Documentation
|
|
254
|
+
|
|
255
|
+
| Feature | Description | Guide |
|
|
256
|
+
|:--------|:------------|:-----:|
|
|
257
|
+
| 🗂️ **Entities** | `BaseEntity`, `SoftDeletableEntity`, timestamps, JSON transform | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/entities.md) |
|
|
258
|
+
| 🗃️ **Schema Options** | Indexes, lifecycle hooks, custom schema initialization | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/schema-options.md) |
|
|
259
|
+
| 🔐 **Authentication** | Dual-token JWT, 8 endpoints, cookie mode, revocation, OTP ⭐ | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/authentication.md) |
|
|
260
|
+
| 🛡️ **Authorization** | Ability predicates, `filter` vs `throw` mode | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/authorization.md) |
|
|
261
|
+
| ⚡ **Caching** | Global cache, auto-purge endpoint, `disableCache` option ⭐ | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/caching.md) |
|
|
262
|
+
| ✅ **Validation** | `class-validator`, global + per-route `ValidationPipe` | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/validation.md) |
|
|
263
|
+
| 📡 **WebSockets** | Socket.IO, room-targeted broadcast, `onConnection`, debug ⭐ | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/websockets.md) |
|
|
264
|
+
| 🟢 **Presence** | Online/offline tracking, InMemory & Redis adapters ⭐ | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/presence.md) |
|
|
265
|
+
| 🔁 **Callbacks** | `beforeSave`, `afterSave`, `beforeDelete`, typed contexts ⭐ | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/callbacks.md) |
|
|
266
|
+
| 🔄 **Versioning** | URI-based API versioning | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/versioning.md) |
|
|
267
|
+
| 📚 **Swagger UI** | Auto-generated OpenAPI docs, visibility decorators | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/swagger-ui.md) |
|
|
268
|
+
| 🗂️ **Route Config** | DTOs, cascade delete, predicates, subPath, interceptors ⭐ | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/route-config.md) |
|
|
269
|
+
| 🎛️ **Controller Config** | `forFeature` options, `customRoutes`, `extraProviders` ⭐ | [View](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/main/README/controller-config.md) |
|
|
270
|
+
|
|
271
|
+
> [!NOTE]
|
|
272
|
+
> **Key reminders:**
|
|
273
|
+
> - `BaseEntity` auto-enables timestamps — no `timestamps: true` needed in `@Schema()`
|
|
274
|
+
> - Soft delete: extend `SoftDeletableEntity` to get `isDeleted` + `deletedAt` fields
|
|
275
|
+
> - Version strings must be numeric (`'1'`, `'2'`), not semver
|
|
276
|
+
> - WebSocket auth event names are fixed: `auth-login`, `auth-register`, `auth-refresh-token`, `auth-logout`…
|
|
277
|
+
> - CRUD WS events auto-generated: `kebabCase(routeType + '/' + displayedName)`
|
|
278
|
+
> - Ability predicate signature: `(user, body?) => boolean` for auth, `(entity, user) => boolean` for CRUD
|
|
914
279
|
|
|
915
280
|
---
|
|
916
281
|
|
|
917
282
|
## License
|
|
918
283
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
---
|
|
922
|
-
|
|
284
|
+
MIT — see [LICENSE](LICENSE)
|
|
923
285
|
|
|
924
286
|
**Made with ❤️ by [Mickaël NODANCHE](https://cv-mikeonline.web.app)**
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|