response-standardizer 1.0.0

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.
@@ -0,0 +1,15 @@
1
+ import { RestResponseFunctions, RestMiddlewareFunctions } from "./types.js";
2
+ declare global {
3
+ namespace Express {
4
+ interface Response {
5
+ json: (data: any) => Response;
6
+ send: (data?: any) => Response;
7
+ locals: {
8
+ requestId: string;
9
+ [key: string]: any;
10
+ };
11
+ }
12
+ }
13
+ }
14
+ export declare const RestResponse: RestResponseFunctions;
15
+ export declare const RestMiddleware: RestMiddlewareFunctions;
package/dist/index.js ADDED
@@ -0,0 +1,69 @@
1
+ import { generateRequestId, getTimestamp } from "./utils.js";
2
+ const success = (req, res, data, message = null) => {
3
+ res.status(200).json({ data, message });
4
+ };
5
+ const paginate = (req, res, data, pagination, message = null) => {
6
+ res.status(200).json({ data, message, pagination });
7
+ };
8
+ const created = (req, res, data, message = "Record created successfully") => {
9
+ res.status(201).json({ data, message });
10
+ };
11
+ const deleted = (req, res) => {
12
+ res.status(204).send();
13
+ };
14
+ const validationError = (req, res, errors, message = "Validation error") => {
15
+ res.status(400).json({ errors, message, data: null });
16
+ };
17
+ const exceptionError = (req, res, errors, message = "Internal server error") => {
18
+ res.status(500).json({
19
+ data: null,
20
+ message,
21
+ errors
22
+ });
23
+ };
24
+ const unauthorized = (req, res, message = "Unauthorized") => {
25
+ res.status(401).json({ message });
26
+ };
27
+ const accessDenied = (req, res, message = "Access denied") => {
28
+ res.status(403).json({ message });
29
+ };
30
+ export const RestResponse = {
31
+ success,
32
+ paginate,
33
+ created,
34
+ deleted,
35
+ validationError,
36
+ exceptionError,
37
+ unauthorized,
38
+ accessDenied
39
+ };
40
+ const responseHandlerMiddleware = (req, res, next) => {
41
+ const oldJson = res.json.bind(res);
42
+ const oldSend = res.send.bind(res);
43
+ res.json = function (data) {
44
+ const response = {
45
+ status: res.statusCode >= 200 && res.statusCode < 300 ? "success" : "error",
46
+ code: res.statusCode,
47
+ message: data?.message || res.locals.message || (res.statusCode < 300 ? "OK" : "Error"),
48
+ data: data?.data ?? null,
49
+ errors: data?.errors ?? null,
50
+ meta: {
51
+ requestId: res.locals.requestId || generateRequestId(),
52
+ timestamp: getTimestamp(),
53
+ pagination: data?.pagination ?? null
54
+ }
55
+ };
56
+ return oldJson(response);
57
+ };
58
+ res.send = function (data) {
59
+ if (res.statusCode === 204) {
60
+ return oldSend();
61
+ }
62
+ return oldSend(data);
63
+ };
64
+ res.locals.requestId = generateRequestId();
65
+ next();
66
+ };
67
+ export const RestMiddleware = {
68
+ responseHandlerMiddleware
69
+ };
@@ -0,0 +1,34 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ export interface StandardResponse<T = any> {
3
+ status: "success" | "error";
4
+ code: number;
5
+ message: string;
6
+ data: T | null;
7
+ errors: Record<string, string[]> | null;
8
+ meta: {
9
+ requestId: string;
10
+ timestamp: string;
11
+ pagination: PaginationMeta | null;
12
+ };
13
+ }
14
+ export interface PaginationMeta {
15
+ total: number;
16
+ page: number;
17
+ perPage: number;
18
+ pageCount: number;
19
+ }
20
+ export type ErrorFields = Record<string, string[]>;
21
+ export type Middleware = (req: Request, res: Response, next: NextFunction) => void;
22
+ export interface RestMiddlewareFunctions {
23
+ responseHandlerMiddleware: <T>(req: Request, res: Response, next: NextFunction) => void;
24
+ }
25
+ export interface RestResponseFunctions {
26
+ success: <T>(req: Request, res: Response, data: T, message?: string | null) => void;
27
+ paginate: <T>(req: Request, res: Response, data: T, pagination: PaginationMeta, message?: string | null) => void;
28
+ created: <T>(req: Request, res: Response, data: T, message?: string) => void;
29
+ deleted: (req: Request, res: Response) => void;
30
+ validationError: (req: Request, res: Response, errors: ErrorFields, message?: string) => void;
31
+ exceptionError: (req: Request, res: Response, errors: any, message?: string) => void;
32
+ unauthorized: (req: Request, res: Response, message?: string) => void;
33
+ accessDenied: (req: Request, res: Response, message?: string) => void;
34
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export declare const generateRequestId: () => string;
2
+ export declare const getTimestamp: () => string;
package/dist/utils.js ADDED
@@ -0,0 +1,7 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+ export const generateRequestId = () => {
3
+ return uuidv4();
4
+ };
5
+ export const getTimestamp = () => {
6
+ return new Date().toISOString();
7
+ };
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "response-standardizer",
3
+ "version": "1.0.0",
4
+ "description": "Express middleware to standardize API responses",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepare": "npm run build"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://gitlab.com/dezhnevesht-archive-software/response-standardizer.git"
14
+ },
15
+ "author": "Hamid Atyabi",
16
+ "license": "ISC",
17
+ "bugs": {
18
+ "url": "https://gitlab.com/dezhnevesht-archive-software/response-standardizer/issues"
19
+ },
20
+ "homepage": "https://gitlab.com/dezhnevesht-archive-software/response-standardizer#readme",
21
+
22
+ "peerDependencies": {
23
+ "express": "^4 || ^5"
24
+ },
25
+
26
+ "dependencies": {
27
+ "uuid": "^9.0.1"
28
+ },
29
+
30
+ "devDependencies": {
31
+ "@types/express": "^5.0.5",
32
+ "@types/uuid": "^10.0.0",
33
+ "ts-node-dev": "^2.0.0",
34
+ "typescript": "^5.9.3"
35
+ }
36
+ }
package/src/index.ts ADDED
@@ -0,0 +1,121 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ import { ErrorFields, PaginationMeta, StandardResponse, RestResponseFunctions, RestMiddlewareFunctions } from "./types.js";
3
+ import { generateRequestId, getTimestamp } from "./utils.js";
4
+ declare global {
5
+ namespace Express {
6
+ interface Response {
7
+ json: (data: any) => Response;
8
+ send: (data?: any) => Response;
9
+ locals: {
10
+ requestId: string;
11
+ [key: string]: any;
12
+ };
13
+ }
14
+ }
15
+ }
16
+
17
+ const success = <T>(req: Request, res: Response, data: T, message: string | null = null) => {
18
+ res.status(200).json({ data, message });
19
+ };
20
+
21
+ const paginate = <T>(
22
+ req: Request,
23
+ res: Response,
24
+ data: T,
25
+ pagination: PaginationMeta,
26
+ message: string | null = null
27
+ ) => {
28
+ res.status(200).json({ data, message, pagination });
29
+ };
30
+
31
+ const created = <T>(req: Request, res: Response, data: T, message = "Record created successfully") => {
32
+ res.status(201).json({ data, message });
33
+ };
34
+
35
+ const deleted = (req: Request, res: Response) => {
36
+ res.status(204).send();
37
+ };
38
+
39
+ const validationError = (
40
+ req: Request,
41
+ res: Response,
42
+ errors: ErrorFields,
43
+ message = "Validation error"
44
+ ) => {
45
+ res.status(400).json({ errors, message, data: null });
46
+ };
47
+
48
+ const exceptionError = (
49
+ req: Request,
50
+ res: Response,
51
+ errors: any,
52
+ message = "Internal server error"
53
+ ) => {
54
+ res.status(500).json({
55
+ data: null,
56
+ message,
57
+ errors
58
+ });
59
+ };
60
+
61
+ const unauthorized = (req: Request, res: Response, message = "Unauthorized") => {
62
+ res.status(401).json({ message });
63
+ };
64
+
65
+ const accessDenied = (req: Request, res: Response, message = "Access denied") => {
66
+ res.status(403).json({ message });
67
+ };
68
+
69
+ export const RestResponse: RestResponseFunctions = {
70
+ success,
71
+ paginate,
72
+ created,
73
+ deleted,
74
+ validationError,
75
+ exceptionError,
76
+ unauthorized,
77
+ accessDenied
78
+ };
79
+
80
+
81
+ const responseHandlerMiddleware = (
82
+ req: Request,
83
+ res: Response,
84
+ next: NextFunction
85
+ ) => {
86
+
87
+ const oldJson = res.json.bind(res);
88
+ const oldSend = res.send.bind(res);
89
+
90
+ res.json = function (data: any) {
91
+ const response: StandardResponse = {
92
+ status: res.statusCode >= 200 && res.statusCode < 300 ? "success" : "error",
93
+ code: res.statusCode,
94
+ message: data?.message || res.locals.message || (res.statusCode < 300 ? "OK" : "Error"),
95
+ data: data?.data ?? null,
96
+ errors: data?.errors ?? null,
97
+ meta: {
98
+ requestId: res.locals.requestId || generateRequestId(),
99
+ timestamp: getTimestamp(),
100
+ pagination: data?.pagination ?? null
101
+ }
102
+ };
103
+
104
+ return oldJson(response);
105
+ };
106
+
107
+ res.send = function (data: any) {
108
+ if (res.statusCode === 204) {
109
+ return oldSend();
110
+ }
111
+ return oldSend(data);
112
+ };
113
+
114
+ res.locals.requestId = generateRequestId();
115
+ next();
116
+ };
117
+
118
+
119
+ export const RestMiddleware: RestMiddlewareFunctions = {
120
+ responseHandlerMiddleware
121
+ }
package/src/types.ts ADDED
@@ -0,0 +1,55 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+
3
+ export interface StandardResponse<T = any> {
4
+ status: "success" | "error";
5
+ code: number;
6
+ message: string;
7
+ data: T | null;
8
+ errors: Record<string, string[]> | null;
9
+ meta: {
10
+ requestId: string;
11
+ timestamp: string;
12
+ pagination: PaginationMeta | null;
13
+ };
14
+ }
15
+
16
+ export interface PaginationMeta {
17
+ total: number;
18
+ page: number;
19
+ perPage: number;
20
+ pageCount: number;
21
+ }
22
+
23
+ export type ErrorFields = Record<string, string[]>;
24
+
25
+ export type Middleware = (req: Request, res: Response, next: NextFunction) => void;
26
+
27
+ export interface RestMiddlewareFunctions {
28
+ responseHandlerMiddleware: <T>(req: Request, res: Response, next: NextFunction) => void
29
+ }
30
+ export interface RestResponseFunctions {
31
+ success: <T>(req: Request, res: Response, data: T, message?: string | null) => void;
32
+ paginate: <T>(
33
+ req: Request,
34
+ res: Response,
35
+ data: T,
36
+ pagination: PaginationMeta,
37
+ message?: string | null
38
+ ) => void;
39
+ created: <T>(req: Request, res: Response, data: T, message?: string) => void;
40
+ deleted: (req: Request, res: Response) => void;
41
+ validationError: (
42
+ req: Request,
43
+ res: Response,
44
+ errors: ErrorFields,
45
+ message?: string
46
+ ) => void;
47
+ exceptionError: (
48
+ req: Request,
49
+ res: Response,
50
+ errors: any,
51
+ message?: string
52
+ ) => void;
53
+ unauthorized: (req: Request, res: Response, message?: string) => void;
54
+ accessDenied: (req: Request, res: Response, message?: string) => void;
55
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+
3
+ export const generateRequestId = () => {
4
+ return uuidv4();
5
+ }
6
+
7
+ export const getTimestamp = () => {
8
+ return new Date().toISOString();
9
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node",
6
+ "declaration": true,
7
+ "outDir": "dist",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["src/**/*.ts"]
13
+ }