@elsikora/nestjs-crud-automator 1.11.0 → 1.11.1-dev.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/README.md +293 -300
- package/dist/cjs/class/api/subscriber/executor.class.d.ts +6 -2
- package/dist/cjs/class/api/subscriber/executor.class.js +32 -4
- package/dist/cjs/class/api/subscriber/executor.class.js.map +1 -1
- package/dist/cjs/decorator/api/function/create.decorator.js +9 -4
- package/dist/cjs/decorator/api/function/create.decorator.js.map +1 -1
- package/dist/cjs/decorator/api/function/delete.decorator.js +9 -4
- package/dist/cjs/decorator/api/function/delete.decorator.js.map +1 -1
- package/dist/cjs/decorator/api/function/get-list.decorator.js +9 -4
- package/dist/cjs/decorator/api/function/get-list.decorator.js.map +1 -1
- package/dist/cjs/decorator/api/function/get-many.decorator.js +9 -4
- package/dist/cjs/decorator/api/function/get-many.decorator.js.map +1 -1
- package/dist/cjs/decorator/api/function/get.decorator.js +9 -4
- package/dist/cjs/decorator/api/function/get.decorator.js.map +1 -1
- package/dist/cjs/decorator/api/function/update.decorator.js +9 -4
- package/dist/cjs/decorator/api/function/update.decorator.js.map +1 -1
- package/dist/cjs/factory/api/controller.factory.js +42 -17
- package/dist/cjs/factory/api/controller.factory.js.map +1 -1
- package/dist/cjs/interface/class/api/subscriber/error-execution-context.interface.d.ts +15 -0
- package/dist/cjs/interface/class/api/subscriber/execution-context.interface.d.ts +5 -1
- package/dist/cjs/interface/class/api/subscriber/function-error-execution-context.interface.d.ts +10 -0
- package/dist/cjs/interface/class/api/subscriber/function-execution-context.interface.d.ts +4 -0
- package/dist/cjs/interface/class/api/subscriber/function.interface.d.ts +13 -12
- package/dist/cjs/interface/class/api/subscriber/index.d.ts +3 -0
- package/dist/cjs/interface/class/api/subscriber/route-error-execution-context.interface.d.ts +10 -0
- package/dist/cjs/interface/class/api/subscriber/route-execution-context.interface.d.ts +4 -0
- package/dist/cjs/interface/class/api/subscriber/route.interface.d.ts +13 -12
- package/dist/esm/class/api/subscriber/executor.class.d.ts +6 -2
- package/dist/esm/class/api/subscriber/executor.class.js +32 -4
- package/dist/esm/class/api/subscriber/executor.class.js.map +1 -1
- package/dist/esm/decorator/api/function/create.decorator.js +9 -4
- package/dist/esm/decorator/api/function/create.decorator.js.map +1 -1
- package/dist/esm/decorator/api/function/delete.decorator.js +9 -4
- package/dist/esm/decorator/api/function/delete.decorator.js.map +1 -1
- package/dist/esm/decorator/api/function/get-list.decorator.js +9 -4
- package/dist/esm/decorator/api/function/get-list.decorator.js.map +1 -1
- package/dist/esm/decorator/api/function/get-many.decorator.js +9 -4
- package/dist/esm/decorator/api/function/get-many.decorator.js.map +1 -1
- package/dist/esm/decorator/api/function/get.decorator.js +9 -4
- package/dist/esm/decorator/api/function/get.decorator.js.map +1 -1
- package/dist/esm/decorator/api/function/update.decorator.js +9 -4
- package/dist/esm/decorator/api/function/update.decorator.js.map +1 -1
- package/dist/esm/factory/api/controller.factory.js +42 -17
- package/dist/esm/factory/api/controller.factory.js.map +1 -1
- package/dist/esm/interface/class/api/subscriber/error-execution-context.interface.d.ts +15 -0
- package/dist/esm/interface/class/api/subscriber/execution-context.interface.d.ts +5 -1
- package/dist/esm/interface/class/api/subscriber/function-error-execution-context.interface.d.ts +10 -0
- package/dist/esm/interface/class/api/subscriber/function-execution-context.interface.d.ts +4 -0
- package/dist/esm/interface/class/api/subscriber/function.interface.d.ts +13 -12
- package/dist/esm/interface/class/api/subscriber/index.d.ts +3 -0
- package/dist/esm/interface/class/api/subscriber/route-error-execution-context.interface.d.ts +10 -0
- package/dist/esm/interface/class/api/subscriber/route-execution-context.interface.d.ts +4 -0
- package/dist/esm/interface/class/api/subscriber/route.interface.d.ts +13 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
</a> <img src="https://img.shields.io/badge/npm-blue.svg?style=for-the-badge&logo=npm&logoColor=white" alt="npm"> <img src="https://img.shields.io/badge/typescript-blue.svg?style=for-the-badge&logo=typescript&logoColor=white" alt="typescript"> <img src="https://img.shields.io/badge/nestjs-red.svg?style=for-the-badge&logo=nestjs&logoColor=white" alt="nestjs"> <img src="https://img.shields.io/badge/swagger-green.svg?style=for-the-badge&logo=swagger&logoColor=white" alt="swagger"> <img src="https://img.shields.io/badge/license-blue.svg?style=for-the-badge&logo=license&logoColor=white" alt="license"> <img src="https://img.shields.io/badge/version-1.6.2-brightgreen.svg?style=for-the-badge&logo=v&logoColor=white" alt="version-1.6.2">
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
|
-
|
|
15
14
|
## 📚 Table of Contents
|
|
15
|
+
|
|
16
16
|
- [Description](#-description)
|
|
17
17
|
- [Features](#-features)
|
|
18
18
|
- [Installation](#-installation)
|
|
@@ -25,13 +25,14 @@
|
|
|
25
25
|
- [FAQ](#-faq)
|
|
26
26
|
- [License](#-license)
|
|
27
27
|
|
|
28
|
-
|
|
29
28
|
## 📖 Description
|
|
29
|
+
|
|
30
30
|
NestJS-Crud-Automator is a comprehensive library designed to eliminate repetitive code when building RESTful APIs with NestJS. It provides a suite of decorators, utilities, and validation tools that automatically generate controllers, DTOs, and service methods for handling Create, Read, Update, and Delete operations. This library significantly reduces development time by providing a declarative approach to API development. By simply describing your entity properties once, the library auto-generates all the necessary boilerplate code including Swagger documentation, validation rules, and transformation logic. Perfect for developers working on data-heavy applications who want to focus on business logic rather than repetitive CRUD implementation.
|
|
31
31
|
|
|
32
32
|
The core philosophy of this library is built on four pillars: being **Declarative** (describe your API, don't code it), writing **Minimum Code** (drastically reduce boilerplate), ensuring **Flexibility** (override or extend any automated behavior), and guaranteeing **Type-Safety** (leverage TypeScript to prevent errors). It achieves this through real-time in-memory code generation, a heavy reliance on decorators for configuration, and smart conventions to reduce setup.
|
|
33
33
|
|
|
34
34
|
## 🚀 Features
|
|
35
|
+
|
|
35
36
|
- ✨ **🏗️ Automatic generation of controllers, DTOs, and service methods for CRUD operations**
|
|
36
37
|
- ✨ **📝 Comprehensive Swagger/OpenAPI documentation generation for all endpoints**
|
|
37
38
|
- ✨ **✅ Built-in validation rules with class-validator integration**
|
|
@@ -49,6 +50,7 @@ The core philosophy of this library is built on four pillars: being **Declarativ
|
|
|
49
50
|
- ✨ **Convention over Configuration:** Smart defaults for service and DTO naming to reduce boilerplate.
|
|
50
51
|
|
|
51
52
|
## 🛠 Installation
|
|
53
|
+
|
|
52
54
|
```bash
|
|
53
55
|
## Installation
|
|
54
56
|
|
|
@@ -82,6 +84,7 @@ npm install @nestjs/common @nestjs/swagger @nestjs/throttler typeorm class-trans
|
|
|
82
84
|
```
|
|
83
85
|
|
|
84
86
|
## 💡 Usage
|
|
87
|
+
|
|
85
88
|
## Basic Usage
|
|
86
89
|
|
|
87
90
|
### 1. Define Your Entity
|
|
@@ -89,57 +92,57 @@ npm install @nestjs/common @nestjs/swagger @nestjs/throttler typeorm class-trans
|
|
|
89
92
|
First, define your entity with the `ApiPropertyDescribe` decorators to provide metadata for CRUD generation:
|
|
90
93
|
|
|
91
94
|
```typescript
|
|
92
|
-
import { Entity, PrimaryGeneratedColumn, Column } from
|
|
93
|
-
import { ApiPropertyDescribe, EApiPropertyDescribeType, EApiPropertyStringType, EApiPropertyDateIdentifier, EApiPropertyDateType } from
|
|
95
|
+
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
|
|
96
|
+
import { ApiPropertyDescribe, EApiPropertyDescribeType, EApiPropertyStringType, EApiPropertyDateIdentifier, EApiPropertyDateType } from "@elsikora/nestjs-crud-automator";
|
|
94
97
|
|
|
95
|
-
@Entity(
|
|
98
|
+
@Entity("users")
|
|
96
99
|
export class UserEntity {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
100
|
+
@PrimaryGeneratedColumn("uuid")
|
|
101
|
+
@ApiPropertyDescribe({
|
|
102
|
+
type: EApiPropertyDescribeType.UUID,
|
|
103
|
+
description: "User unique identifier",
|
|
104
|
+
})
|
|
105
|
+
id: string;
|
|
106
|
+
|
|
107
|
+
@Column()
|
|
108
|
+
@ApiPropertyDescribe({
|
|
109
|
+
type: EApiPropertyDescribeType.STRING,
|
|
110
|
+
description: "User name",
|
|
111
|
+
format: EApiPropertyStringType.STRING,
|
|
112
|
+
minLength: 3,
|
|
113
|
+
maxLength: 50,
|
|
114
|
+
pattern: "/^[a-zA-Z0-9_-]+$/",
|
|
115
|
+
exampleValue: "john_doe",
|
|
116
|
+
})
|
|
117
|
+
username: string;
|
|
118
|
+
|
|
119
|
+
@Column()
|
|
120
|
+
@ApiPropertyDescribe({
|
|
121
|
+
type: EApiPropertyDescribeType.STRING,
|
|
122
|
+
description: "User email",
|
|
123
|
+
format: EApiPropertyStringType.EMAIL,
|
|
124
|
+
minLength: 5,
|
|
125
|
+
maxLength: 255,
|
|
126
|
+
pattern: "/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/",
|
|
127
|
+
exampleValue: "user@example.com",
|
|
128
|
+
})
|
|
129
|
+
email: string;
|
|
130
|
+
|
|
131
|
+
@Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
|
|
132
|
+
@ApiPropertyDescribe({
|
|
133
|
+
type: EApiPropertyDescribeType.DATE,
|
|
134
|
+
identifier: EApiPropertyDateIdentifier.CREATED_AT,
|
|
135
|
+
format: EApiPropertyDateType.DATE_TIME,
|
|
136
|
+
})
|
|
137
|
+
createdAt: Date;
|
|
138
|
+
|
|
139
|
+
@Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP", onUpdate: "CURRENT_TIMESTAMP" })
|
|
140
|
+
@ApiPropertyDescribe({
|
|
141
|
+
type: EApiPropertyDescribeType.DATE,
|
|
142
|
+
identifier: EApiPropertyDateIdentifier.UPDATED_AT,
|
|
143
|
+
format: EApiPropertyDateType.DATE_TIME,
|
|
144
|
+
})
|
|
145
|
+
updatedAt: Date;
|
|
143
146
|
}
|
|
144
147
|
```
|
|
145
148
|
|
|
@@ -148,28 +151,28 @@ export class UserEntity {
|
|
|
148
151
|
Create a service with the `ApiService` decorator to add CRUD operations:
|
|
149
152
|
|
|
150
153
|
```typescript
|
|
151
|
-
import { Injectable } from
|
|
152
|
-
import { InjectRepository } from
|
|
153
|
-
import { Repository } from
|
|
154
|
-
import { ApiService, ApiServiceBase } from
|
|
155
|
-
import { UserEntity } from
|
|
154
|
+
import { Injectable } from "@nestjs/common";
|
|
155
|
+
import { InjectRepository } from "@nestjs/typeorm";
|
|
156
|
+
import { Repository } from "typeorm";
|
|
157
|
+
import { ApiService, ApiServiceBase } from "@elsikora/nestjs-crud-automator";
|
|
158
|
+
import { UserEntity } from "./user.entity";
|
|
156
159
|
|
|
157
160
|
@Injectable()
|
|
158
161
|
@ApiService<UserEntity>({
|
|
159
|
-
|
|
162
|
+
entity: UserEntity,
|
|
160
163
|
})
|
|
161
164
|
export class UserService extends ApiServiceBase<UserEntity> {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
165
|
+
constructor(
|
|
166
|
+
@InjectRepository(UserEntity)
|
|
167
|
+
public repository: Repository<UserEntity>,
|
|
168
|
+
) {
|
|
169
|
+
super();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// You can add custom methods here that go beyond basic CRUD
|
|
173
|
+
async findByEmail(email: string): Promise<UserEntity | undefined> {
|
|
174
|
+
return this.repository.findOne({ where: { email } });
|
|
175
|
+
}
|
|
173
176
|
}
|
|
174
177
|
```
|
|
175
178
|
|
|
@@ -178,41 +181,41 @@ export class UserService extends ApiServiceBase<UserEntity> {
|
|
|
178
181
|
Create a controller with the `ApiController` decorator to generate all CRUD endpoints:
|
|
179
182
|
|
|
180
183
|
```typescript
|
|
181
|
-
import { Controller, UseGuards } from
|
|
182
|
-
import { ApiController, EApiRouteType } from
|
|
183
|
-
import { UserEntity } from
|
|
184
|
-
import { UserService } from
|
|
185
|
-
import { JwtAuthGuard } from
|
|
184
|
+
import { Controller, UseGuards } from "@nestjs/common";
|
|
185
|
+
import { ApiController, EApiRouteType } from "@elsikora/nestjs-crud-automator";
|
|
186
|
+
import { UserEntity } from "./user.entity";
|
|
187
|
+
import { UserService } from "./user.service";
|
|
188
|
+
import { JwtAuthGuard } from "../auth/guards/jwt-auth.guard";
|
|
186
189
|
|
|
187
|
-
@Controller(
|
|
190
|
+
@Controller("users")
|
|
188
191
|
@ApiController<UserEntity>({
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
192
|
+
entity: UserEntity,
|
|
193
|
+
name: "Users",
|
|
194
|
+
routes: {
|
|
195
|
+
[EApiRouteType.CREATE]: {
|
|
196
|
+
authentication: {
|
|
197
|
+
guard: JwtAuthGuard,
|
|
198
|
+
bearerStrategies: ["jwt"],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
[EApiRouteType.UPDATE]: {
|
|
202
|
+
authentication: {
|
|
203
|
+
guard: JwtAuthGuard,
|
|
204
|
+
bearerStrategies: ["jwt"],
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
[EApiRouteType.DELETE]: {
|
|
208
|
+
authentication: {
|
|
209
|
+
guard: JwtAuthGuard,
|
|
210
|
+
bearerStrategies: ["jwt"],
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
[EApiRouteType.GET]: {},
|
|
214
|
+
[EApiRouteType.GET_LIST]: {},
|
|
215
|
+
},
|
|
213
216
|
})
|
|
214
217
|
export class UserController {
|
|
215
|
-
|
|
218
|
+
constructor(public service: UserService) {}
|
|
216
219
|
}
|
|
217
220
|
```
|
|
218
221
|
|
|
@@ -223,28 +226,28 @@ export class UserController {
|
|
|
223
226
|
Add custom validators to your DTOs:
|
|
224
227
|
|
|
225
228
|
```typescript
|
|
226
|
-
import { ApiController, EApiRouteType, EApiDtoType, AllOrNoneOfListedPropertiesValidator } from
|
|
229
|
+
import { ApiController, EApiRouteType, EApiDtoType, AllOrNoneOfListedPropertiesValidator } from "@elsikora/nestjs-crud-automator";
|
|
227
230
|
|
|
228
231
|
@ApiController<UserEntity>({
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
232
|
+
entity: UserEntity,
|
|
233
|
+
name: "Users",
|
|
234
|
+
routes: {
|
|
235
|
+
[EApiRouteType.CREATE]: {
|
|
236
|
+
autoDto: {
|
|
237
|
+
[EApiDtoType.BODY]: {
|
|
238
|
+
validators: [
|
|
239
|
+
{
|
|
240
|
+
constraintClass: AllOrNoneOfListedPropertiesValidator,
|
|
241
|
+
options: ["firstName", "lastName"],
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
},
|
|
245
248
|
})
|
|
246
249
|
export class UserController {
|
|
247
|
-
|
|
250
|
+
constructor(public service: UserService) {}
|
|
248
251
|
}
|
|
249
252
|
```
|
|
250
253
|
|
|
@@ -253,30 +256,30 @@ export class UserController {
|
|
|
253
256
|
Automatically transform request data:
|
|
254
257
|
|
|
255
258
|
```typescript
|
|
256
|
-
import { ApiController, EApiRouteType, EApiDtoType, EApiControllerRequestTransformerType, TRANSFORMER_VALUE_DTO_CONSTANT } from
|
|
259
|
+
import { ApiController, EApiRouteType, EApiDtoType, EApiControllerRequestTransformerType, TRANSFORMER_VALUE_DTO_CONSTANT } from "@elsikora/nestjs-crud-automator";
|
|
257
260
|
|
|
258
261
|
@ApiController<UserEntity>({
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
262
|
+
entity: UserEntity,
|
|
263
|
+
name: "Users",
|
|
264
|
+
routes: {
|
|
265
|
+
[EApiRouteType.CREATE]: {
|
|
266
|
+
request: {
|
|
267
|
+
transformers: {
|
|
268
|
+
[EApiDtoType.BODY]: [
|
|
269
|
+
{
|
|
270
|
+
key: "createdBy",
|
|
271
|
+
type: EApiControllerRequestTransformerType.DYNAMIC,
|
|
272
|
+
value: TRANSFORMER_VALUE_DTO_CONSTANT.AUTHORIZED_ENTITY,
|
|
273
|
+
shouldSetValueEvenIfMissing: true,
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
277
280
|
})
|
|
278
281
|
export class UserController {
|
|
279
|
-
|
|
282
|
+
constructor(public service: UserService) {}
|
|
280
283
|
}
|
|
281
284
|
```
|
|
282
285
|
|
|
@@ -285,34 +288,34 @@ export class UserController {
|
|
|
285
288
|
Automatically load related entities:
|
|
286
289
|
|
|
287
290
|
```typescript
|
|
288
|
-
import { ApiController, EApiRouteType, EApiControllerLoadRelationsStrategy } from
|
|
291
|
+
import { ApiController, EApiRouteType, EApiControllerLoadRelationsStrategy } from "@elsikora/nestjs-crud-automator";
|
|
289
292
|
|
|
290
293
|
@ApiController<PostEntity>({
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
294
|
+
entity: PostEntity,
|
|
295
|
+
name: "Posts",
|
|
296
|
+
routes: {
|
|
297
|
+
[EApiRouteType.GET]: {
|
|
298
|
+
request: {
|
|
299
|
+
relations: {
|
|
300
|
+
shouldLoadRelations: true,
|
|
301
|
+
relationsLoadStrategy: EApiControllerLoadRelationsStrategy.AUTO,
|
|
302
|
+
servicesLoadStrategy: EApiControllerLoadRelationsStrategy.AUTO,
|
|
303
|
+
shouldForceAllServicesToBeSpecified: false,
|
|
304
|
+
relationsToLoad: ["author", "comments"],
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
response: {
|
|
308
|
+
relations: ["author", "comments"],
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
309
312
|
})
|
|
310
313
|
export class PostController {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
314
|
+
constructor(
|
|
315
|
+
public service: PostService,
|
|
316
|
+
public authorService: UserService,
|
|
317
|
+
public commentsService: CommentService,
|
|
318
|
+
) {}
|
|
316
319
|
}
|
|
317
320
|
```
|
|
318
321
|
|
|
@@ -321,31 +324,31 @@ export class PostController {
|
|
|
321
324
|
Use custom DTOs instead of auto-generated ones:
|
|
322
325
|
|
|
323
326
|
```typescript
|
|
324
|
-
import { ApiController, EApiRouteType } from
|
|
325
|
-
import { CreateUserDto } from
|
|
326
|
-
import { UpdateUserDto } from
|
|
327
|
-
import { UserResponseDto } from
|
|
327
|
+
import { ApiController, EApiRouteType } from "@elsikora/nestjs-crud-automator";
|
|
328
|
+
import { CreateUserDto } from "./dto/create-user.dto";
|
|
329
|
+
import { UpdateUserDto } from "./dto/update-user.dto";
|
|
330
|
+
import { UserResponseDto } from "./dto/user-response.dto";
|
|
328
331
|
|
|
329
332
|
@ApiController<UserEntity>({
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
333
|
+
entity: UserEntity,
|
|
334
|
+
name: "Users",
|
|
335
|
+
routes: {
|
|
336
|
+
[EApiRouteType.CREATE]: {
|
|
337
|
+
dto: {
|
|
338
|
+
body: CreateUserDto,
|
|
339
|
+
response: UserResponseDto,
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
[EApiRouteType.UPDATE]: {
|
|
343
|
+
dto: {
|
|
344
|
+
body: UpdateUserDto,
|
|
345
|
+
response: UserResponseDto,
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
},
|
|
346
349
|
})
|
|
347
350
|
export class UserController {
|
|
348
|
-
|
|
351
|
+
constructor(public service: UserService) {}
|
|
349
352
|
}
|
|
350
353
|
```
|
|
351
354
|
|
|
@@ -364,15 +367,16 @@ To simplify debugging and request tracing in complex systems, the library provid
|
|
|
364
367
|
This allows you to link a specific client request with the logs on the server, which is invaluable when investigating incidents.
|
|
365
368
|
|
|
366
369
|
**Registration:** `main.ts`
|
|
370
|
+
|
|
367
371
|
```typescript
|
|
368
|
-
import { CorrelationIDResponseBodyInterceptor } from
|
|
372
|
+
import { CorrelationIDResponseBodyInterceptor } from "@elsikora/nestjs-crud-automator";
|
|
369
373
|
|
|
370
374
|
async function bootstrap() {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
375
|
+
const app = await NestFactory.create(AppModule);
|
|
376
|
+
// ...
|
|
377
|
+
app.useGlobalInterceptors(new CorrelationIDResponseBodyInterceptor());
|
|
378
|
+
// ...
|
|
379
|
+
await app.listen(3000);
|
|
376
380
|
}
|
|
377
381
|
```
|
|
378
382
|
|
|
@@ -380,59 +384,59 @@ async function bootstrap() {
|
|
|
380
384
|
|
|
381
385
|
This is the most powerful feature for extending the default behavior. It allows you to "subscribe" to events in the CRUD request lifecycle and execute your code before, after, or in case of an error in the main operation. This is an ideal solution for tasks such as:
|
|
382
386
|
|
|
383
|
-
-
|
|
384
|
-
-
|
|
385
|
-
-
|
|
386
|
-
-
|
|
387
|
-
-
|
|
387
|
+
- Auditing.
|
|
388
|
+
- Sending notifications.
|
|
389
|
+
- Complex, context-dependent validation.
|
|
390
|
+
- Data enrichment before saving.
|
|
391
|
+
- Custom error handling.
|
|
388
392
|
|
|
389
393
|
#### Enabling the Subscriber System
|
|
390
394
|
|
|
391
395
|
To get the subscriber system working, you need to follow **three mandatory steps**:
|
|
392
396
|
|
|
393
|
-
1. **Import `ApiSubscriberModule`**: This module provides the `ApiSubscriberDiscoveryService`, which is responsible for discovering your subscribers. You need to import it into the root module of your application.
|
|
394
|
-
|
|
397
|
+
1. **Import `ApiSubscriberModule`**: This module provides the `ApiSubscriberDiscoveryService`, which is responsible for discovering your subscribers. You need to import it into the root module of your application. `app.module.ts`
|
|
398
|
+
|
|
395
399
|
```typescript
|
|
396
400
|
import { ApiSubscriberModule } from "@elsikora/nestjs-crud-automator";
|
|
397
401
|
|
|
398
402
|
@Module({
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
403
|
+
imports: [
|
|
404
|
+
// ... other modules
|
|
405
|
+
ApiSubscriberModule, // <--- IMPORTANT
|
|
406
|
+
],
|
|
407
|
+
// ...
|
|
404
408
|
})
|
|
405
409
|
export class AppModule {}
|
|
406
410
|
```
|
|
407
411
|
|
|
408
412
|
2. **Make the controller "observable"**: Add the `@ApiControllerObservable()` decorator to the controller class whose events you want to monitor.
|
|
413
|
+
|
|
409
414
|
```typescript
|
|
410
415
|
import { ApiController, ApiControllerObservable } from "@elsikora/nestjs-crud-automator";
|
|
411
416
|
|
|
412
417
|
@Controller("posts")
|
|
413
418
|
@ApiController({
|
|
414
|
-
|
|
419
|
+
/* ... */
|
|
415
420
|
})
|
|
416
421
|
@ApiControllerObservable() // <--- IMPORTANT
|
|
417
422
|
export class PostController {
|
|
418
|
-
|
|
423
|
+
/* ... */
|
|
419
424
|
}
|
|
420
425
|
```
|
|
421
426
|
|
|
422
|
-
3. **Make the service "observable"**: Similarly, add the `@ApiServiceObservable()` decorator to the service class.
|
|
423
|
-
```typescript
|
|
424
|
-
import { ApiService, ApiServiceBase, ApiServiceObservable } from "@elsikora/nestjs-crud-automator";
|
|
427
|
+
3. **Make the service "observable"**: Similarly, add the `@ApiServiceObservable()` decorator to the service class. ```typescript import { ApiService, ApiServiceBase, ApiServiceObservable } from "@elsikora/nestjs-crud-automator";
|
|
425
428
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
429
|
+
@Injectable()
|
|
430
|
+
@ApiService({
|
|
431
|
+
/* ... */
|
|
432
|
+
})
|
|
433
|
+
@ApiServiceObservable() // <--- IMPORTANT
|
|
434
|
+
export class PostService extends ApiServiceBase<Post> {
|
|
435
|
+
/* ... */
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Without these steps, your subscriber classes will simply not be discovered and called.
|
|
436
440
|
|
|
437
441
|
#### Two Levels of Interception
|
|
438
442
|
|
|
@@ -463,6 +467,7 @@ In case of an error at any stage, execution is interrupted, and the correspondin
|
|
|
463
467
|
**Task**: Log which user created which post.
|
|
464
468
|
|
|
465
469
|
1. **Create the subscriber:** `post-audit.subscriber.ts`
|
|
470
|
+
|
|
466
471
|
```typescript
|
|
467
472
|
import { Injectable } from "@nestjs/common";
|
|
468
473
|
import { ApiRouteSubscriber, ApiRouteSubscriberBase, IApiSubscriberRouteExecutionContext } from "@elsikora/nestjs-crud-automator";
|
|
@@ -472,18 +477,18 @@ In case of an error at any stage, execution is interrupted, and the correspondin
|
|
|
472
477
|
@Injectable()
|
|
473
478
|
@ApiRouteSubscriber({ entity: Post, priority: 10 }) // Specify entity and priority
|
|
474
479
|
export class PostAuditSubscriber extends ApiRouteSubscriberBase<Post> {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
480
|
+
// Hook is called AFTER a post is successfully created in the controller
|
|
481
|
+
async onAfterCreate(context: IApiSubscriberRouteExecutionContext<Post, Post, { user: User }>): Promise<Post> {
|
|
482
|
+
const createdPost = context.result; // Result of the controller's operation
|
|
483
|
+
const currentUser = context.data.user; // Immutable input data, including request.user
|
|
484
|
+
|
|
485
|
+
if (createdPost && currentUser) {
|
|
486
|
+
console.log(`AUDIT: User ${currentUser.id} created Post ${createdPost.id} with title "${createdPost.title}"`);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// We don't want to change the result, so we just return it
|
|
490
|
+
return createdPost;
|
|
491
|
+
}
|
|
487
492
|
}
|
|
488
493
|
```
|
|
489
494
|
|
|
@@ -494,6 +499,7 @@ In case of an error at any stage, execution is interrupted, and the correspondin
|
|
|
494
499
|
**Task**: When creating a post, automatically generate a `slug` from the `title` before saving it to the database.
|
|
495
500
|
|
|
496
501
|
1. **Create the subscriber:** `post-slug.subscriber.ts`
|
|
502
|
+
|
|
497
503
|
```typescript
|
|
498
504
|
import { Injectable } from "@nestjs/common";
|
|
499
505
|
import { ApiFunctionSubscriber, ApiFunctionSubscriberBase, IApiSubscriberFunctionExecutionContext, TApiFunctionCreateProperties } from "@elsikora/nestjs-crud-automator";
|
|
@@ -503,19 +509,19 @@ In case of an error at any stage, execution is interrupted, and the correspondin
|
|
|
503
509
|
@Injectable()
|
|
504
510
|
@ApiFunctionSubscriber({ entity: Post })
|
|
505
511
|
export class PostSlugSubscriber extends ApiFunctionSubscriberBase<Post> {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
512
|
+
// Hook is called BEFORE repository.save() is called
|
|
513
|
+
async onBeforeCreate(context: IApiSubscriberFunctionExecutionContext<Post, TApiFunctionCreateProperties<Post>>): Promise<TApiFunctionCreateProperties<Post>> {
|
|
514
|
+
const postData = context.result; // This is the object that will go into repository.save()
|
|
515
|
+
|
|
516
|
+
if (postData.body.title) {
|
|
517
|
+
// Modify the object, adding the slug
|
|
518
|
+
postData.body.slug = slugify(postData.body.title, { lower: true, strict: true });
|
|
519
|
+
console.log(`ENRICHMENT: Generated slug: ${postData.body.slug}`);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Return the modified object, which will be saved
|
|
523
|
+
return postData;
|
|
524
|
+
}
|
|
519
525
|
}
|
|
520
526
|
```
|
|
521
527
|
|
|
@@ -535,24 +541,19 @@ In case of an error at any stage, execution is interrupted, and the correspondin
|
|
|
535
541
|
The library automatically generates Swagger/OpenAPI documentation for all endpoints. To enable it in your NestJS application:
|
|
536
542
|
|
|
537
543
|
```typescript
|
|
538
|
-
import { NestFactory } from
|
|
539
|
-
import { DocumentBuilder, SwaggerModule } from
|
|
540
|
-
import { AppModule } from
|
|
544
|
+
import { NestFactory } from "@nestjs/core";
|
|
545
|
+
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
|
546
|
+
import { AppModule } from "./app.module";
|
|
541
547
|
|
|
542
548
|
async function bootstrap() {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const document = SwaggerModule.createDocument(app, config);
|
|
553
|
-
SwaggerModule.setup('api', app, document);
|
|
554
|
-
|
|
555
|
-
await app.listen(3000);
|
|
549
|
+
const app = await NestFactory.create(AppModule);
|
|
550
|
+
|
|
551
|
+
const config = new DocumentBuilder().setTitle("Your API").setDescription("API description").setVersion("1.0").addBearerAuth().build();
|
|
552
|
+
|
|
553
|
+
const document = SwaggerModule.createDocument(app, config);
|
|
554
|
+
SwaggerModule.setup("api", app, document);
|
|
555
|
+
|
|
556
|
+
await app.listen(3000);
|
|
556
557
|
}
|
|
557
558
|
bootstrap();
|
|
558
559
|
```
|
|
@@ -568,37 +569,39 @@ The library provides advanced filtering capabilities for list endpoints:
|
|
|
568
569
|
This query would search for users with "john" in their username and created between Jan 1 and Dec 31, 2023.
|
|
569
570
|
|
|
570
571
|
## 🛣 Roadmap
|
|
572
|
+
|
|
571
573
|
## Roadmap
|
|
572
574
|
|
|
573
|
-
| Task / Feature
|
|
574
|
-
|
|
575
|
-
| Core CRUD operations
|
|
576
|
-
| TypeORM integration
|
|
577
|
-
| Swagger/OpenAPI documentation
|
|
578
|
-
| Validation with class-validator
|
|
579
|
-
| Transformation with class-transformer
|
|
580
|
-
| Advanced filtering for GET_LIST operation
|
|
581
|
-
| Authentication guard integration
|
|
582
|
-
| Request/response transformers
|
|
583
|
-
| Relation loading strategies
|
|
584
|
-
| Custom validator integration
|
|
585
|
-
| Pagination support
|
|
586
|
-
| Error handling with standardized responses | ✅ Done
|
|
587
|
-
| Support for TypeScript decorators
|
|
588
|
-
| Support for ESM and CommonJS modules
|
|
589
|
-
| Subscriber System
|
|
590
|
-
| MongoDB support
|
|
591
|
-
| GraphQL integration
|
|
592
|
-
| Support for soft deletes
|
|
593
|
-
| Role-based access control
|
|
594
|
-
| Cache integration
|
|
595
|
-
| Audit logging middleware
|
|
575
|
+
| Task / Feature | Status |
|
|
576
|
+
| ------------------------------------------ | -------------- |
|
|
577
|
+
| Core CRUD operations | ✅ Done |
|
|
578
|
+
| TypeORM integration | ✅ Done |
|
|
579
|
+
| Swagger/OpenAPI documentation | ✅ Done |
|
|
580
|
+
| Validation with class-validator | ✅ Done |
|
|
581
|
+
| Transformation with class-transformer | ✅ Done |
|
|
582
|
+
| Advanced filtering for GET_LIST operation | ✅ Done |
|
|
583
|
+
| Authentication guard integration | ✅ Done |
|
|
584
|
+
| Request/response transformers | ✅ Done |
|
|
585
|
+
| Relation loading strategies | ✅ Done |
|
|
586
|
+
| Custom validator integration | ✅ Done |
|
|
587
|
+
| Pagination support | ✅ Done |
|
|
588
|
+
| Error handling with standardized responses | ✅ Done |
|
|
589
|
+
| Support for TypeScript decorators | ✅ Done |
|
|
590
|
+
| Support for ESM and CommonJS modules | ✅ Done |
|
|
591
|
+
| Subscriber System | ✅ Done |
|
|
592
|
+
| MongoDB support | 🚧 In Progress |
|
|
593
|
+
| GraphQL integration | 🚧 In Progress |
|
|
594
|
+
| Support for soft deletes | 🚧 In Progress |
|
|
595
|
+
| Role-based access control | 🚧 In Progress |
|
|
596
|
+
| Cache integration | 🚧 In Progress |
|
|
597
|
+
| Audit logging middleware | 🚧 In Progress |
|
|
596
598
|
| Bulk operations (create many, update many) | 🚧 In Progress |
|
|
597
|
-
| Query complexity analyzer
|
|
598
|
-
| Rate limiting enhancements
|
|
599
|
-
| Custom parameter decorators
|
|
599
|
+
| Query complexity analyzer | 🚧 In Progress |
|
|
600
|
+
| Rate limiting enhancements | 🚧 In Progress |
|
|
601
|
+
| Custom parameter decorators | 🚧 In Progress |
|
|
600
602
|
|
|
601
603
|
## ❓ FAQ
|
|
604
|
+
|
|
602
605
|
## Frequently Asked Questions
|
|
603
606
|
|
|
604
607
|
### How does NestJS-Crud-Automator compare to @nestjsx/crud?
|
|
@@ -608,6 +611,7 @@ While @nestjsx/crud provides similar functionality, NestJS-Crud-Automator offers
|
|
|
608
611
|
### Can I customize the generated endpoints?
|
|
609
612
|
|
|
610
613
|
Yes! The library provides multiple ways to customize your endpoints:
|
|
614
|
+
|
|
611
615
|
1. You can disable specific routes
|
|
612
616
|
2. Add authentication guards to specific routes
|
|
613
617
|
3. Customize DTO validation and transformation
|
|
@@ -635,24 +639,13 @@ The core library doesn't include file upload functionality, but you can easily e
|
|
|
635
639
|
Yes, as long as your repository follows the TypeORM Repository pattern, it will work with NestJS-Crud-Automator.
|
|
636
640
|
|
|
637
641
|
## 🔒 License
|
|
638
|
-
|
|
642
|
+
|
|
643
|
+
This project is licensed under \*\*MIT License
|
|
639
644
|
|
|
640
645
|
Copyright (c) 2025 ElsiKora
|
|
641
646
|
|
|
642
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
643
|
-
|
|
644
|
-
in
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
furnished to do so, subject to the following conditions:
|
|
648
|
-
|
|
649
|
-
The above copyright notice and this permission notice shall be included in all
|
|
650
|
-
copies or substantial portions of the Software.
|
|
651
|
-
|
|
652
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
653
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
654
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
655
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
656
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
657
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
658
|
-
SOFTWARE.**.
|
|
647
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
648
|
+
|
|
649
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
650
|
+
|
|
651
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\*\*.
|