mongodb-dynamic-api 1.4.0 → 1.4.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/README.md CHANGED
@@ -47,38 +47,35 @@
47
47
 
48
48
  ---
49
49
 
50
+ ## mongodb-dynamic-api <img src="https://pbs.twimg.com/media/EDoWJbUXYAArclg.png" width="24" height="24" />
51
+ ```text
52
+ npm install --save mongodb-dynamic-api
53
+ ```
54
+
55
+ ---
56
+
50
57
  <div style="text-align: center; width: 100%;">
51
58
 
52
- # mongodb-dynamic-api
59
+ # Dynamic API Module
53
60
 
54
61
  </div>
55
62
 
56
63
 
57
64
  <p style="text-align: justify; width: 100%;font-size: 15px;">
58
65
 
59
- **mongodb-dynamic-api** is an auto generated CRUD API for MongoDB using NestJS 10.
66
+ In summary, DynamicApiModule is a flexible and configurable module using NestJS 10 that provides dynamic API functionality.
67
+ <br>It must be set up at the root level with global settings and then configured for individual features.
68
+ <br>It has several optional features such as
69
+ Swagger UI,
70
+ Authentication (JWT),
71
+ Authorization (Casl),
72
+ Validation (Class Validator)
73
+ and Caching (cache-manager).
60
74
 
61
75
  </p>
62
76
 
63
- ---
64
-
65
- ## npm package <img src="https://pbs.twimg.com/media/EDoWJbUXYAArclg.png" width="24" height="24" />
66
- ```text
67
- npm install --save mongodb-dynamic-api
68
- ```
69
-
70
77
  ___
71
78
 
