@carecard/common-util 3.0.10 → 3.1.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/index.d.ts CHANGED
@@ -4,7 +4,10 @@
4
4
  * @param arrayOfProperties - An array of property names to extract.
5
5
  * @returns A new object with the extracted properties.
6
6
  */
7
- export function extractObjectWithProperties(obj: any, arrayOfProperties: string[]): Record<string, any>;
7
+ export function extractObjectWithProperties(
8
+ obj: any,
9
+ arrayOfProperties: string[]
10
+ ): Record<string, any>;
8
11
 
9
12
  /**
10
13
  * Utility functions for object manipulation.
@@ -15,47 +18,56 @@ export const util: {
15
18
  };
16
19
 
17
20
  /** Throws an Account_Suspended error. */
18
- export function throwAccountSuspendedError(params?: { userMessage?: string, details?: any }): never;
21
+ export function throwAccountSuspendedError(params?: { userMessage?: string; details?: any }): never;
19
22
  /** Throws an Account_Blocked error. */
20
- export function throwAccountBlockedError(params?: { userMessage?: string, details?: any }): never;
23
+ export function throwAccountBlockedError(params?: { userMessage?: string; details?: any }): never;
21
24
  /** Throws an Account_Inactive error. */
22
- export function throwAccountInactiveError(params?: { userMessage?: string, details?: any }): never;
25
+ export function throwAccountInactiveError(params?: { userMessage?: string; details?: any }): never;
23
26
  /** Middleware to handle 404 Not Found. */
24
27
  export function notFound404(req: any, res: any, next: any): void;
25
28
  /** Central application error handler middleware. */
26
29
  export function appErrorHandler(err: any, req: any, res: any, next: any): void;
27
30
  /** Throws a Validation_Failure error. */
28
- export function throwValidationFailureError(params?: { userMessage?: string, details?: any }): never;
31
+ export function throwValidationFailureError(params?: {
32
+ userMessage?: string;
33
+ details?: any;
34
+ }): never;
29
35
  /** Throws a Record_Exist error. */
30
- export function throwRecordExistError(params?: { userMessage?: string, details?: any }): never;
36
+ export function throwRecordExistError(params?: { userMessage?: string; details?: any }): never;
31
37
  /** Throws a Wrong_Credentials error. */
32
- export function throwWrongCredentialsError(params?: { userMessage?: string, details?: any }): never;
38
+ export function throwWrongCredentialsError(params?: { userMessage?: string; details?: any }): never;
33
39
  /** Throws a Login_Required error. */
34
- export function throwLoginRequiredError(params?: { userMessage?: string, details?: any }): never;
40
+ export function throwLoginRequiredError(params?: { userMessage?: string; details?: any }): never;
35
41
  /** Throws a Record_NotFound error. */
36
- export function throwRecordNotFoundError(params?: { userMessage?: string, details?: any }): never;
42
+ export function throwRecordNotFoundError(params?: { userMessage?: string; details?: any }): never;
37
43
  /** Throws a Record_NotSaved error. */
38
- export function throwRecordNotSavedError(params?: { userMessage?: string, details?: any }): never;
44
+ export function throwRecordNotSavedError(params?: { userMessage?: string; details?: any }): never;
39
45
  /** Throws an Update_Failed error. */
40
- export function throwUpdateFailedError(params?: { userMessage?: string, details?: any }): never;
46
+ export function throwUpdateFailedError(params?: { userMessage?: string; details?: any }): never;
41
47
  /** Throws a Transaction_Failed error. */
42
- export function throwTransactionFailedError(params?: { userMessage?: string, details?: any }): never;
48
+ export function throwTransactionFailedError(params?: {
49
+ userMessage?: string;
50
+ details?: any;
51
+ }): never;
43
52
  /** Throws a Used_Token error. */
44
- export function throwUsedTokenError(params?: { userMessage?: string, details?: any }): never;
53
+ export function throwUsedTokenError(params?: { userMessage?: string; details?: any }): never;
45
54
  /** Throws a Bad_Visitor_Token error. */
46
- export function throwBadVisitorTokenError(params?: { userMessage?: string, details?: any }): never;
55
+ export function throwBadVisitorTokenError(params?: { userMessage?: string; details?: any }): never;
47
56
  /** Throws a File_Format_Not_Supported error. */
48
- export function throwFileFormatNotSupportedError(params?: { userMessage?: string, details?: any }): never;
57
+ export function throwFileFormatNotSupportedError(params?: {
58
+ userMessage?: string;
59
+ details?: any;
60
+ }): never;
49
61
  /** Throws a Not_Authorized error. */
50
- export function throwNotAuthorizedError(params?: { userMessage?: string, details?: any }): never;
62
+ export function throwNotAuthorizedError(params?: { userMessage?: string; details?: any }): never;
51
63
  /** Throws a Bad_Input error. */
52
- export function throwBadInputError(params?: { userMessage?: string, details?: any }): never;
64
+ export function throwBadInputError(params?: { userMessage?: string; details?: any }): never;
53
65
  /** Throws an Input_Not_Uuid error. */
54
- export function throwInputNotUuidError(params?: { userMessage?: string, details?: any }): never;
66
+ export function throwInputNotUuidError(params?: { userMessage?: string; details?: any }): never;
55
67
  /** Throws a File_Too_Large error. */
56
- export function throwFileTooLargeError(params?: { userMessage?: string, details?: any }): never;
68
+ export function throwFileTooLargeError(params?: { userMessage?: string; details?: any }): never;
57
69
  /** Throws an Invalid_Time_Value error. */
