@digitaldefiance/express-suite-starter 2.4.6 → 2.4.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitaldefiance/express-suite-starter",
3
- "version": "2.4.6",
3
+ "version": "2.4.7",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "create-express-suite": "./dist/src/cli.js"
@@ -1,35 +1,84 @@
1
- import { Application, BaseApplication, IFailableResult, IServerInitResult, DummyEmailService, emailServiceRegistry, BaseRouter, IApplication } from '@digitaldefiance/node-express-suite';
1
+ import {
2
+ Application,
3
+ BaseApplication,
4
+ IFailableResult,
5
+ IServerInitResult,
6
+ DummyEmailService,
7
+ emailServiceRegistry,
8
+ BaseRouter,
9
+ IApplication,
10
+ ApiRouter as LibraryApiRouter,
11
+ } from '@digitaldefiance/node-express-suite';
2
12
  import { Environment } from './environment';
3
13
  import { IConstants } from './interfaces/constants';
4
14
  import { Constants } from './constants';
5
- import { ApiRouter } from './routers/api';
6
15
  import { AppRouter } from './routers/app';
7
16
  import { getSchemaMap } from './schemas/schema';
8
17
  import { initMiddleware } from './middlewares';
9
18
  import { EmailService } from './services/email';
19
+ import { Types } from '@digitaldefiance/mongoose-types';
20
+ import { ModelDocMap } from './shared-types';
10
21
 
