@eeplatform/core 1.8.3 → 1.8.6
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 +18 -0
- package/CONVENTION.md +632 -0
- package/dist/index.d.ts +2 -154
- package/dist/index.js +709 -1772
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +703 -1773
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @eeplatform/core
|
|
2
2
|
|
|
3
|
+
## 1.8.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 26b7703: Increase max limit and remove aggregation sort
|
|
8
|
+
|
|
9
|
+
## 1.8.5
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 7145ec1: Fix query
|
|
14
|
+
|
|
15
|
+
## 1.8.4
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- dd69528: Update PSGC query
|
|
20
|
+
|
|
3
21
|
## 1.8.3
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/CONVENTION.md
ADDED
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
# EEPlatform Coding Convention
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Project Architecture Overview](#project-architecture-overview)
|
|
6
|
+
- [Layer Responsibilities](#layer-responsibilities)
|
|
7
|
+
- [File Structure & Naming Conventions](#file-structure--naming-conventions)
|
|
8
|
+
- [Models Layer](#models-layer)
|
|
9
|
+
- [Repository Layer](#repository-layer)
|
|
10
|
+
- [Service Layer](#service-layer)
|
|
11
|
+
- [Controller Layer](#controller-layer)
|
|
12
|
+
- [Routes Layer](#routes-layer)
|
|
13
|
+
- [Type Definitions](#type-definitions)
|
|
14
|
+
- [Error Handling](#error-handling)
|
|
15
|
+
- [Documentation](#documentation)
|
|
16
|
+
|
|
17
|
+
## Project Architecture Overview
|
|
18
|
+
|
|
19
|
+
Our project follows a layered architecture with clear separation of concerns:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
Routes → Controllers → Services → Repositories → Database
|
|
23
|
+
↓ ↓ ↓ ↓
|
|
24
|
+
Models ← Models ← Models ← Models
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Each layer has specific responsibilities and should only communicate with adjacent layers.
|
|
28
|
+
|
|
29
|
+
## Layer Responsibilities
|
|
30
|
+
|
|
31
|
+
### Models
|
|
32
|
+
|
|
33
|
+
- Define data structures and types
|
|
34
|
+
- Contain data validation schemas using Joi
|
|
35
|
+
- Transform and validate incoming data
|
|
36
|
+
- Handle ObjectId conversion and data cleaning
|
|
37
|
+
|
|
38
|
+
### Repositories
|
|
39
|
+
|
|
40
|
+
- Direct database interactions using MongoDB native driver
|
|
41
|
+
- Implement caching strategies (Redis)
|
|
42
|
+
- Handle cache invalidation on mutations
|
|
43
|
+
- Set cache on read operations
|
|
44
|
+
- Manage database sessions and transactions
|
|
45
|
+
|
|
46
|
+
### Services
|
|
47
|
+
|
|
48
|
+
- Business logic implementation
|
|
49
|
+
- Third-party service integrations
|
|
50
|
+
- Coordinate multiple repositories
|
|
51
|
+
- Handle complex operations requiring transactions
|
|
52
|
+
- Transform data between layers
|
|
53
|
+
|
|
54
|
+
### Controllers
|
|
55
|
+
|
|
56
|
+
- Handle HTTP request/response lifecycle
|
|
57
|
+
- Input validation using Joi schemas
|
|
58
|
+
- Choose between repository or service calls based on complexity
|
|
59
|
+
- Direct repository calls for simple CRUD operations
|
|
60
|
+
- Service calls for complex business logic
|
|
61
|
+
|
|
62
|
+
### Routes
|
|
63
|
+
|
|
64
|
+
- Define API endpoints and HTTP methods
|
|
65
|
+
- Apply middleware (authentication, rate limiting, etc.)
|
|
66
|
+
- Map endpoints to appropriate controller methods
|
|
67
|
+
- Handle route-specific security requirements
|
|
68
|
+
|
|
69
|
+
## File Structure & Naming Conventions
|
|
70
|
+
|
|
71
|
+
### Directory Structure
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
src/
|
|
75
|
+
├── resources/
|
|
76
|
+
│ ├── [resource-name]/
|
|
77
|
+
│ │ ├── [resource-name].model.ts
|
|
78
|
+
│ │ ├── [resource-name].repository.ts
|
|
79
|
+
│ │ ├── [resource-name].service.ts
|
|
80
|
+
│ │ ├── [resource-name].controller.ts
|
|
81
|
+
│ │ └── index.ts
|
|
82
|
+
├── routes/
|
|
83
|
+
│ ├── [resource-name].route.ts
|
|
84
|
+
│ └── index.ts
|
|
85
|
+
└── config.ts
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Naming Conventions
|
|
89
|
+
|
|
90
|
+
- **Files**: Use kebab-case (e.g., `user-profile.model.ts`)
|
|
91
|
+
- **Types**: Use PascalCase with 'T' prefix (e.g., `TUser`, `TSchool`)
|
|
92
|
+
- **Schemas**: Use camelCase with 'schema' prefix (e.g., `schemaUser`, `schemaSchoolUpdate`)
|
|
93
|
+
- **Functions**: Use camelCase (e.g., `createUser`, `getUserById`)
|
|
94
|
+
- **Exported Functions**: Use 'use' prefix for composition functions (e.g., `useUserRepo`, `useUserService`)
|
|
95
|
+
|
|
96
|
+
## Models Layer
|
|
97
|
+
|
|
98
|
+
### Responsibilities
|
|
99
|
+
|
|
100
|
+
- Define TypeScript types/interfaces
|
|
101
|
+
- Create Joi validation schemas
|
|
102
|
+
- Implement model transformation functions
|
|
103
|
+
- Handle ObjectId conversions
|
|
104
|
+
|
|
105
|
+
### Example Structure
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { BadRequestError } from "@eeplatform/nodejs-utils";
|
|
109
|
+
import Joi from "joi";
|
|
110
|
+
import { ObjectId } from "mongodb";
|
|
111
|
+
|
|
112
|
+
// Type Definition
|
|
113
|
+
export type TUser = {
|
|
114
|
+
_id?: ObjectId;
|
|
115
|
+
email: string;
|
|
116
|
+
firstName: string;
|
|
117
|
+
lastName: string;
|
|
118
|
+
status?: string;
|
|
119
|
+
createdAt?: Date | string;
|
|
120
|
+
updatedAt?: Date | string;
|
|
121
|
+
deletedAt?: Date | string;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Joi Schema
|
|
125
|
+
export const schemaUser = Joi.object({
|
|
126
|
+
_id: Joi.string().hex().optional().allow("", null),
|
|
127
|
+
email: Joi.string().email().required(),
|
|
128
|
+
firstName: Joi.string().required(),
|
|
129
|
+
lastName: Joi.string().required(),
|
|
130
|
+
status: Joi.string().optional().allow("", null),
|
|
131
|
+
createdAt: Joi.date().optional().allow("", null),
|
|
132
|
+
updatedAt: Joi.date().optional().allow("", null),
|
|
133
|
+
deletedAt: Joi.date().optional().allow("", null),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Model Function
|
|
137
|
+
export function MUser(value: TUser): TUser {
|
|
138
|
+
const { error } = schemaUser.validate(value);
|
|
139
|
+
if (error) {
|
|
140
|
+
throw new BadRequestError(error.message);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Handle ObjectId conversion
|
|
144
|
+
if (value._id && typeof value._id === "string") {
|
|
145
|
+
try {
|
|
146
|
+
value._id = new ObjectId(value._id);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
throw new BadRequestError("Invalid ID.");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
_id: value._id ?? new ObjectId(),
|
|
154
|
+
email: value.email,
|
|
155
|
+
firstName: value.firstName,
|
|
156
|
+
lastName: value.lastName,
|
|
157
|
+
status: value.status ?? "active",
|
|
158
|
+
createdAt: value.createdAt ? new Date(value.createdAt) : new Date(),
|
|
159
|
+
updatedAt: value.updatedAt ? new Date(value.updatedAt) : new Date(),
|
|
160
|
+
deletedAt: value.deletedAt ? new Date(value.deletedAt) : null,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Validation Guidelines
|
|
166
|
+
|
|
167
|
+
- Use `.required()` for mandatory fields
|
|
168
|
+
- Use `.optional().allow("", null)` for optional fields
|
|
169
|
+
- Use `.hex()` for MongoDB ObjectId validation
|
|
170
|
+
- Use `.email()` for email validation
|
|
171
|
+
- Use `.isoDate()` for date string validation
|
|
172
|
+
|
|
173
|
+
## Repository Layer
|
|
174
|
+
|
|
175
|
+
### Responsibilities
|
|
176
|
+
|
|
177
|
+
- Database CRUD operations
|
|
178
|
+
- Caching implementation
|
|
179
|
+
- Cache invalidation strategies
|
|
180
|
+
- Database indexing
|
|
181
|
+
- Query optimization
|
|
182
|
+
|
|
183
|
+
### Example Structure
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import {
|
|
187
|
+
useAtlas,
|
|
188
|
+
useCache,
|
|
189
|
+
makeCacheKey,
|
|
190
|
+
logger,
|
|
191
|
+
} from "@eeplatform/nodejs-utils";
|
|
192
|
+
import { ObjectId, ClientSession } from "mongodb";
|
|
193
|
+
import { TUser, MUser } from "./user.model";
|
|
194
|
+
|
|
195
|
+
export function useUserRepo() {
|
|
196
|
+
const db = useAtlas.getDb();
|
|
197
|
+
if (!db) {
|
|
198
|
+
throw new Error("Unable to connect to server.");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const namespace_collection = "users";
|
|
202
|
+
const collection = db.collection(namespace_collection);
|
|
203
|
+
const { getCache, setCache, delNamespace } = useCache(namespace_collection);
|
|
204
|
+
|
|
205
|
+
// Cache management
|
|
206
|
+
function delCachedData() {
|
|
207
|
+
delNamespace()
|
|
208
|
+
.then(() => {
|
|
209
|
+
logger.log({
|
|
210
|
+
level: "info",
|
|
211
|
+
message: `Cache namespace cleared for ${namespace_collection}`,
|
|
212
|
+
});
|
|
213
|
+
})
|
|
214
|
+
.catch((err) => {
|
|
215
|
+
logger.log({
|
|
216
|
+
level: "error",
|
|
217
|
+
message: `Failed to clear cache namespace: ${err.message}`,
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Create operation
|
|
223
|
+
async function add(value: TUser, session?: ClientSession) {
|
|
224
|
+
const user = MUser(value);
|
|
225
|
+
const result = await collection.insertOne(user, { session });
|
|
226
|
+
delCachedData(); // Invalidate cache on mutations
|
|
227
|
+
return result.insertedId;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Read operation with caching
|
|
231
|
+
async function getById(_id: string | ObjectId) {
|
|
232
|
+
try {
|
|
233
|
+
_id = new ObjectId(_id);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
throw new BadRequestError("Invalid ID.");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const cacheKey = makeCacheKey(namespace_collection, {
|
|
239
|
+
_id: _id.toString(),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Check cache first
|
|
243
|
+
const cachedData = await getCache<TUser>(cacheKey);
|
|
244
|
+
if (cachedData) {
|
|
245
|
+
logger.log({
|
|
246
|
+
level: "info",
|
|
247
|
+
message: `Cache hit for user by ID: ${cacheKey}`,
|
|
248
|
+
});
|
|
249
|
+
return cachedData;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Fetch from database
|
|
253
|
+
const data = await collection.findOne<TUser>({ _id });
|
|
254
|
+
|
|
255
|
+
// Set cache for future requests
|
|
256
|
+
if (data) {
|
|
257
|
+
setCache(cacheKey, data, 600) // 10 minutes TTL
|
|
258
|
+
.then(() => {
|
|
259
|
+
logger.log({
|
|
260
|
+
level: "info",
|
|
261
|
+
message: `Cache set for user by ID: ${cacheKey}`,
|
|
262
|
+
});
|
|
263
|
+
})
|
|
264
|
+
.catch((err) => {
|
|
265
|
+
logger.log({
|
|
266
|
+
level: "error",
|
|
267
|
+
message: `Failed to set cache: ${err.message}`,
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return data;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
add,
|
|
277
|
+
getById,
|
|
278
|
+
delCachedData,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Caching Guidelines
|
|
284
|
+
|
|
285
|
+
- **Cache Keys**: Use `makeCacheKey(namespace, options)` for consistent key generation
|
|
286
|
+
- **Cache TTL**: Use appropriate TTL values (300s for user data, 600s for less frequently changing data)
|
|
287
|
+
- **Cache Invalidation**: Clear cache on all mutation operations (create, update, delete)
|
|
288
|
+
- **Cache Logging**: Log cache hits/misses and errors for monitoring
|
|
289
|
+
- **Error Handling**: Cache failures should not break functionality
|
|
290
|
+
|
|
291
|
+
## Service Layer
|
|
292
|
+
|
|
293
|
+
### Responsibilities
|
|
294
|
+
|
|
295
|
+
- Business logic implementation
|
|
296
|
+
- Third-party integrations
|
|
297
|
+
- Multi-repository coordination
|
|
298
|
+
- Transaction management
|
|
299
|
+
- Complex data transformations
|
|
300
|
+
|
|
301
|
+
### Example Structure
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
import { BadRequestError, useAtlas } from "@eeplatform/nodejs-utils";
|
|
305
|
+
import { useUserRepo } from "./user.repository";
|
|
306
|
+
import { useRoleRepo } from "../role/role.repository";
|
|
307
|
+
import { TUser } from "./user.model";
|
|
308
|
+
|
|
309
|
+
export function useUserService() {
|
|
310
|
+
const { add: addUser, getById: getUserById } = useUserRepo();
|
|
311
|
+
const { add: addRole } = useRoleRepo();
|
|
312
|
+
|
|
313
|
+
async function createUserWithRole(userData: TUser, roleData: any) {
|
|
314
|
+
const session = useAtlas.getClient()?.startSession();
|
|
315
|
+
if (!session) {
|
|
316
|
+
throw new BadRequestError("Unable to start database session.");
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
session.startTransaction();
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
// Business logic: Create user first
|
|
323
|
+
const userId = await addUser(userData, session);
|
|
324
|
+
|
|
325
|
+
// Business logic: Assign default role
|
|
326
|
+
roleData.user = userId;
|
|
327
|
+
await addRole(roleData, session);
|
|
328
|
+
|
|
329
|
+
await session.commitTransaction();
|
|
330
|
+
return "User created successfully with role assigned.";
|
|
331
|
+
} catch (error) {
|
|
332
|
+
await session.abortTransaction();
|
|
333
|
+
throw new BadRequestError("Failed to create user with role.");
|
|
334
|
+
} finally {
|
|
335
|
+
session.endSession();
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
createUserWithRole,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Service Guidelines
|
|
346
|
+
|
|
347
|
+
- **Use transactions** for operations involving multiple repositories
|
|
348
|
+
- **Coordinate business logic** across different domains
|
|
349
|
+
- **Handle third-party integrations** (APIs, file uploads, external services)
|
|
350
|
+
- **Transform data** between different formats/schemas
|
|
351
|
+
- **Implement retry logic** for external service calls
|
|
352
|
+
|
|
353
|
+
## Controller Layer
|
|
354
|
+
|
|
355
|
+
### Responsibilities
|
|
356
|
+
|
|
357
|
+
- Handle HTTP request/response lifecycle
|
|
358
|
+
- Input validation
|
|
359
|
+
- Choose between repository or service calls
|
|
360
|
+
- Error handling and response formatting
|
|
361
|
+
|
|
362
|
+
### Example Structure
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { Request, Response, NextFunction } from "express";
|
|
366
|
+
import { BadRequestError } from "@eeplatform/nodejs-utils";
|
|
367
|
+
import Joi from "joi";
|
|
368
|
+
import { useUserRepo } from "./user.repository";
|
|
369
|
+
import { useUserService } from "./user.service";
|
|
370
|
+
import { schemaUser } from "./user.model";
|
|
371
|
+
|
|
372
|
+
export function useUserController() {
|
|
373
|
+
const { getById: _getById } = useUserRepo();
|
|
374
|
+
const { createUserWithRole } = useUserService();
|
|
375
|
+
|
|
376
|
+
// Simple operation - use repository directly
|
|
377
|
+
async function getById(req: Request, res: Response, next: NextFunction) {
|
|
378
|
+
const validation = Joi.object({
|
|
379
|
+
id: Joi.string().hex().required(),
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
const { error } = validation.validate(req.params);
|
|
383
|
+
if (error) {
|
|
384
|
+
next(new BadRequestError(`Validation error: ${error.message}`));
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const user = await _getById(req.params.id);
|
|
390
|
+
res.json(user);
|
|
391
|
+
} catch (error) {
|
|
392
|
+
next(error);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Complex operation - use service
|
|
397
|
+
async function createWithRole(
|
|
398
|
+
req: Request,
|
|
399
|
+
res: Response,
|
|
400
|
+
next: NextFunction
|
|
401
|
+
) {
|
|
402
|
+
const { error } = schemaUser.validate(req.body);
|
|
403
|
+
if (error) {
|
|
404
|
+
next(new BadRequestError(`Validation error: ${error.message}`));
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
const result = await createUserWithRole(req.body.user, req.body.role);
|
|
410
|
+
res.status(201).json({ message: result });
|
|
411
|
+
} catch (error) {
|
|
412
|
+
next(error);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return {
|
|
417
|
+
getById,
|
|
418
|
+
createWithRole,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Controller Decision Matrix
|
|
424
|
+
|
|
425
|
+
- **Use Repository** for:
|
|
426
|
+
|
|
427
|
+
- Simple CRUD operations
|
|
428
|
+
- Direct data retrieval
|
|
429
|
+
- Single-table operations
|
|
430
|
+
- Operations without business logic
|
|
431
|
+
|
|
432
|
+
- **Use Service** for:
|
|
433
|
+
- Complex business logic
|
|
434
|
+
- Multi-repository operations
|
|
435
|
+
- Third-party integrations
|
|
436
|
+
- Transaction-based operations
|
|
437
|
+
- Data transformation requirements
|
|
438
|
+
|
|
439
|
+
## Routes Layer
|
|
440
|
+
|
|
441
|
+
### Responsibilities
|
|
442
|
+
|
|
443
|
+
- Define API endpoints
|
|
444
|
+
- Apply middleware
|
|
445
|
+
- Map HTTP methods to controllers
|
|
446
|
+
- Handle route-specific security
|
|
447
|
+
|
|
448
|
+
### Example Structure
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
import express from "express";
|
|
452
|
+
import { requireAuth } from "@eeplatform/nodejs-utils";
|
|
453
|
+
import { useUserController } from "./user.controller";
|
|
454
|
+
|
|
455
|
+
const router = express.Router();
|
|
456
|
+
|
|
457
|
+
const { getById, createWithRole, updateById, deleteById } = useUserController();
|
|
458
|
+
|
|
459
|
+
// Public routes
|
|
460
|
+
router.post("/register", createWithRole);
|
|
461
|
+
|
|
462
|
+
// Protected routes
|
|
463
|
+
router.get("/:id", requireAuth, getById);
|
|
464
|
+
router.put("/:id", requireAuth, updateById);
|
|
465
|
+
router.delete("/:id", requireAuth, deleteById);
|
|
466
|
+
|
|
467
|
+
export default router;
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Route Guidelines
|
|
471
|
+
|
|
472
|
+
- **Group related endpoints** in the same route file
|
|
473
|
+
- **Apply authentication middleware** to protected routes
|
|
474
|
+
- **Use consistent HTTP methods** (GET, POST, PUT, PATCH, DELETE)
|
|
475
|
+
- **Include route parameters** in URL paths (e.g., `/:id`)
|
|
476
|
+
- **Apply rate limiting** for public endpoints
|
|
477
|
+
- **Use specific middleware** for route-specific requirements
|
|
478
|
+
|
|
479
|
+
### HTTP Method Conventions
|
|
480
|
+
|
|
481
|
+
- **GET**: Retrieve data (no side effects)
|
|
482
|
+
- **POST**: Create new resources
|
|
483
|
+
- **PUT**: Replace entire resource
|
|
484
|
+
- **PATCH**: Partial resource updates
|
|
485
|
+
- **DELETE**: Remove resources
|
|
486
|
+
|
|
487
|
+
## Type Definitions
|
|
488
|
+
|
|
489
|
+
### Naming Conventions
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
// Types with 'T' prefix
|
|
493
|
+
export type TUser = {
|
|
494
|
+
/* ... */
|
|
495
|
+
};
|
|
496
|
+
export type TSchool = {
|
|
497
|
+
/* ... */
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
// Interfaces with 'I' prefix (rare usage)
|
|
501
|
+
export interface IUserService {
|
|
502
|
+
/* ... */
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Enums with descriptive names
|
|
506
|
+
export enum UserStatus {
|
|
507
|
+
ACTIVE = "active",
|
|
508
|
+
INACTIVE = "inactive",
|
|
509
|
+
SUSPENDED = "suspended",
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Common Type Patterns
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
// Base fields for all entities
|
|
517
|
+
type BaseEntity = {
|
|
518
|
+
_id?: ObjectId;
|
|
519
|
+
status?: string;
|
|
520
|
+
createdAt?: Date | string;
|
|
521
|
+
updatedAt?: Date | string;
|
|
522
|
+
deletedAt?: Date | string;
|
|
523
|
+
createdBy?: string | ObjectId;
|
|
524
|
+
updatedBy?: string | ObjectId;
|
|
525
|
+
deletedBy?: string | ObjectId;
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
// Pagination parameters
|
|
529
|
+
type PaginationParams = {
|
|
530
|
+
page?: number;
|
|
531
|
+
limit?: number;
|
|
532
|
+
search?: string;
|
|
533
|
+
sort?: Record<string, number>;
|
|
534
|
+
status?: string;
|
|
535
|
+
};
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
## Error Handling
|
|
539
|
+
|
|
540
|
+
### Error Types
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
import {
|
|
544
|
+
BadRequestError,
|
|
545
|
+
NotFoundError,
|
|
546
|
+
InternalServerError,
|
|
547
|
+
UnauthorizedError,
|
|
548
|
+
ForbiddenError,
|
|
549
|
+
} from "@eeplatform/nodejs-utils";
|
|
550
|
+
|
|
551
|
+
// Usage examples
|
|
552
|
+
throw new BadRequestError("Invalid input data");
|
|
553
|
+
throw new NotFoundError("User not found");
|
|
554
|
+
throw new UnauthorizedError("Access token required");
|
|
555
|
+
throw new ForbiddenError("Insufficient permissions");
|
|
556
|
+
throw new InternalServerError("Database connection failed");
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### Error Handling Patterns
|
|
560
|
+
|
|
561
|
+
```typescript
|
|
562
|
+
// In Controllers
|
|
563
|
+
try {
|
|
564
|
+
const result = await someOperation();
|
|
565
|
+
res.json(result);
|
|
566
|
+
} catch (error) {
|
|
567
|
+
next(error); // Pass to error middleware
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// In Services/Repositories
|
|
571
|
+
if (!data) {
|
|
572
|
+
throw new NotFoundError("Resource not found");
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (validationError) {
|
|
576
|
+
throw new BadRequestError(`Validation failed: ${validationError.message}`);
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
## Documentation
|
|
581
|
+
|
|
582
|
+
### Code Documentation
|
|
583
|
+
|
|
584
|
+
```typescript
|
|
585
|
+
/**
|
|
586
|
+
* Creates a new user with assigned role
|
|
587
|
+
* @param userData - User information and credentials
|
|
588
|
+
* @param roleData - Role assignment details
|
|
589
|
+
* @returns Success message
|
|
590
|
+
* @throws BadRequestError when validation fails
|
|
591
|
+
* @throws InternalServerError when database operation fails
|
|
592
|
+
*/
|
|
593
|
+
async function createUserWithRole(
|
|
594
|
+
userData: TUser,
|
|
595
|
+
roleData: any
|
|
596
|
+
): Promise<string> {
|
|
597
|
+
// Implementation
|
|
598
|
+
}
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### API Documentation
|
|
602
|
+
|
|
603
|
+
- Use OpenAPI/Swagger specifications
|
|
604
|
+
- Document request/response schemas
|
|
605
|
+
- Include example payloads
|
|
606
|
+
- Document error responses
|
|
607
|
+
- Provide authentication requirements
|
|
608
|
+
|
|
609
|
+
### README Requirements
|
|
610
|
+
|
|
611
|
+
Each module should include:
|
|
612
|
+
|
|
613
|
+
- Purpose and functionality
|
|
614
|
+
- Installation instructions
|
|
615
|
+
- Configuration options
|
|
616
|
+
- Usage examples
|
|
617
|
+
- API endpoint documentation
|
|
618
|
+
- Contributing guidelines
|
|
619
|
+
|
|
620
|
+
## Best Practices Summary
|
|
621
|
+
|
|
622
|
+
1. **Separation of Concerns**: Each layer has distinct responsibilities
|
|
623
|
+
2. **Caching Strategy**: Cache reads, invalidate on writes
|
|
624
|
+
3. **Error Handling**: Use appropriate error types and propagate properly
|
|
625
|
+
4. **Validation**: Validate at model level and controller level
|
|
626
|
+
5. **Transactions**: Use for operations affecting multiple collections
|
|
627
|
+
6. **Logging**: Log cache operations, errors, and important events
|
|
628
|
+
7. **Type Safety**: Use TypeScript strictly, define all types
|
|
629
|
+
8. **Documentation**: Clear, maintained documentation for all APIs
|
|
630
|
+
9. **Security**: Apply authentication and authorization consistently
|
|
631
|
+
|
|
632
|
+
This convention ensures maintainable, scalable, and consistent code across the entire EEPlatform ecosystem.
|