58
- export function throwInvalidTimeValueError(params?: { userMessage?: string, details?: any }): never;
70
+ export function throwInvalidTimeValueError(params?: { userMessage?: string; details?: any }): never;
59
71
 
60
72
  /**
61
73
  * Application-level error handlers and throwers.
@@ -101,3 +113,78 @@ export const resCode: {
101
113
  setCreated201: typeof setCreated201;
102
114
  setBadRequest400ClientError: typeof setBadRequest400ClientError;
103
115
  };
116
+
117
+ // --- New Standardized API Response System ---
118
+
119
+ /**
120
+ * Standard API response metadata.
121
+ */
122
+ export interface ApiResponseMeta {
123
+ version: string;
124
+ service: string;
125
+ environment: string;
126
+ timestamp: string;
127
+ requestId: string;
128
+ traceId: string;
129
+ client?: {
130
+ appId?: string;
131
+ ip?: string;
132
+ };
133
+ pagination?: {
134
+ page: number;
135
+ limit: number;
136
+ total: number;
137
+ };
138
+ [key: string]: any;
139
+ }
140
+
141
+ /**
142
+ * Standard API error object.
143
+ */
144
+ export interface ApiError {
145
+ code: string;
146
+ message?: string;
147
+ details?: string;
148
+ fields?: Record<string, string>;
149
+ }
150
+
151
+ /**
152
+ * Standard API response body.
153
+ */
154
+ export interface ApiResponse<T = any> {
155
+ success: boolean;
156
+ statusCode: number;
157
+ message: string;
158
+ data: T | null;
159
+ error: ApiError | null;
160
+ meta: ApiResponseMeta;
161
+ }
162
+
163
+ /**
164
+ * Express middleware to generate and attach request context.
165
+ */
166
+ export function requestContext(req: any, res: any, next: any): void;
167
+
168
+ /**
169
+ * Standardized API response utility.
170
+ */
171
+ export function sendResponse<T = any>(params: {
172
+ req: any;
173
+ res: any;
174
+ statusCode?: number;
175
+ success?: boolean;
176
+ message?: string;
177
+ data?: T | null;
178
+ error?: ApiError | null;
179
+ meta?: Partial<ApiResponseMeta>;
180
+ }): any;
181
+
182
+ /**
183
+ * Helper for standardized errors.
184
+ */
185
+ export function createError(params: {
186
+ code: string;
187
+ details?: string;
188
+ message?: string;
189
+ fields?: Record<string, string>;
190
+ }): ApiError;
package/index.js CHANGED
@@ -1,5 +1,8 @@
1
- module.exports = {
2
- util: require( './lib/untilityFunctions' ),
3
- error: require( './lib/appErrorHandlers' ),
4
- resCode: require( './lib/responseStatus' )
5
- }
1
+ 'use strict';
2
+
3
+ /**
4
+ * @carecard/common-util
5
+ * Standardized API response and utility functions for Express.js and Next.js microservices.
6
+ */
7
+
8
+ module.exports = require('./src/index');
package/index.mjs ADDED
@@ -0,0 +1,5 @@
1
+ import commonUtil from './index.js';
2
+
3
+ export const { util, error, resCode, requestContext, sendResponse, createError } = commonUtil;
4
+
5
+ export default commonUtil;
package/package.json CHANGED
@@ -1,29 +1,67 @@
1
1
  {
2
2
  "name": "@carecard/common-util",
3
- "version": "3.0.10",
4
- "repository": "https://github.com/CareCard-ca/pkg-common-util",
5
- "description": "Common utility for MVC framework",
3
+ "version": "3.1.1",
4
+ "description": "Standardized API response and utility functions for Express.js and Next.js microservices",
6
5
  "main": "index.js",
6
+ "module": "index.mjs",
7
7
  "types": "index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./index.mjs",
11
+ "require": "./index.js",
12
+ "types": "./index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "index.js",
17
+ "index.mjs",
18
+ "index.d.ts",
19
+ "src"
20
+ ],
21
+ "engines": {
22
+ "node": ">=18.0.0"
23
+ },
24
+ "sideEffects": false,
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/CareCard-ca/pkg-common-util"
28
+ },
8
29
  "scripts": {
9
30
  "test": "export NODE_ENV=test && mocha --recursive",
10
- "test:types": "tsc --noEmit && mocha -r ts-node/register test/types.test.ts"
31
+ "test:types": "tsc --noEmit && mocha -r ts-node/register test/types.test.ts",
32
+ "test:coverage": "export NODE_ENV=test && nyc mocha --recursive",
33
+ "test:types:coverage": "tsc --noEmit && export NODE_ENV=test && nyc mocha -r ts-node/register 'test/**/*.{js,ts}'",
34
+ "coverage": "export NODE_ENV=test && nyc mocha --recursive -r ts-node/register 'test/**/*.{js,ts}' && nyc check-coverage",
35
+ "prettier": "prettier --write .",
36
+ "prettier:check": "prettier --check .",
37
+ "prepare": "husky"
11
38
  },
12
39
  "keywords": [
13
- "validate",
14
- "data"
40
+ "express",
41
+ "nextjs",
42
+ "api-response",
43
+ "standardization",
44
+ "utility",
45
+ "request-context",
46
+ "tracing"
15
47
  ],
16
48
  "author": "CareCard team",
17
49
  "license": "ISC",