72
- ### Table of Contents
73
-
74
- [Swagger UI](#swagger-ui--optional-but-strongly-recommended)
75
- <br>[Validation](#validation--optional)
76
- <br>[Versioning](#versioning--optional)
77
- <br>[Caching](#caching--enabled-by-default)
78
- <br>[Authentication](#authentication--optional)
79
- <br>[Casl](#casl--only-with-authentication)
80
-
81
- ---
82
79
  ### HOW TO ENJOY IT
83
80
 
84
81
  - Start a new [nest](https://docs.nestjs.com/) project with **typescript** (use the `--strict` option)
@@ -86,7 +83,7 @@ ___
86
83
  nest new --strict your-project-name
87
84
  ```
88
85
 
89
- - Go to your project root and install the [mongodb-dynamic-api](https://www.npmjs.com/package/mongodb-dynamic-api) package
86
+ - Go to your new project root and install the [mongodb-dynamic-api](https://www.npmjs.com/package/mongodb-dynamic-api) package
90
87
  ```text
91
88
  npm i -S mongodb-dynamic-api
92
89
  ```
@@ -95,13 +92,13 @@ npm i -S mongodb-dynamic-api
95
92
  - Add `DynamicApiModule.forRoot` to your `app.module.ts` and pass your **MongoDB connection string** to the method.
96
93
 
97
94
  ```typescript
98
- // app.module.ts
95
+ // src/app.module.ts
99
96
  import { DynamicApiModule } from 'mongodb-dynamic-api';
100
97
 
101
98
  @Module({
102
99
  imports: [
103
100
  DynamicApiModule.forRoot(
104
- 'mongodb://127.0.0.1:27017/dynamic-api-db', // <- replace by your own connection string
101
+ 'mongodb-uri', // <- replace by your own MongoDB connection string
105
102
  ),
106
103
  // ...
107
104
  ],
@@ -113,19 +110,18 @@ export class AppModule {}
113
110
  **Basic Usage**
114
111
 
115
112
  - Ok, now let's add our first content with just 2 files. It will be a simple `User` with a `name` and an `email` field.
116
- - We use the `@Schema` and `@Prop` decorators from the `@nestjs/mongoose` package to define our MongoDB model. <br>*See <strong>nestjs</strong> <a href="https://docs.nestjs.com/techniques/mongodb#model-injection" target="_blank">documentation</a> for more details.*
117
-
113
+ - We use the `@Schema` and `@Prop` decorators from the <a href="https://docs.nestjs.com/techniques/mongodb#model-injection" target="_blank">@nestjs/mongoose</a> package to define our MongoDB model.
118
114
 
119
- - You must extend the `BaseEntity` (or `SoftDeletableEntity`) class from the `mongodb-dynamic-api` package **for all your collection models**.
115
+ - You must extend the `BaseEntity` class from the `mongodb-dynamic-api` package **for all your collection models**.
120
116
  - Just create a new file `user.ts` and add the following code.
121
117
 
122
118
  ```typescript
123
- // users/user.ts
119
+ // src/users/user.ts
124
120
  import { Prop, Schema } from '@nestjs/mongoose';
125
121
  import { BaseEntity } from 'mongodb-dynamic-api';
126
122
 
127
123
  @Schema({ collection: 'users' })
128
- export class User extends BaseEntity {
124
+ export class User extends BaseEntity { // <- extends BaseEntity
129
125
  @Prop({ type: String, required: true })
130
126
  name: string;
131
127
 
@@ -134,12 +130,12 @@ export class User extends BaseEntity {
134
130
  }
135
131
  ```
136
132
 
137
- - Then we will use the `DynamicApiModule.forFeature` method to add the `User` content.
133
+ - Then we will use the `DynamicApiModule.forFeature` method to add the `User` API to our application.
138
134
  - We pass the `User` class to the `entity` property and specify the path `users` to the `controllerOptions` property.
139
135
  - Create a new file `users.module.ts` and add the following code.
140
136
 
141
137
  ```typescript
142
- // users/users.module.ts
138
+ // src/users/users.module.ts
143
139
  import { DynamicApiModule } from 'mongodb-dynamic-api';
144
140
  import { User } from './user';
145
141
 
@@ -159,14 +155,15 @@ export class UsersModule {}
159
155
  - Last step, add the `UsersModule` to the **imports** in the `app.module.ts` after the `DynamicApiModule.forRoot` method.
160
156
 
161
157
  ```typescript
162
- // app.module.ts
158
+ // src/app.module.ts
163
159
  import { DynamicApiModule } from 'mongodb-dynamic-api';
164
160
  import { UsersModule } from './users/users.module';
165
161
 
166
162
  @Module({
167
163
  imports: [
168
- DynamicApiModule.forRoot('...'),
169
- // ...
164
+ DynamicApiModule.forRoot(
165
+ 'mongodb-uri', // <- replace by your own MongoDB connection string
166
+ ),
170
167
  UsersModule,
171
168
  ],
172
169
  controllers: [AppController],
@@ -178,746 +175,34 @@ export class AppModule {}
178
175
  **And that's all !** *You now have a fully functional CRUD API for the `User` content at the `/users` path.*
179
176
 
180
177
 
181
-
182
178
  | Endpoint | Body | Param | Query |
183
179
  |:--------------------------------------------------|:----------------------------------------------:|:------------:|:---------------:|
184
- | **GET /users** <br>*Get many* | x | x | x |
185
- | **GET /users/:id** <br>*Get one* | x | `id: string` | x |
186
- | **POST /users/many** <br>*Create many* | `{ list: [{ name: string; email: string; }] }` | x | x |
187
- | **POST /users** <br>*Create one* | `{ name: string; email: string; }` | x | x |
188
- | **PUT /users/:id** <br>*Replace one* | `{ name: string; email: string; }` | `id: string` | x |
189
- | **PATCH /users** <br>*Update many* | `{ name?: string; email?: string; }` | x | `ids: string[]` |
190
- | **PATCH /users/:id** <br>*Update one* | `{ name?: string; email?: string; }` | `id: string` | x |
191
- | **DELETE /users** <br>*Delete many* | x | x | `ids: string[]` |
192
- | **DELETE /users/:id** <br>*Delete one* | x | `id: string` | x |
193
- | **POST /users/duplicate** <br>*Duplicate many* | `{ name?: string; email?: string; }` | x | `ids: string[]` |
194
- | **POST /users/duplicate/:id**<br>*Duplicate one* | `{ name?: string; email?: string; }` | `id: string` | x |
195
-
196
-
197
- ### [Swagger UI](https://docs.nestjs.com/openapi/introduction#document-options) (optional but strongly recommended)
198
- `function enableDynamicAPISwagger(app: INestApplication, options?: DynamicAPISwaggerOptions): void`
199
-
200
- **Configuration**
201
-
202
- ```typescript
203
- // main.ts
204
- import { enableDynamicAPISwagger } from 'mongodb-dynamic-api';
205
-
206
- async function bootstrap() {
207
- const app = await NestFactory.create(AppModule);
208
- // ...
209
- enableDynamicAPISwagger(app); // <- add this line in your main.ts file
210
-
211
- await app.listen(3000);
212
- }
213
- ```
214
-
215
- The `enableDynamicAPISwagger` function will automatically build the swagger documentation.
216
- <br>This method can be called with optional parameters to specify more documentation options.
217
- <br>*See <strong>nestjs</strong> <a href="https://docs.nestjs.com/openapi/introduction#document-options" target="_blank">documentation</a> for more details.*
218
-
219
- **Usage**
220
-
221
- Add the `@ApiProperty` | `@ApiPropertyOptional` decorators to your class properties to have a better swagger documentation.
222
- <br>Let's add an optional company field to the `User` class.
223
-
224
- ```typescript
225
- // user.ts
226
- import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
227
-
228
- @Schema({ collection: 'users' })
229
- export class User extends BaseEntity {
230
- @ApiProperty() // <- add this line
231
- @Prop({ type: String, required: true })
232
- name: string;
233
-
234
- @ApiProperty() // <- add this line
235
- @Prop({ type: String, required: true })
236
- email: string;
237
-
238
- @ApiPropertyOptional() // <- add this line
239
- @Prop({ type: String })
240
- company?: string;
241
- }
242
- ```
243
-
244
- go to the swagger API path (default to `/dynamic-api`) and you will see the auto generated API
245
-
246
- ![User API](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/images/dynamic-api-user-full.Jpeg?raw=true "User API")
247
-
248
- <a href="https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/swagger-user-api.md" target="_blank">See more User API screenshots</a>
180
+ | GET **/users** <br>*Get many* | x | x | x |
181
+ | GET **/users/:id** <br>*Get one* | x | `id: string` | x |
182
+ | POST **/users/many** <br>*Create many* | `{ list: [{ name: string; email: string; }] }` | x | x |
183
+ | POST **/users** <br>*Create one* | `{ name: string; email: string; }` | x | x |
184
+ | PUT **/users/:id** <br>*Replace one* | `{ name: string; email: string; }` | `id: string` | x |
185
+ | PATCH **/users** <br>*Update many* | `{ name?: string; email?: string; }` | x | `ids: string[]` |
186
+ | PATCH **/users/:id** <br>*Update one* | `{ name?: string; email?: string; }` | `id: string` | x |
187
+ | DELETE **/users** <br>*Delete many* | x | x | `ids: string[]` |
188
+ | DELETE **/users/:id** <br>*Delete one* | x | `id: string` | x |
189
+ | POST **/users/duplicate** <br>*Duplicate many* | `{ name?: string; email?: string; }` | x | `ids: string[]` |
190
+ | POST **/users/duplicate/:id**<br>*Duplicate one* | `{ name?: string; email?: string; }` | `id: string` | x |
249
191
 
250
192
  ___
251
- ### [Validation](https://docs.nestjs.com/techniques/validation#using-the-built-in-validationpipe) (optional)
252
- `function enableDynamicAPIValidation(app: INestApplication, options?: ValidationPipeOptions): void`
253
-
254
- **Configuration**
255
-
256
- ```typescript
257
- // main.ts
258
- import { enableDynamicAPIValidation } from 'mongodb-dynamic-api';
259
-
260
- async function bootstrap() {
261
- const app = await NestFactory.create(AppModule);
262
- // ...
263
- enableDynamicAPIValidation(app); // <- add this line in your main.ts file
264
-
265
- await app.listen(3000);
266
- }
267
- ```
268
- The `enableDynamicAPIValidation` function allow to configure the pipe validation options for the API globally.
269
-
270
- You can also define the pipe validation options in the `DynamicApiModule.forFeature` method, either in the controller options,
271
- or in each route object defined in the routes property.
272
- <br>*If the options are specified in 2, the options specified in the route will have priority.*
273
-
274
- ```typescript
275
- // users.module.ts
276
- import { DynamicApiModule } from 'mongodb-dynamic-api';
277
- import { User } from './user';
278
-
279
- @Module({
280
- imports: [
281
- DynamicApiModule.forFeature({
282
- entity: User,
283
- controllerOptions: {
284
- // ...
285
- validationPipeOptions: { // <- in the controller options
286
- transform: true,
287
- whitelist: true,
288
- forbidNonWhitelisted: true,
289
- },
290
- },
291
- routes: [
292
- {
293
- type: 'DuplicateOne',
294
- validationPipeOptions: { transform: true }, // <- in the route options
295
- },
296
- ],
297
- }),
298
- ],
299
- })
300
- export class UsersModule {}
301
- ```
302
-
303
- **Usage**
304
-
305
- Use the `Class validator` <a href="https://github.com/typestack/class-validator?tab=readme-ov-file#validation-decorators" target="_blank">decorators</a> to validate your class properties.
306
- <br>Let's add `IsEmail` decorator to the `email` field.
307
-
308
- ```typescript
309
- // user.ts
310
- import { IsEmail } from 'class-validator';
311
-
312
- @Schema({ collection: 'users' })
313
- export class User extends BaseEntity {
314
- @ApiProperty()
315
- @Prop({ type: String, required: true })
316
- name: string;
317
-
318
- @ApiProperty()
319
- @IsEmail()
320
- @Prop({ type: String, required: true })
321
- email: string;
322
193
 
323
- @ApiPropertyOptional()
324
- @Prop({ type: String })
325
- company?: string;
326
- }
327
- ```
328
-
329
- Ok, now if you try to create a new user with an invalid email, you will get a `400 Bad Request` error.
330
-
331
- ![User API Validation](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/images/dynamic-api-validation.Jpeg?raw=true "User API Validation")
332
-
333
-
334
- ___
335
- ### [Versioning](https://docs.nestjs.com/techniques/versioning) (optional)
336
- `function enableDynamicAPIVersioning(app: INestApplication, options?: VersioningOptions): void`
194
+ Go further with optional features like **Swagger UI**, **Validation**, **Caching**, **Authentication** and **Authorization**.
337
195
 
338
- The `enableDynamicAPIVersioning` function will automatically add versioning to the API.
339
- <br>By default, it will use the <strong>URI versioning type</strong>.
340
- <br>This method can be called with a second <strong>optional parameter</strong> to specify custom options.
341
- <br>*See <strong>nestjs</strong> <a href="https://docs.nestjs.com/techniques/versioning" target="_blank">documentation</a> for more details.*
342
-
343
- **Configuration**
344
-
345
- ```typescript
346
- // main.ts
347
- import { enableDynamicAPIVersioning } from 'mongodb-dynamic-api';
348
-
349
- async function bootstrap() {
350
- const app = await NestFactory.create(AppModule);
351
- // ...
352
- enableDynamicAPIVersioning(app); // <- add this line in your main.ts file
353
-
354
- await app.listen(3000);
355
- }
356
- ```
357
-
358
- **Usage**
359
-
360
- Pass the `version` property to the `controllerOptions` object or to the `route` object in the `DynamicApiModule.forFeature` method.
361
- <br>*If the version is specified in 2, the version specified in the route will have priority.*
362
-
363
- Let's add a new version to the `User` content.
364
-
365
- ```typescript
366
- // create-one-user-v2.dto.ts
367
- import { ApiProperty, ApiPropertyOptional, PickType } from '@nestjs/swagger';
368
- import { IsOptional, IsString } from 'class-validator';
369
- import { User } from './user';
370
-
371
- export class CreateOneUserV2Dto extends PickType(User, ['email']) {
372
- @ApiProperty()
373
- @IsString()
374
- fullName: string;
375
-
376
- @ApiProperty()
377
- @IsString()
378
- phoneNumber: string;
379
-
380
- @ApiPropertyOptional()
381
- @IsString()
382
- @IsOptional()
383
- country?: string;
384
- }
385
- ```
386
-
387
- ```typescript
388
- // user-v2.presenter.ts
389
- import { ApiProperty, ApiPropertyOptional, PickType } from '@nestjs/swagger';
390
- import { User } from './user';
391
-
392
- export class UserV2Presenter extends PickType(User, ['email']) {
393
- @ApiProperty()
394
- fullName: string;
395
-
396
- @ApiProperty()
397
- phoneNumber: string;
398
-
399
- @ApiPropertyOptional()
400
- country?: string;
401
- }
402
- ```
403
-
404
- ```typescript
405
- // users.module.ts
406
- import { DynamicApiModule } from 'mongodb-dynamic-api';
407
- import { User } from './user';
408
- import { CreateOneUserV2Dto } from './create-one-user-v2.dto';
409
- import { UserV2Presenter } from './user-v2.presenter';
410
-
411
- @Module({
412
- imports: [
413
- DynamicApiModule.forFeature({
414
- entity: User,
415
- controllerOptions: {
416
- path: 'users',
417
- version: '1', // <- add this line
418
- },
419
- routes: [
420
- { type: 'GetMany' },
421
- { type: 'GetOne' },
422
- {
423
- type: 'CreateOne',
424
- dTOs: {
425
- body: CreateOneUserV2Dto,
426
- presenter: UserV2Presenter,
427
- },
428
- version: '2', // <- add this line
429
- },
430
- ],
431
- }),
432
- ],
433
- })
434
- export class UsersModule {}
435
- ```
436
-
437
- Great, now you have a versioned User API, and you can access it at the `/v1/users` and `/v2/users` paths.
438
-
439
- ![User API Versioned](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/images/dynamic-api-versioning.Jpeg?raw=true "User API Versioned")
440
-
441
-
442
- ___
443
-
444
- ### [Caching](https://docs.nestjs.com/techniques/caching#in-memory-cache) (enabled by default)
445
-
446
- By default, the caching is activated globally for all the routes. It uses the nestjs built-in in-memory data store with the default options.
447
- <br>You can configure the cache options by passing the `cacheOptions` in the second parameter of the `DynamicApiModule.forRoot` method.
448
-
449
- **Configuration**
450
-
451
- ```typescript
452
- // app.module.ts
453
- import { DynamicApiModule } from 'mongodb-dynamic-api';
454
-
455
- @Module({
456
- imports: [
457
- DynamicApiModule.forRoot('...', {
458
- cacheOptions: {
459
- ttl: 60, // <- The time to live in milliseconds. This is the maximum amount of time that an item can be in the cache before it is removed.
460
- max: 100, // <- The maximum number of items that can be stored in the cache.
461
- },
462
- }),
463
- // ...
464
- ],
465
- controllers: [AppController],
466
- providers: [AppService],
467
- })
468
- export class AppModule {}
469
- ```
470
-
471
- *See <strong>nestjs</strong> <a href="https://docs.nestjs.com/techniques/caching" target="_blank">documentation</a> for more details.*
472
-
473
- **[Not recommended]** The cache can also be disabled globally with the `useGlobalCache` property set to `false` in the `DynamicApiModule.forRoot` method.
474
-
475
- ```typescript
476
- // app.module.ts
477
- import { DynamicApiModule } from 'mongodb-dynamic-api';
478
-
479
- @Module({
480
- imports: [
481
- DynamicApiModule.forRoot('...', {
482
- useGlobalCache: false, // <- add this line
483
- }),
484
- // ...
485
- ],
486
- controllers: [AppController],
487
- providers: [AppService],
488
- })
489
- export class AppModule {}
490
- ```
491
-
492
- **Usage**
493
-
494
- When you request the `/users` route with the `GET` method, the response will be cached until the cache expires or until the maximum number of items is reached or until a request like `POST`, `PUT`, `PATCH`, or `DELETE` is made on the same route.
495
- <br><br>Let's inspect the behavior in the network tab of our browser
496
- <br>We expected to see a code `200` for the first GET request, a code `304`* for the second GET request, and again a code `200` for the third GET request made after the POST request.
497
-
498
- **The 304 Not Modified redirect response code indicates that there is no need to retransmit the requested resources. This is an implicit redirection to a cached resource.*
499
-
500
- ```text
501
- 1. GET /users
502
- ```
503
- ![First GET request](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/images/dynamic-api-caching-1-GET-first-request.Jpeg?raw=true "First GET request")
504
-
505
- ```text
506
- 2. GET /users
507
- ```
508
- ![Second GET request](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/images/dynamic-api-caching-2-GET-second-request.Jpeg?raw=true "Second GET request")
509
- ```text
510
- 3. POST /users
511
- ```
512
- ![POST request](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/images/dynamic-api-caching-3-POST-request.Jpeg?raw=true "POST request")
513
- ```text
514
- 4. GET /users
515
- ```
516
- ![Third GET request](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/images/dynamic-api-caching-4-GET-third-request.Jpeg?raw=true "Third GET request")
517
-
518
-
519
- ___
520
-
521
- ### [Authentication](https://docs.nestjs.com/security/authorization#integrating-casl) (optional)
522
-
523
- An authentication strategy like <a href="https://docs.nestjs.com/security/authentication#jwt-token" target="_blank">JWT</a> is already implemented in the Dynamic API.
524
- All you have to do is to pass the User object and some options to the `useAuth` property of the `DynamicApiModule.forRoot` method.
525
-
526
- **Configuration**
527
-
528
- Ok, let's update our `User` class to add a `password` field.
529
-
530
- ```typescript
531
- // user.ts
532
- import { IsEmail } from 'class-validator';
533
-
534
- @Schema({ collection: 'users' })
535
- export class User extends BaseEntity {
536
- @ApiProperty()
537
- @IsNotEmpty()
538
- @IsString()
539
- @Prop({ type: String, required: true })
540
- email: string;
541
-
542
- @Exclude()
543
- @IsNotEmpty()
544
- @IsString()
545
- @Prop({ type: String, required: true })
546
- password: string;
547
-
548
- @ApiPropertyOptional({ type: Boolean, default: false })
549
- @IsBoolean()
550
- @IsOptional()
551
- @Prop({ type: Boolean, default: false })
552
- isAdmin: boolean;
553
-
554
- @ApiPropertyOptional()
555
- @IsNotEmpty()
556
- @IsString()
557
- @IsOptional()
558
- @Prop({ type: String })
559
- company?: string;
560
- }
561
- ```
562
-
563
- Now, we are going to add the `useAuth` property to the `DynamicApiModule.forRoot` method and pass the `User` object and some options.
564
-
565
- ```typescript
566
- // app.module.ts
567
- import { DynamicApiModule } from 'mongodb-dynamic-api';
568
- import { User } from './users/user';
569
- import { UsersModule } from './users/users.module';
570
-
571
- @Module({
572
- imports: [
573
- DynamicApiModule.forRoot('...', {
574
- // ...,
575
- useAuth: { // <- add this
576
- user: {
577
- entity: User, // <- put here the entity which will represent a User of your API
578
- loginField: 'email',
579
- passwordField: 'password',
580
- },
581
- jwt: {
582
- secret: 'my-secret', // <- replace by your own JWT secret in production
583
- },
584
- },
585
- }),
586
- UsersModule,
587
- ],
588
- controllers: [AppController],
589
- providers: [AppService],
590
- })
591
- export class AppModule {}
592
- ```
593
-
594
- By setting the `useAuth` property, the Dynamic API will automatically add the authentication API.
595
- <br>It will add the `/auth/register`, `/auth/login`, and `/auth/account` routes to the API.
596
-
597
- By default, only the `/auth/register` and `/auth/login` routes are public.
598
- All other routes are protected and require a valid `JWT token` to access them.
599
-
600
- **Swagger Configuration**
601
-
602
- For Swagger users, you must enable the bearer Auth option by setting the `bearerAuth` property to `true` in the enableDynamicAPISwagger method.
603
- This will add the Authorize button in the Swagger UI. This button will allow you to pass the `JWT Token` and unlock the protected routes.
604
-
605
- ```typescript
606
- // main.ts
607
- import { enableDynamicAPISwagger } from 'mongodb-dynamic-api';
608
-
609
- async function bootstrap() {
610
- const app = await NestFactory.create(AppModule);
611
- // ...
612
- enableDynamicAPISwagger(app, {
613
- // ...,
614
- swaggerExtraConfig: { // <- add this line in your main.ts file
615
- bearerAuth: true,
616
- },
617
- });
618
-
619
- await app.listen(3000);
620
- }
621
- ```
622
-
623
- ![Swagger UI - Authentication API](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/images/dynamic-api-authentication.Jpeg?raw=true "Swagger UI - Authentication API")
624
-
625
- <a href="https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/swagger-authentication-api.md" target="_blank">See more Authentication API screenshots</a>
626
-
627
-
628
- **Usage**
629
-
630
- Ok let's add a new user with the `POST` method on the `/auth/register` route.
631
- <br>You will receive a valid `JWT token` in the response.
632
-
633
- ```text
634
- POST /auth/register
635
-
636
- curl -X 'POST' \
637
- '<your-host>/auth/register' \
638
- -H 'accept: application/json' \
639
- -H 'Content-Type: application/json' \
640
- -d '{
641
- "email": "<your-email>",
642
- "password": "<your-password>" // <- the password will be hashed automatically before saving in the database
643
- }'
644
- ```
645
- ```json
646
- # Server response
647
- {"accessToken":"<your-jwt-token>"}
648
- ```
649
-
650
- If you go to `/auth/login` and request the route with the `POST` method passing the `email` and `password` fields in the body.
651
- <br>You will also receive a valid `JWT token` in the response.
652
-
653
- ```text
654
- POST /auth/login
655
-
656
- curl -X 'POST' \
657
- '<your-host>/auth/login' \
658
- -H 'accept: application/json' \
659
- -H 'Content-Type: application/json' \
660
- -d '{
661
- "email": "<your-email>",
662
- "password": "<your-password>"
663
- }'
664
- ```
665
- ```json
666
- # Server response
667
- {"accessToken":"<your-jwt-token>"}
668
- ```
669
-
670
- Now let's request the `/auth/account` protected route with the `GET` method and pass our valid JWT token in the `Authorization` header.
671
-
672
- ```text
673
- GET /auth/account
674
-
675
- curl -X 'GET' \
676
- '<your-host>/auth/account' \
677
- -H 'accept: application/json' \
678
- -H 'Authorization: Bearer <your-jwt-token>'
679
- ```
680
- ```json
681
- # Server response
682
- {"id":"65edc717c1ec...","email":"<your-email>"}
683
- ```
684
-
685
- Great, now you have a fully functional authentication API.
686
-
687
- All other routes are protected and require a valid JWT token to be accessed. You can easily make it public by adding the `isPublic` property to the `controllerOptions` object or to the `route` object in the `DynamicApiModule.forFeature` method.
688
-
689
- ```typescript
690
- // users.module.ts
691
- import { DynamicApiModule } from 'mongodb-dynamic-api';
692
- import { User } from './user';
693
-
694
- @Module({
695
- imports: [
696
- DynamicApiModule.forFeature({
697
- entity: User,
698
- controllerOptions: {
699
- path: 'users',
700
- isPublic: true, // <- add this to make all user API routes public
701
- },
702
- // ...
703
- }),
704
- ],
705
- })
706
- export class UsersModule {}
707
- ```
708
- ```typescript
709
- // users.module.ts
710
- import { DynamicApiModule } from 'mongodb-dynamic-api';
711
- import { User } from './user';
712
-
713
- @Module({
714
- imports: [
715
- DynamicApiModule.forFeature({
716
- entity: User,
717
- controllerOptions: {
718
- path: 'users',
719
- },
720
- routes: [
721
- { type: 'GetMany' }, // <- protected route
722
- { type: 'GetOne', isPublic: true }, // <- public route
723
- { type: 'UpdateOne' }, // <- protected route
724
- { type: 'DeleteOne' }, // <- protected route
725
- ],
726
- }),
727
- ],
728
- })
729
- export class UsersModule {}
730
- ```
731
-
732
- ___
733
-
734
- ### [Casl](https://docs.nestjs.com/security/authorization#integrating-casl) (only with authentication)
735
-
736
- Casl will allow you to condition the actions of your users for each protected route of your APIs.
737
- <br>Authentication is required, you need to enable it or implement your own strategy that adds the User object in the request.
738
-
739
- **MongoDB dynamic API** uses the `User` object in the requests to apply the ability predicates defined in the `DynamicApiModule.forFeature`.
740
- <br>You can define them either **in the controller options**,
741
- or **in each route object** declared in the routes property.
742
- <br>*If the ability predicates are specified in 2, those defined in the route will have priority.*
743
-
744
- **An ability predicate is an arrow function that takes a subject and the User object (optional) as arguments and returns a boolean.**
745
-
746
- Let's create a new Article content and set the ability predicates to the `UpdateOne`, `DeleteOne` and `DeleteMany` routes.
747
-
748
- **Configuration**
749
-
750
- ```typescript
751
- // article.ts
752
- import { Prop } from '@nestjs/mongoose';
753
- import { ApiProperty } from '@nestjs/swagger';
754
- import { BaseEntity } from 'mongodb-dynamic-api';
755
-
756
- export class Article extends BaseEntity {
757
- @ApiProperty({ type: Boolean, default: false })
758
- @Prop({ type: Boolean, default: false })
759
- isPublished: boolean;
760
-
761
- @ApiProperty()
762
- @Prop({ type: String })
763
- authorId: string;
764
- }
765
- ```
766
-
767
- ```typescript
768
- // articles.module.ts
769
- import { Module } from '@nestjs/common';
770
- import { DynamicApiModule } from 'mongodb-dynamic-api';
771
- import { User } from '../users/user';
772
- import { Article } from './article';
773
-
774
- @Module({
775
- imports: [
776
- DynamicApiModule.forFeature({
777
- entity: Article,
778
- controllerOptions: {
779
- path: 'articles',
780
- abilityPredicates: [ // <- declare the ability predicates in the controller options
781
- {
782
- targets: ['DeleteMany', 'DeleteOne'], // <- declare the targets
783
- predicate: (_: Article, user: User) => user.isAdmin, // <- add the condition
784
- },
785
- ],
786
- },
787
- routes: [
788
- { type: 'GetMany', isPublic: true },
789
- { type: 'GetOne', isPublic: true },
790
- { type: 'CreateOne' },
791
- {
792
- type: 'UpdateOne',
793
- abilityPredicate: (article: Article, user: User) => // <- declare the ability predicate in the route object
794
- article.authorId === user.id && !article.isPublished,
795
- },
796
- ],
797
- }),
798
- ],
799
- })
800
- export class ArticlesModule {}
801
- ```
802
-
803
- ```typescript
804
- // app.module.ts
805
- import { Module } from '@nestjs/common';
806
- import { DynamicApiModule } from 'mongodb-dynamic-api';
807
- import { AppController } from './app.controller';
808
- import { AppService } from './app.service';
809
- import { User } from './users/user';
810
- import { ArticlesModule } from './articles/articles.module';
811
-
812
- @Module({
813
- imports: [
814
- DynamicApiModule.forRoot(
815
- 'your-mongodb-uri',
816
- {
817
- useAuth: {
818
- user: {
819
- entity: User,
820
- additionalFields: {
821
- toRegister: ['isAdmin'], // <- here you can set additional fields to display in the register body
822
- toRequest: ['isAdmin', 'company'], // <- here you can set additional fields to the User object in the request
823
- },
824
- },
825
- },
826
- },
827
- ),
828
- ArticlesModule,
829
- ],
830
- controllers: [AppController],
831
- providers: [AppService],
832
- })
833
- export class AppModule {}
834
- ```
835
-
836
-
837
- **Usage**
838
-
839
- First, let's create an admin user with the `POST` method on the `/auth/register` public route.
840
- ```text
841
- POST /auth/register
842
-
843
- curl -X 'POST' \
844
- '<your-host>/auth/register' \
845
- -H 'accept: application/json' \
846
- -H 'Content-Type: application/json' \
847
- -d '{
848
- "email": "admin@test.co",
849
- "isAdmin": true,
850
- "password": "admin"
851
- }'
852
- ```
853
-
854
- Then, we are going to protect the `/auth/register` route by setting the `protectRegister` property to `true` and add a **register ability predicate** in the useAuth Object of the `DynamicApiModule.forRoot` method.
855
- ```typescript
856
- // app.module.ts
857
- @Module({
858
- imports: [
859
- DynamicApiModule.forRoot(
860
- 'your-mongodb-uri',
861
- {
862
- useAuth: {
863
- // ...,
864
- protectRegister: true, // <- add this line
865
- registerAbilityPredicate: (user: User) => user.isAdmin,
866
- },
867
- },
868
- ),
869
- ```
870
-
871
- Ok, now let's create a non admin user with the `POST` method on the `/auth/register` route.
872
- ```text
873
- POST /auth/register
874
-
875
- curl -X 'POST' \
876
- '<your-host>/auth/register' \
877
- -H 'accept: application/json' \
878
- -H 'Content-Type: application/json' \
879
- -d '{
880
- "email": "toto@test.co",
881
- "password": "toto"
882
- }'
883
- ```
884
- ```json
885
- # Server response
886
- {"accessToken":"<toto-jwt-token>"}
887
- ```
888
-
889
- Next, under toto's account (not admin), we will try to register a new user with the `POST` method on the `/auth/register` route.
890
- <br>The register ability predicate will return `false` and we will receive a `403 Forbidden` error.
891
-
892
- ```text
893
- POST /auth/register
894
-
895
- curl -X 'POST' \
896
- 'http://localhost:5000/auth/register' \
897
- -H 'accept: application/json' \
898
- -H 'Authorization: Bearer <toto-jwt-token>' \
899
- -H 'Content-Type: application/json' \
900
- -d '{
901
- "email": "bill@test.co",
902
- "password": "bill"
903
- }'
904
- ```
905
- ```json
906
- # Server response
907
- {
908
- "message": "Forbidden resource",
909
- "error": "Forbidden",
910
- "statusCode": 403
911
- }
912
- ```
913
-
914
- The register route is now well protected and only an admin user can create new users.
915
-
916
-
917
- ___
196
+ - **[Swagger UI](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/swagger-ui.md)**
197
+ - **[Validation](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/validation.md)** with **Class Validator**
198
+ - **[Caching](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/caching.md)** with **cache-manager**
199
+ - **[Authentication](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/authentication.md)** with **JWT**
200
+ - **[Authorization](https://github.com/MikeDev75015/mongodb-dynamic-api/blob/develop/README/authorization.md)** with **Casl**
918
201
 
919
- More coming soon...
920
202
 
921
203
 
922
204
 
205
+ <br>
206
+ <br>
207
+ <br>
923
208