11
- export class App<TInitResults extends IServerInitResult = IServerInitResult, TConstants extends IConstants = IConstants> extends Application<TInitResults, any, Environment, TConstants, any> {
22
+ /**
23
+ * Main application class.
24
+ *
25
+ * This class sets up the Express application with:
26
+ * - Library's ApiRouter which provides user authentication routes
27
+ * - App router for serving React frontend
28
+ * - Database initialization
29
+ * - Email service registration
30
+ *
31
+ * Note: The library's ApiRouter includes UserController which provides:
32
+ * - /api/user/verify - Token verification
33
+ * - /api/user/request-direct-login - Direct login request
34
+ * - /api/user/login - User login
35
+ * - And other user management endpoints
36
+ *
37
+ * For custom API routes, create additional decorator-based controllers
38
+ * and mount them on the router.
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const env = new Environment(join(App.distDir, 'my-api', '.env'));
43
+ * const app = new App(
44
+ * env,
45
+ * DatabaseInitializationService.initUserDb.bind(DatabaseInitializationService),
46
+ * DatabaseInitializationService.serverInitResultHash.bind(DatabaseInitializationService),
47
+ * Constants,
48
+ * );
49
+ * await app.start();
50
+ * ```
51
+ */
52
+ export class App<
53
+ TInitResults extends IServerInitResult<Types.ObjectId> = IServerInitResult<Types.ObjectId>,
54
+ TConstants extends IConstants = IConstants,
55
+ > extends Application<TInitResults, ModelDocMap, Types.ObjectId, Environment<Types.ObjectId>, TConstants, AppRouter> {
12
56
  constructor(
13
- environment: Environment,
14
- databaseInitFunction: (application: BaseApplication<any, TInitResults>) => Promise<IFailableResult<TInitResults>>,
57
+ environment: Environment<Types.ObjectId>,
58
+ databaseInitFunction: (
59
+ application: BaseApplication<Types.ObjectId, ModelDocMap, TInitResults>,
60
+ ) => Promise<IFailableResult<TInitResults>>,
15
61
  initResultHashFunction: (initResults: TInitResults) => string,
16
62
  constants: TConstants = Constants as TConstants,
17
63
  ) {
18
64
  super(
19
- environment,
20
- (app: IApplication): BaseRouter<IApplication> => new ApiRouter(app),
65
+ environment,
66
+ // Use the library's ApiRouter which includes UserController for auth routes
67
+ (app: IApplication<Types.ObjectId>): BaseRouter<Types.ObjectId> => {
68
+ return new LibraryApiRouter(app);
69
+ },
21
70
  getSchemaMap,
22
- databaseInitFunction,
23
- initResultHashFunction,
71
+ databaseInitFunction,
72
+ initResultHashFunction,
24
73
  undefined, // Default CSP config
25
74
  constants,
26
- (apiRouter: BaseRouter<IApplication>): AppRouter => new AppRouter(apiRouter),
75
+ (apiRouter: BaseRouter<Types.ObjectId>): AppRouter => new AppRouter(apiRouter),
27
76
  initMiddleware,
28
77
  );
29
-
78
+
30
79
  // Register the DummyEmailService - users should replace this with their own email service
31
- const emailService = new DummyEmailService(this);
32
- //const emailService = new EmailService(this);
80
+ const emailService = new DummyEmailService<Types.ObjectId>(this);
81
+ // const emailService = new EmailService(this);
33
82
  emailServiceRegistry.setService(emailService);
34
83
  }
35
84
  }
@@ -1,11 +1,77 @@
1
- import { ApiRouter as BaseApiRouter, IApplication, IBaseDocument } from '@digitaldefiance/node-express-suite';
1
+ import {
2
+ DecoratorBaseController,
3
+ ApiController,
4
+ Get,
5
+ RequireAuth,
6
+ Public,
7
+ ApiTags,
8
+ ApiSummary,
9
+ ApiDescription,
10
+ Returns,
11
+ Param,
12
+ Query,
13
+ Paginated,
14
+ IApplication,
15
+ } from '@digitaldefiance/node-express-suite';
2
16
  import { ITokenRole, ITokenUser, IUserBase } from '@digitaldefiance/suite-core-lib';
3
17
  import { Types } from '@digitaldefiance/mongoose-types';
4
18
  import { Environment } from '../environment';
5
19
  import { IConstants } from '../interfaces/constants';
6
20
  import { CoreLanguageCode } from '@digitaldefiance/i18n-lib';
7
21
 
8
- export class ApiRouter<
22
+ /**
23
+ * CustomApiController - Example decorator-based API controller.
24
+ *
25
+ * This controller demonstrates the recommended decorator-based approach for
26
+ * building custom Express API endpoints with @digitaldefiance/node-express-suite.
27
+ *
28
+ * Note: The main user authentication routes (/api/user/*) are provided by the
29
+ * library's ApiRouter which is configured in application.ts. This controller
30
+ * is for adding your own custom API endpoints.
31
+ *
32
+ * Features demonstrated:
33
+ * - @ApiController for controller registration and OpenAPI metadata
34
+ * - @Get, @Post, @Put, @Delete for HTTP method routing
35
+ * - @RequireAuth and @Public for authentication control
36
+ * - @ApiTags, @ApiSummary, @ApiDescription for OpenAPI documentation
37
+ * - @Returns for response documentation
38
+ * - @Param, @Query for parameter injection
39
+ * - @Paginated for pagination support
40
+ *
41
+ * To use this controller, mount it on your application's router in application.ts:
42
+ * ```typescript
43
+ * // In your Application class constructor, after creating the LibraryApiRouter:
44
+ * const customController = new CustomApiController(app);
45
+ * apiRouter.router.use('/custom', customController.router);
46
+ * ```
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * // Create your own controller by extending DecoratorBaseController
51
+ * @ApiTags('Users')
52
+ * @ApiController('/api/users', { description: 'User management endpoints' })
53
+ * class UserController extends DecoratorBaseController {
54
+ * constructor(app: IApplication) {
55
+ * super(app);
56
+ * }
57
+ *
58
+ * @Public()
59
+ * @ApiSummary('Get user by ID')
60
+ * @Returns(200, 'User', { description: 'User found' })
61
+ * @Returns(404, 'ErrorResponse', { description: 'User not found' })
62
+ * @Get('/:id')
63
+ * async getUser(@Param('id') id: string) {
64
+ * // Your implementation here
65
+ * }
66
+ * }
67
+ * ```
68
+ */
69
+ @ApiTags('Custom')
70
+ @ApiController('/custom', {
71
+ description: 'Custom API endpoints - add your own routes here',
72
+ })
73
+ @RequireAuth()
74
+ export class CustomApiController<
9
75
  TID extends Types.ObjectId | string = Types.ObjectId,
10
76
  TDate extends Date = Date,
11
77
  TLanguage extends CoreLanguageCode = CoreLanguageCode,
@@ -15,10 +81,84 @@ export class ApiRouter<
15
81
  TTokenUser extends ITokenUser = ITokenUser,
16
82
  TEnvironment extends Environment<TID> = Environment<TID>,
17
83
  TConstants extends IConstants = IConstants,
18
- TBaseDocument extends IBaseDocument<any, TID> = IBaseDocument<any, TID>,
19
84
  TApplication extends IApplication<TID> = IApplication<TID>,
20
- > extends BaseApiRouter<TID,TDate,TLanguage,TAccountStatus, TUser,TTokenRole,TBaseDocument, TTokenUser,TConstants, TEnvironment, TApplication> {
85
+ > extends DecoratorBaseController<TLanguage, TID> {
21
86
  constructor(app: TApplication) {
22
87
  super(app);
23
88
  }
24
- }
89
+
90
+ /**
91
+ * Health check endpoint.
92
+ * Returns the current server status.
93
+ */
94
+ @Public()
95
+ @ApiSummary('Health check')
96
+ @ApiDescription('Returns the current server health status')
97
+ @Returns(200, 'HealthResponse', { description: 'Server is healthy' })
98
+ @Get('/health')
99
+ async healthCheck() {
100
+ return {
101
+ status: 'ok',
102
+ timestamp: new Date().toISOString(),
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Example paginated list endpoint.
108
+ * Demonstrates pagination with query parameter injection.
109
+ */
110
+ @Public()
111
+ @Paginated({ defaultPageSize: 20, maxPageSize: 100 })
112
+ @ApiSummary('List items')
113
+ @ApiDescription('Returns a paginated list of items')
114
+ @Returns(200, 'ItemList', { description: 'List of items' })
115
+ @Get('/items')
116
+ async listItems(
117
+ @Query('page') page: number = 1,
118
+ @Query('limit') limit: number = 20,
119
+ ) {
120
+ // Example implementation - replace with your actual logic
121
+ return {
122
+ items: [],
123
+ total: 0,
124
+ page,
125
+ pageSize: limit,
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Example get by ID endpoint.
131
+ * Demonstrates path parameter injection.
132
+ */
133
+ @Public()
134
+ @ApiSummary('Get item by ID')
135
+ @ApiDescription('Returns a single item by its ID')
136
+ @Returns(200, 'Item', { description: 'Item found' })
137
+ @Returns(404, 'ErrorResponse', { description: 'Item not found' })
138
+ @Get('/items/:id')
139
+ async getItem(@Param('id') id: string) {
140
+ // Example implementation - replace with your actual logic
141
+ return {
142
+ id,
143
+ name: 'Example Item',
144
+ createdAt: new Date().toISOString(),
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Example authenticated endpoint.
150
+ * Requires JWT authentication (inherited from class-level @RequireAuth).
151
+ */
152
+ @ApiSummary('Get current user profile')
153
+ @ApiDescription('Returns the authenticated user profile')
154
+ @Returns(200, 'UserProfile', { description: 'User profile' })
155
+ @Returns(401, 'ErrorResponse', { description: 'Unauthorized' })
156
+ @Get('/profile')
157
+ async getProfile() {
158
+ // Access authenticated user via this.req.user
159
+ return {
160
+ message: 'Authenticated endpoint',
161
+ // user: this.req.user,
162
+ };
163
+ }
164
+ }
@@ -3,9 +3,10 @@ import { Request, Response, NextFunction } from 'express';
3
3
  import { Environment } from '../environment';
4
4
  import { IConstants } from '../interfaces/constants';
5
5
  import { Constants } from '../constants';
6
+ import { Types } from '@digitaldefiance/mongoose-types';
6
7
 
7
- export class AppRouter extends BaseAppRouter<IApplication> {
8
- constructor(apiRouter: BaseRouter<IApplication>) {
8
+ export class AppRouter extends BaseAppRouter<Types.ObjectId, IApplication<Types.ObjectId>> {
9
+ constructor(apiRouter: BaseRouter<Types.ObjectId, IApplication<Types.ObjectId>>) {
9
10
  super(apiRouter);
10
11
  }
11
12
 
@@ -17,7 +18,7 @@ export class AppRouter extends BaseAppRouter<IApplication> {
17
18
  const jsFile = this.getAssetFilename(this.assetsDir, /^index-.*\.js$/);
18
19
  const cssFile = this.getAssetFilename(this.assetsDir, /^index-.*\.css$/);
19
20
  const constants = this.application.constants as IConstants;
20
- const environment = this.application.environment as Environment;
21
+ const environment = this.application.environment as Environment<Types.ObjectId>;
21
22
  const SiteName = constants.Site;
22
23
  const locals = {
23
24
  ...this.getBaseViewLocals(req, res),
@@ -1,2 +1 @@
1
- export * from './api';
2
1
  export * from './app';
@@ -1,4 +1,4 @@
1
- import { Connection } from '@digitaldefiance/mongoose-types';
1
+ import { Connection, Types } from '@digitaldefiance/mongoose-types';
2
2
  import { BaseModelName, createEmailTokenSchema, createMnemonicSchema, createRoleSchema, createUsedDirectLoginTokenSchema, createUserRoleSchema, MnemonicSchema, RoleSchema, SchemaCollection, SchemaMap, UsedDirectLoginTokenSchema, UserRoleSchema } from '@digitaldefiance/node-express-suite';
3
3
  import EmailTokenModel from '../models/email-token';
4
4
  import MnemonicModel from '../models/mnemonic';
@@ -11,7 +11,7 @@ import { ModelDocMap } from '../shared-types';
11
11
  import { IConstants } from '../interfaces/constants';
12
12
  import { Constants } from '../constants';
13
13
 
14
- export function getSchemaMap(connection: Connection, constants: IConstants = Constants): SchemaMap<ModelDocMap> {
14
+ export function getSchemaMap(connection: Connection, constants: IConstants = Constants): SchemaMap<Types.ObjectId, ModelDocMap> {
15
15
  return {
16
16
  EmailToken: {
17
17
  collection: SchemaCollection.EmailToken,
@@ -48,7 +48,7 @@ export type SchemaMap = {
48
48
  /**
49
49
  * For each model name, contains the corresponding schema and model
50
50
  */
51
- [K in keyof ModelDocMap]: ISchema<ModelDocMap[K]>;
51
+ [K in keyof ModelDocMap]: ISchema<Types.ObjectId, ModelDocMap[K]>;
52
52
  };
53
53
 
54
54
  export type ApiRequestHandler<T extends ApiResponse> = (
@@ -4,20 +4,88 @@ API business logic library for {{WORKSPACE_NAME}}.
4
4
 
5
5
  ## Purpose
6
6
 
7
- Business logic, services, and models for the API layer.
7
+ Business logic, services, models, and decorator-based API controllers for the API layer.
8
8
 
9
9
  ## Structure
10
10
 
11
11
  ```
12
12
  src/
13
- ├── services/ # Business logic services
14
- ├── models/ # Data models
15
- ├── validators/ # Input validation
16
- └── utils/ # API utilities
13
+ ├── lib/
14
+ ├── routers/ # Decorator-based API controllers
15
+ ├── services/ # Business logic services
16
+ │ ├── models/ # Mongoose data models
17
+ │ ├── documents/ # TypeScript document interfaces
18
+ │ ├── schemas/ # Mongoose schemas
19
+ │ ├── interfaces/ # TypeScript interfaces
20
+ │ ├── application.ts # Main application class
21
+ │ ├── environment.ts # Environment configuration
22
+ │ └── constants.ts # Application constants
23
+ └── index.ts # Public exports
24
+ ```
25
+
26
+ ## Decorator-Based Controllers
27
+
28
+ This library uses the decorator-based approach from `@digitaldefiance/node-express-suite` for defining API routes. This provides:
29
+
30
+ - **Type-safe routing** with `@Get`, `@Post`, `@Put`, `@Delete`, `@Patch` decorators
31
+ - **Automatic OpenAPI documentation** with `@ApiTags`, `@ApiSummary`, `@Returns` decorators
32
+ - **Built-in authentication** with `@RequireAuth`, `@Public` decorators
33
+ - **Request validation** with `@ValidateBody`, `@ValidateParams`, `@ValidateQuery` decorators
34
+ - **Parameter injection** with `@Param`, `@Body`, `@Query`, `@Header` decorators
35
+
36
+ ### Example Controller
37
+
38
+ ```typescript
39
+ import {
40
+ DecoratorBaseController,
41
+ ApiController,
42
+ Get,
43
+ Post,
44
+ RequireAuth,
45
+ Public,
46
+ ApiTags,
47
+ ApiSummary,
48
+ Returns,
49
+ Param,
50
+ Body,
51
+ ValidateBody,
52
+ } from '@digitaldefiance/node-express-suite';
53
+ import { z } from 'zod';
54
+
55
+ const CreateItemSchema = z.object({
56
+ name: z.string().min(1).max(100),
57
+ description: z.string().optional(),
58
+ });
59
+
60
+ @ApiTags('Items')
61
+ @ApiController('/api/items', { description: 'Item management endpoints' })
62
+ @RequireAuth()
63
+ export class ItemController extends DecoratorBaseController {
64
+ constructor(app: IApplication) {
65
+ super(app);
66
+ }
67
+
68
+ @Public()
69
+ @ApiSummary('List all items')
70
+ @Returns(200, 'ItemList')
71
+ @Get('/')
72
+ async listItems() {
73
+ return { items: [] };
74
+ }
75
+
76
+ @ValidateBody(CreateItemSchema)
77
+ @ApiSummary('Create a new item')
78
+ @Returns(201, 'Item')
79
+ @Returns(400, 'ValidationError')
80
+ @Post('/')
81
+ async createItem(@Body() data: z.infer<typeof CreateItemSchema>) {
82
+ return { id: 'new-id', ...data };
83
+ }
84
+ }
17
85
  ```
18
86
 
19
87
  ## Usage
20
88
 
21
89
  ```typescript
22
- import { UserService } from '{{NAMESPACE_ROOT}}/api-lib';
90
+ import { App, Environment, Constants } from '{{NAMESPACE_ROOT}}/api-lib';
23
91
  ```