18
50
  "devDependencies": {
51
+ "@istanbuljs/nyc-config-typescript": "1.0.2",
19
52
  "@types/mocha": "10.0.10",
20
53
  "@types/node": "25.5.0",
21
- "mocha": "11.7.5",
54
+ "express": "5.1.0",
55
+ "husky": "9.1.7",
56
+ "mocha": "^11.3.0",
57
+ "nyc": "17.1.0",
58
+ "prettier": "^3.8.1",
59
+ "source-map-support": "0.5.21",
22
60
  "supertest": "7.1.4",
23
61
  "ts-node": "10.9.2",
24
62
  "typescript": "5.9.3"
25
63
  },
26
- "dependencies": {
27
- "express": "5.1.0"
64
+ "peerDependencies": {
65
+ "express": "^4.0.0 || ^5.0.0"
28
66
  }
29
67
  }
package/readme.md CHANGED
@@ -1,7 +1,161 @@
1
- #Common Utility
2
- ### Functions
1
+ # @carecard/common-util
3
2
 
4
- Extract specific attributes from an object
5
- ```js
6
- extractObjectWithProperties( obj, arrayOfProperties )
3
+ Standardized API response system, request context middleware, and utility functions for Express.js and Next.js microservices.
4
+
5
+ ## Features
6
+
7
+ - **Standardized Response Format**: Consistent JSON structure for all API responses.
8
+ - **Request Context Middleware**: Automatic generation of `requestId` and `traceId`.
9
+ - **Distributed Tracing Support**: Header-based trace propagation (`x-trace-id`).
10
+ - **Type Safety**: Full TypeScript support with included type definitions.
11
+ - **Next.js & Express Compatibility**: Works seamlessly across different Node.js frameworks.
12
+ - **Microservice Ready**: Built-in metadata for service identification, environment, and versioning.
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @carecard/common-util
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ### Express.js
23
+
24
+ #### Setup Middleware
25
+
26
+ ```javascript
27
+ const express = require('express');
28
+ const { requestContext } = require('@carecard/common-util');
29
+
30
+ const app = express();
31
+
32
+ // Attach request context (generates requestId, traceId, extracts client info)
33
+ app.use(requestContext);
34
+ ```
35
+
36
+ #### Sending Responses
37
+
38
+ ```javascript
39
+ const { sendResponse } = require('@carecard/common-util');
40
+
41
+ app.get('/api/users', (req, res) => {
42
+ const users = [{ id: 1, name: 'John Doe' }];
43
+
44
+ return sendResponse({
45
+ req,
46
+ res,
47
+ message: 'Users fetched successfully',
48
+ data: users,
49
+ meta: {
50
+ pagination: { page: 1, limit: 10, total: 1 }
51
+ }
52
+ });
53
+ });
54
+ ```
55
+
56
+ #### Standardized Error Handling
57
+
58
+ ```javascript
59
+ const { error } = require('@carecard/common-util');
60
+ const { appErrorHandler, notFound404, throwRecordNotFoundError } = error;
61
+
62
+ const app = require('express')();
63
+
64
+ // Usage in controllers
65
+ app.get('/api/users/:id', (req, res) => {
66
+ const user = null;
67
+ if (!user) {
68
+ throwRecordNotFoundError({ userMessage: 'User not found' });
69
+ }
70
+ });
71
+
72
+ // Handle 404s
73
+ app.use(notFound404);
74
+
75
+ // Central error handler
76
+ app.use(appErrorHandler);
7
77
  ```
78
+
79
+ ### Next.js (API Routes)
80
+
81
+ #### JavaScript Example
82
+
83
+ ```javascript
84
+ // pages/api/hello.js
85
+ import { sendResponse } from '@carecard/common-util';
86
+
87
+ export default function handler(req, res) {
88
+ return sendResponse({
89
+ req,
90
+ res,
91
+ message: 'Hello from Next.js!',
92
+ data: { greeting: 'Welcome' }
93
+ });
94
+ }
95
+ ```
96
+
97
+ #### TypeScript Example
98
+
99
+ ```typescript
100
+ // pages/api/hello.ts
101
+ import type { NextApiRequest, NextApiResponse } from 'next';
102
+ import { sendResponse } from '@carecard/common-util';
103
+
104
+ export default function handler(req: NextApiRequest, res: NextApiResponse) {
105
+ return sendResponse({
106
+ req,
107
+ res,
108
+ message: 'Typed response',
109
+ data: { status: 'ok' }
110
+ });
111
+ }
112
+ ```
113
+
114
+ ## API Reference
115
+
116
+ ### `sendResponse({ req, res, statusCode, success, message, data, error, meta })`
117
+
118
+ Sends a standardized JSON response.
119
+
120
+ ### `requestContext(req, res, next)`
121
+
122
+ Middleware that attaches:
123
+
124
+ - `req.requestId`: UUID v4 unique to the request.
125
+ - `req.traceId`: Propagated from `x-trace-id` header or newly generated.
126
+ - `req.client`: Object containing `appId` (from `x-app-id`) and `ip`.
127
+
128
+ ### `createError({ code, message, details, fields })`
129
+
130
+ Helper to create a standardized error object for `sendResponse`.
131
+
132
+ ## Standard Response Structure
133
+
134
+ ```json
135
+ {
136
+ "success": true,
137
+ "statusCode": 200,
138
+ "message": "Success message",
139
+ "data": { ... },
140
+ "error": null,
141
+ "meta": {
142
+ "version": "1.0.0",
143
+ "service": "my-service",
144
+ "environment": "production",
145
+ "timestamp": "2024-03-26T00:00:00.000Z",
146
+ "requestId": "...",
147
+ "traceId": "...",
148
+ "client": { "appId": "...", "ip": "..." }
149
+ }
150
+ }
151
+ ```
152
+
153
+ ## Environment Variables
154
+
155
+ - `API_VERSION`: Used in `meta.version` (default: `1.0.0`)
156
+ - `SERVICE_NAME`: Used in `meta.service` (default: `unknown-service`)
157
+ - `NODE_ENV`: Used in `meta.environment` (default: `development`)
158
+
159
+ ## License
160
+
161
+ ISC
package/src/index.js ADDED
@@ -0,0 +1,12 @@
1
+ const requestContext = require('./middleware/requestContext');
2
+ const sendResponse = require('./utils/sendResponse');
3
+ const createError = require('./utils/createError');
4
+
5
+ module.exports = {
6
+ util: require('./lib/utilityFunctions'),
7
+ error: require('./lib/appErrorHandlers'),
8
+ resCode: require('./lib/responseStatus'),
9
+ requestContext,
10
+ sendResponse,
11
+ createError
12
+ };
@@ -0,0 +1,265 @@
1
+ 'use strict';
2
+
3
+ const sendResponse = require('../utils/sendResponse');
4
+ const createError = require('../utils/createError');
5
+
6
+ /**
7
+ * These are application level error handlers. All error responses should be send using these functions.
8
+ * These should be use in app.js file, not in controller files.
9
+ */
10
+ module.exports = {
11
+ throwAccountSuspendedError,
12
+ throwAccountBlockedError,
13
+ throwAccountInactiveError,
14
+ notFound404,
15
+ appErrorHandler,
16
+ throwValidationFailureError,
17
+ throwRecordExistError,
18
+ throwWrongCredentialsError,
19
+ throwLoginRequiredError,
20
+ throwRecordNotFoundError,
21
+ throwRecordNotSavedError,
22
+ throwUpdateFailedError,
23
+ throwTransactionFailedError,
24
+ throwUsedTokenError,
25
+ throwBadVisitorTokenError: throwVisitorTokenError,
26
+ throwFileFormatNotSupportedError,
27
+ throwNotAuthorizedError,
28
+ throwBadInputError,
29
+ throwInputNotUuidError,
30
+ throwFileTooLargeError,
31
+ throwInvalidTimeValueError
32
+ };
33
+
34
+ function notFound404(req, res, next) {
35
+ return sendResponse({
36
+ req,
37
+ res,
38
+ statusCode: 404,
39
+ success: false,
40
+ message: 'Not found',
41
+ error: createError({
42
+ code: 'NOT_FOUND',
43
+ message: 'Not found',
44
+ details: null
45
+ })
46
+ });
47
+ }
48
+
49
+ function appErrorHandler(err, req, res, next) {
50
+ let statusCode = 500;
51
+ const errorMessage = err?.message || 'Internal Server Error';
52
+
53
+ switch (err?.message) {
54
+ case 'Account_Suspended':
55
+ case 'Account_Blocked':
56
+ case 'Account_Inactive':
57
+ statusCode = 403;
58
+ break;
59
+
60
+ case 'Validation_Failure':
61
+ case 'Used_Token':
62
+ case 'Wrong_Credentials':
63
+ case 'Bad_Visitor_Token':
64
+ case 'Login_Required':
65
+ case 'Not_Authorized':
66
+ statusCode = 401;
67
+ break;
68
+
69
+ case 'Record_NotSaved':
70
+ case 'Update_Failed':
71
+ case 'Transaction_Failed':
72
+ case 'Bad_Input':
73
+ case 'Input_Not_Uuid':
74
+ statusCode = 400;
75
+ break;
76
+
77
+ case 'Record_Exist':
78
+ statusCode = 409;
79
+ break;
80
+
81
+ case 'Record_NotFound':
82
+ statusCode = 404;
83
+ break;
84
+
85
+ case 'File_Format_Not_Supported':
86
+ statusCode = 415;
87
+ break;
88
+
89
+ case 'File too large':
90
+ statusCode = 413;
91
+ break;
92
+
93
+ case 'Invalid time value':
94
+ statusCode = 403;
95
+ break;
96
+
97
+ default:
98
+ statusCode = 500;
99
+ }
100
+
101
+ return sendResponse({
102
+ req,
103
+ res,
104
+ statusCode,
105
+ success: false,
106
+ message: errorMessage,
107
+ error: createError({
108
+ code: err?.code ?? null,
109
+ message: err?.userMessage ?? null,
110
+ details: err?.details ?? null
111
+ })
112
+ });
113
+ }
114
+
115
+ function throwAccountSuspendedError(params = {}) {
116
+ const error = new Error('Account_Suspended');
117
+ error.code = 'ACCOUNT_SUSPENDED';
118
+ error.userMessage = params.userMessage ?? null;
119
+ error.details = params.details ?? null;
120
+ throw error;
121
+ }
122
+
123
+ function throwAccountBlockedError(params = {}) {
124
+ const error = new Error('Account_Blocked');
125
+ error.code = 'ACCOUNT_BLOCKED';
126
+ error.userMessage = params.userMessage ?? null;
127
+ error.details = params.details ?? null;
128
+ throw error;
129
+ }
130
+
131
+ function throwAccountInactiveError(params = {}) {
132
+ const error = new Error('Account_Inactive');
133
+ error.code = 'ACCOUNT_INACTIVE';
134
+ error.userMessage = params.userMessage ?? null;
135
+ error.details = params.details ?? null;
136
+ throw error;
137
+ }
138
+
139
+ function throwValidationFailureError(params = {}) {
140
+ const error = new Error('Validation_Failure');
141
+ error.code = 'VALIDATION_FAILURE';
142
+ error.userMessage = params.userMessage ?? null;
143
+ error.details = params.details ?? null;
144
+ throw error;
145
+ }
146
+
147
+ function throwWrongCredentialsError(params = {}) {
148
+ const error = new Error('Wrong_Credentials');
149
+ error.code = 'WRONG_CREDENTIALS';
150
+ error.userMessage = params.userMessage ?? null;
151
+ error.details = params.details ?? null;
152
+ throw error;
153
+ }
154
+
155
+ function throwRecordExistError(params = {}) {
156
+ const error = new Error('Record_Exist');
157
+ error.code = 'RECORD_EXIST';
158
+ error.userMessage = params.userMessage ?? null;
159
+ error.details = params.details ?? null;
160
+ throw error;
161
+ }
162
+
163
+ function throwRecordNotFoundError(params = {}) {
164
+ const error = new Error('Record_NotFound');
165
+ error.code = 'RECORD_NOT_FOUND';
166
+ error.userMessage = params.userMessage ?? null;
167
+ error.details = params.details ?? null;
168
+ throw error;
169
+ }
170
+
171
+ function throwRecordNotSavedError(params = {}) {
172
+ const error = new Error('Record_NotSaved');
173
+ error.code = 'RECORD_NOT_SAVED';
174
+ error.userMessage = params.userMessage ?? null;
175
+ error.details = params.details ?? null;
176
+ throw error;
177
+ }
178
+
179
+ function throwLoginRequiredError(params = {}) {
180
+ const error = new Error('Login_Required');
181
+ error.code = 'LOGIN_REQUIRED';
182
+ error.userMessage = params.userMessage ?? null;
183
+ error.details = params.details ?? null;
184
+ throw error;
185
+ }
186
+
187
+ function throwUpdateFailedError(params = {}) {
188
+ const error = new Error('Update_Failed');
189
+ error.code = 'UPDATE_FAILED';
190
+ error.userMessage = params.userMessage ?? null;
191
+ error.details = params.details ?? null;
192
+ throw error;
193
+ }
194
+
195
+ function throwTransactionFailedError(params = {}) {
196
+ const error = new Error('Transaction_Failed');
197
+ error.code = 'TRANSACTION_FAILED';
198
+ error.userMessage = params.userMessage ?? null;
199
+ error.details = params.details ?? null;
200
+ throw error;
201
+ }
202
+
203
+ function throwUsedTokenError(params = {}) {
204
+ const error = new Error('Used_Token');
205
+ error.code = 'USED_TOKEN';
206
+ error.userMessage = params.userMessage ?? null;
207
+ error.details = params.details ?? null;
208
+ throw error;
209
+ }
210
+
211
+ function throwVisitorTokenError(params = {}) {
212
+ const error = new Error('Bad_Visitor_Token');
213
+ error.code = 'BAD_VISITOR_TOKEN';
214
+ error.userMessage = params.userMessage ?? null;
215
+ error.details = params.details ?? null;
216
+ throw error;
217
+ }
218
+
219
+ function throwFileFormatNotSupportedError(params = {}) {
220
+ const error = new Error('File_Format_Not_Supported');
221
+ error.code = 'FILE_FORMAT_NOT_SUPPORTED';
222
+ error.userMessage = params.userMessage ?? null;
223
+ error.details = params.details ?? null;
224
+ throw error;
225
+ }
226
+
227
+ function throwNotAuthorizedError(params = {}) {
228
+ const error = new Error('Not_Authorized');
229
+ error.code = 'NOT_AUTHORIZED';
230
+ error.userMessage = params.userMessage ?? null;
231
+ error.details = params.details ?? null;
232
+ throw error;
233
+ }
234
+
235
+ function throwBadInputError(params = {}) {
236
+ const error = new Error('Bad_Input');
237
+ error.code = 'BAD_INPUT';
238
+ error.userMessage = params.userMessage ?? null;
239
+ error.details = params.details ?? null;
240
+ throw error;
241
+ }
242
+
243
+ function throwInputNotUuidError(params = {}) {
244
+ const error = new Error('Input_Not_Uuid');
245
+ error.code = 'INPUT_NOT_UUID';
246
+ error.userMessage = params.userMessage ?? null;
247
+ error.details = params.details ?? null;
248
+ throw error;
249
+ }
250
+
251
+ function throwFileTooLargeError(params = {}) {
252
+ const error = new Error('File too large');
253
+ error.code = 'FILE_TOO_LARGE';
254
+ error.userMessage = params.userMessage ?? null;
255
+ error.details = params.details ?? null;
256
+ throw error;
257
+ }
258
+
259
+ function throwInvalidTimeValueError(params = {}) {
260
+ const error = new Error('Invalid time value');
261
+ error.code = 'INVALID_TIME_VALUE';
262
+ error.userMessage = params.userMessage ?? null;
263
+ error.details = params.details ?? null;
264
+ throw error;
265
+ }
@@ -26,12 +26,11 @@
26
26
  * @param ETag
27
27
  * @returns {*}
28
28
  */
29
- function setOk200( res, ETag ) {
29
+ function setOk200(res, ETag) {
30
+ res.set({ ETag: ETag });
31
+ res.status(200);
30
32
 
31
- res.set( { ETag: ETag } );
32
- res.status( 200 );
33
-
34
- return res;
33
+ return res;
35
34
  }
36
35
 
37
36
  /**
@@ -50,9 +49,9 @@ function setOk200( res, ETag ) {
50
49
  * @param res
51
50
  * @returns {*}
52
51
  */
53
- function setCreated201( res ) {
54
- res.status( 201 );
55
- return res;
52
+ function setCreated201(res) {
53
+ res.status(201);
54
+ return res;
56
55
  }
57
56
 
58
57
  /**
@@ -64,14 +63,13 @@ function setCreated201( res ) {
64
63
  * @param res
65
64
  * @returns {*}
66
65
  */
67
- function setBadRequest400ClientError( res ) {
68
- res.status( 400 );
69
- return res;
66
+ function setBadRequest400ClientError(res) {
67
+ res.status(400);
68
+ return res;
70
69
  }
71
70
 
72
71
  module.exports = {
73
- setOk200,
74
- setCreated201,
75
- setBadRequest400ClientError
72
+ setOk200,
73
+ setCreated201,
74
+ setBadRequest400ClientError
76
75
  };
77
-
@@ -0,0 +1,20 @@
1
+ // Create new object only with properties given in array.
2
+ function extractObjectWithProperties(obj, arrayOfProperties) {
3
+ const returnObj = {};
4
+
5
+ if (!obj || !Array.isArray(arrayOfProperties)) {
6
+ return returnObj;
7
+ }
8
+
9
+ arrayOfProperties.forEach((nameOfProperty) => {
10
+ if (Object.prototype.hasOwnProperty.call(obj, nameOfProperty)) {
11
+ returnObj[nameOfProperty] = obj[nameOfProperty];
12
+ }
13
+ });
14
+
15
+ return returnObj;
16
+ }
17
+
18
+ module.exports = {
19
+ extractObjectWithProperties
20
+ };
@@ -0,0 +1,30 @@
1
+ const { randomUUID } = require('node:crypto');
2
+
3
+ /**
4
+ * Express middleware to generate and attach request context.
5
+ *
6
+ * Generates requestId (UUID v4)
7
+ * Reads traceId from header 'x-trace-id' OR generates one
8
+ * Reads appId from header 'x-app-id'
9
+ * Extracts client IP
10
+ *
11
+ * @param {import('express').Request} req
12
+ * @param {import('express').Response} res
13
+ * @param {import('express').NextFunction} next
14
+ */
15
+ const requestContext = (req, res, next) => {
16
+ req.requestId = randomUUID();
17
+ req.traceId = req.headers['x-trace-id'] || randomUUID();
18
+
19
+ const appId = req.headers['x-app-id'];
20
+ const ip = req.ip || req.headers['x-forwarded-for'] || req.socket.remoteAddress;
21
+
22
+ req.client = {
23
+ appId,
24
+ ip
25
+ };
26
+
27
+ next();
28
+ };
29
+
30
+ module.exports = requestContext;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Standard API response metadata.
3
+ */
4
+ export interface ApiResponseMeta {
5
+ version: string;
6
+ service: string;
7
+ environment: string;
8
+ timestamp: string;
9
+ requestId: string;
10
+ traceId: string;
11
+ client?: {
12
+ appId?: string;
13
+ ip?: string;
14
+ };
15
+ pagination?: {
16
+ page: number;
17
+ limit: number;
18
+ total: number;
19
+ };
20
+ [key: string]: any;
21
+ }
22
+
23
+ /**
24
+ * Standard API error object.
25
+ */
26
+ export interface ApiError {
27
+ code: string;
28
+ message?: string;
29
+ details?: string;
30
+ fields?: Record<string, string>;
31
+ }
32
+
33
+ /**
34
+ * Standard API response body.
35
+ */
36
+ export interface ApiResponse<T = any> {
37
+ success: boolean;
38
+ statusCode: number;
39
+ message: string;
40
+ data: T | null;
41
+ error: ApiError | null;
42
+ meta: ApiResponseMeta;
43
+ }
44
+
45
+ /**
46
+ * Parameters for sendResponse utility.
47
+ */
48
+ export interface SendResponseParams<T = any> {
49
+ req: any;
50
+ res: any;
51
+ statusCode?: number;
52
+ success?: boolean;
53
+ message?: string;
54
+ data?: T | null;
55
+ error?: ApiError | null;
56
+ meta?: Partial<ApiResponseMeta>;
57
+ }
58
+
59
+ /**
60
+ * Parameters for createError utility.
61
+ */
62
+ export interface CreateErrorParams {
63
+ code: string;
64
+ details?: string;
65
+ message?: string;
66
+ fields?: Record<string, string>;
67
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Helper for standardized errors.
3
+ *
4
+ * @param {Object} params
5
+ * @param {string} params.code
6
+ * @param {string} [params.details]
7
+ * @param {string} [params.message]
8
+ * @param {Record<string, string>} [params.fields]
9
+ * @returns {Object} Standardized error object
10
+ */
11
+ const createError = ({ code, details, message, fields }) => {
12
+ return {
13
+ code,
14
+ details,
15
+ message,
16
+ fields
17
+ };
18
+ };
19
+
20
+ module.exports = createError;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Standardized API response utility.
3
+ *
4
+ * Automatically injects:
5
+ * - version (from env: API_VERSION)
6
+ * - service (from env: SERVICE_NAME)
7
+ * - environment (NODE_ENV)
8
+ * - timestamp
9
+ * - requestId
10
+ * - traceId
11
+ * - client info
12
+ *
13
+ * @param {Object} params
14
+ * @param {import('express').Request} params.req
15
+ * @param {import('express').Response} params.res
16
+ * @param {number} [params.statusCode=200]
17
+ * @param {boolean} [params.success=true]
18
+ * @param {string} [params.message='']
19
+ * @param {any} [params.data=null]
20
+ * @param {Object} [params.error=null]
21
+ * @param {Object} [params.meta={}]
22
+ */
23
+ const sendResponse = ({
24
+ req,
25
+ res,
26
+ statusCode = 200,
27
+ success = true,
28
+ message = '',
29
+ data = null,
30
+ error = null,
31
+ meta = {}
32
+ }) => {
33
+ const standardMeta = {
34
+ version: process.env.API_VERSION || '1.0.0',
35
+ service: process.env.SERVICE_NAME || 'unknown-service',
36
+ environment: process.env.NODE_ENV || 'development',
37
+ timestamp: new Date().toISOString(),
38
+ requestId: req.requestId || '',
39
+ traceId: req.traceId || '',
40
+ client:
41
+ req.client && typeof req.client === 'object' && req.client.constructor === Object
42
+ ? req.client
43
+ : {}
44
+ };
45
+
46
+ if (statusCode === 204) {
47
+ return res.status(204).send();
48
+ }
49
+
50
+ const response = {
51
+ success,
52
+ statusCode,
53
+ message,
54
+ data,
55
+ error,
56
+ meta: {
57
+ ...standardMeta,
58
+ ...meta
59
+ }
60
+ };
61
+
62
+ return res.status(statusCode).json(response);
63
+ };
64
+
65
+ module.exports = sendResponse;
@@ -1,297 +0,0 @@
1
- 'use strict';
2
- /**
3
- * These are application level error handlers. All error responses should be send using these functions.
4
- * These should be use in app.js file, not in controller files.
5
- */
6
- module.exports = {
7
- throwAccountSuspendedError,
8
- throwAccountBlockedError,
9
- throwAccountInactiveError,
10
- notFound404,
11
- appErrorHandler,
12
- throwValidationFailureError,
13
- throwRecordExistError,
14
- throwWrongCredentialsError,
15
- throwLoginRequiredError,
16
- throwRecordNotFoundError,
17
- throwRecordNotSavedError,
18
- throwUpdateFailedError,
19
- throwTransactionFailedError,
20
- throwUsedTokenError,
21
- throwBadVisitorTokenError: throwVisitorTokenError,
22
- throwFileFormatNotSupportedError,
23
- throwNotAuthorizedError,
24
- throwBadInputError,
25
- throwInputNotUuidError,
26
- throwFileTooLargeError,
27
- throwInvalidTimeValueError
28
- }
29
-
30
-
31
- function notFound404(req, res, next) {
32
- res.status(404);
33
- const response = {
34
- error:
35
- {
36
- code: 'NOT_FOUND',
37
- message: 'Not found',
38
- details: null
39
- }
40
- }
41
-
42
- res.send(response)
43
- }
44
-
45
- function appErrorHandler(err, req, res, next) {
46
- const response =
47
- {
48
- error:
49
- {
50
- code: err.code ?? null,
51
- message: err.userMessage ?? null,
52
- details: err.details ?? null
53
- }
54
- };
55
-
56
- switch (err?.message) {
57
-
58
- case "Account_Suspended":
59
- case "Account_Blocked":
60
- case "Account_Inactive":
61
- res.status(403);
62
- res.send(response)
63
- break;
64
-
65
- case "Validation_Failure":
66
- res.status(401);
67
- res.send(response)
68
- break;
69
-
70
- case "Used_Token":
71
- case "Wrong_Credentials":
72
- res.status(401);
73
- res.send(response)
74
- break;
75
-
76
- case "Bad_Visitor_Token":
77
- res.status(401);
78
- res.send(response)
79
- break;
80
-
81
- case "Record_NotSaved":
82
- res.status(400);
83
- res.send(response)
84
- break;
85
-
86
- case "Record_Exist":
87
- res.status(409);
88
- res.send(response)
89
- break;
90
-
91
- case "Record_NotFound":
92
- res.status(404);
93
- res.send(response)
94
- break;
95
-
96
- case "Update_Failed":
97
- res.status(400);
98
- res.send(response)
99
- break;
100
-
101
- case "Login_Required":
102
- res.status(401);
103
- res.send(response)
104
- break;
105
-
106
- case "Transaction_Failed":
107
- res.status(400);
108
- res.send(response)
109
- break;
110
-
111
- case "File_Format_Not_Supported":
112
- res.status(415);
113
- res.send(response)
114
- break;
115
-
116
- case "Not_Authorized":
117
- res.status(401);
118
- res.send(response)
119
- break;
120
-
121
- case "Bad_Input":
122
- res.status(400);
123
- res.send(response);
124
- break;
125
-
126
- case "Input_Not_Uuid" :
127
- res.status(400);
128
- res.send(response)
129
- break;
130
-
131
- case "File too large":
132
- res.status(413);
133
- res.send(response)
134
- break;
135
-
136
- case "Invalid time value":
137
- res.status(403);
138
- res.send(response)
139
- break;
140
-
141
- default:
142
- res.status(500);
143
- res.send(response)
144
- }
145
- }
146
-
147
- function throwAccountSuspendedError(params = {}) {
148
- const error = new Error("Account_Suspended");
149
- error.code = "ACCOUNT_SUSPENDED";
150
- error.userMessage = params.userMessage ?? null;
151
- error.details = params.details ?? null;
152
- throw error;
153
- }
154
-
155
- function throwAccountBlockedError(params = {}) {
156
- const error = new Error("Account_Blocked");
157
- error.code = "ACCOUNT_BLOCKED";
158
- error.userMessage = params.userMessage ?? null;
159
- error.details = params.details ?? null;
160
- throw error;
161
- }
162
-
163
- function throwAccountInactiveError(params = {}) {
164
- const error = new Error("Account_Inactive");
165
- error.code = "ACCOUNT_INACTIVE";
166
- error.userMessage = params.userMessage ?? null;
167
- error.details = params.details ?? null;
168
- throw error;
169
- }
170
-
171
- function throwValidationFailureError(params = {}) {
172
- const error = new Error("Validation_Failure");
173
- error.code = "VALIDATION_FAILURE";
174
- error.userMessage = params.userMessage ?? null;
175
- error.details = params.details ?? null;
176
- throw error;
177
- }
178
-
179
- function throwWrongCredentialsError(params = {}) {
180
- const error = new Error("Wrong_Credentials");
181
- error.code = "WRONG_CREDENTIALS";
182
- error.userMessage = params.userMessage ?? null;
183
- error.details = params.details ?? null;
184
- throw error;
185
- }
186
-
187
- function throwRecordExistError(params = {}) {
188
- const error = new Error("Record_Exist");
189
- error.code = "RECORD_EXIST";
190
- error.userMessage = params.userMessage ?? null;
191
- error.details = params.details ?? null;
192
- throw error;
193
- }
194
-
195
- function throwRecordNotFoundError(params = {}) {
196
- const error = new Error("Record_NotFound");
197
- error.code = "RECORD_NOT_FOUND";
198
- error.userMessage = params.userMessage ?? null;
199
- error.details = params.details ?? null;
200
- throw error;
201
- }
202
-
203
- function throwRecordNotSavedError(params = {}) {
204
- const error = new Error("Record_NotSaved");
205
- error.code = "RECORD_NOT_SAVED";
206
- error.userMessage = params.userMessage ?? null;
207
- error.details = params.details ?? null;
208
- throw error;
209
- }
210
-
211
- function throwLoginRequiredError(params = {}) {
212
- const error = new Error("Login_Required");
213
- error.code = "LOGIN_REQUIRED";
214
- error.userMessage = params.userMessage ?? null;
215
- error.details = params.details ?? null;
216
- throw error;
217
- }
218
-
219
- function throwUpdateFailedError(params = {}) {
220
- const error = new Error("Update_Failed");
221
- error.code = "UPDATE_FAILED";
222
- error.userMessage = params.userMessage ?? null;
223
- error.details = params.details ?? null;
224
- throw error;
225
- }
226
-
227
- function throwTransactionFailedError(params = {}) {
228
- const error = new Error("Transaction_Failed");
229
- error.code = "TRANSACTION_FAILED";
230
- error.userMessage = params.userMessage ?? null;
231
- error.details = params.details ?? null;
232
- throw error;
233
- }
234
-
235
- function throwUsedTokenError(params = {}) {
236
- const error = new Error("Used_Token");
237
- error.code = "USED_TOKEN";
238
- error.userMessage = params.userMessage ?? null;
239
- error.details = params.details ?? null;
240
- throw error;
241
- }
242
-
243
- function throwVisitorTokenError(params = {}) {
244
- const error = new Error("Bad_Visitor_Token");
245
- error.code = "BAD_VISITOR_TOKEN";
246
- error.userMessage = params.userMessage ?? null;
247
- error.details = params.details ?? null;
248
- throw error;
249
- }
250
-
251
- function throwFileFormatNotSupportedError(params = {}) {
252
- const error = new Error("File_Format_Not_Supported");
253
- error.code = "FILE_FORMAT_NOT_SUPPORTED";
254
- error.userMessage = params.userMessage ?? null;
255
- error.details = params.details ?? null;
256
- throw error;
257
- }
258
-
259
- function throwNotAuthorizedError(params = {}) {
260
- const error = new Error("Not_Authorized");
261
- error.code = "NOT_AUTHORIZED";
262
- error.userMessage = params.userMessage ?? null;
263
- error.details = params.details ?? null;
264
- throw error;
265
- }
266
-
267
- function throwBadInputError(params = {}) {
268
- const error = new Error("Bad_Input");
269
- error.code = "BAD_INPUT";
270
- error.userMessage = params.userMessage ?? null;
271
- error.details = params.details ?? null;
272
- throw error;
273
- }
274
-
275
- function throwInputNotUuidError(params = {}) {
276
- const error = new Error("Input_Not_Uuid");
277
- error.code = "INPUT_NOT_UUID";
278
- error.userMessage = params.userMessage ?? null;
279
- error.details = params.details ?? null;
280
- throw error;
281
- }
282
-
283
- function throwFileTooLargeError(params = {}) {
284
- const error = new Error("File too large");
285
- error.code = "FILE_TOO_LARGE";
286
- error.userMessage = params.userMessage ?? null;
287
- error.details = params.details ?? null;
288
- throw error;
289
- }
290
-
291
- function throwInvalidTimeValueError(params = {}) {
292
- const error = new Error("Invalid time value");
293
- error.code = "INVALID_TIME_VALUE";
294
- error.userMessage = params.userMessage ?? null;
295
- error.details = params.details ?? null;
296
- throw error;
297
- }
@@ -1,18 +0,0 @@
1
- // Create new object only with properties given in array.
2
- function extractObjectWithProperties( obj, arrayOfProperties ) {
3
- let returnObj = {};
4
-
5
- arrayOfProperties.forEach( nameOfProperty => {
6
- if ( obj?.[ nameOfProperty ] ) {
7
- returnObj[ nameOfProperty ] = obj?.[ nameOfProperty ];
8
- }
9
- } );
10
-
11
- return returnObj;
12
- }
13
-
14
-
15
-
16
- module.exports = {
17
- extractObjectWithProperties
18
- };