@naman_deep_singh/http-response 3.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.
- package/README.md +189 -0
- package/dist/cjs/adapters/express/ExpressResponder.d.ts +18 -0
- package/dist/cjs/adapters/express/ExpressResponder.js +46 -0
- package/dist/cjs/constants/httpStatus.d.ts +54 -0
- package/dist/cjs/constants/httpStatus.js +34 -0
- package/dist/cjs/core/BaseResponder.d.ts +31 -0
- package/dist/cjs/core/BaseResponder.js +116 -0
- package/dist/cjs/core/config.d.ts +15 -0
- package/dist/cjs/core/config.js +15 -0
- package/dist/cjs/core/factory.d.ts +3 -0
- package/dist/cjs/core/factory.js +10 -0
- package/dist/cjs/core/types.d.ts +32 -0
- package/dist/cjs/core/types.js +2 -0
- package/dist/cjs/index.d.ts +8 -0
- package/dist/cjs/index.js +32 -0
- package/dist/cjs/legacy.d.ts +19 -0
- package/dist/cjs/legacy.js +23 -0
- package/dist/cjs/middleware/express/expressMiddleware.d.ts +3 -0
- package/dist/cjs/middleware/express/expressMiddleware.js +13 -0
- package/dist/esm/adapters/express/ExpressResponder.d.ts +18 -0
- package/dist/esm/adapters/express/ExpressResponder.js +42 -0
- package/dist/esm/constants/httpStatus.d.ts +54 -0
- package/dist/esm/constants/httpStatus.js +31 -0
- package/dist/esm/core/BaseResponder.d.ts +31 -0
- package/dist/esm/core/BaseResponder.js +112 -0
- package/dist/esm/core/config.d.ts +15 -0
- package/dist/esm/core/config.js +12 -0
- package/dist/esm/core/factory.d.ts +3 -0
- package/dist/esm/core/factory.js +6 -0
- package/dist/esm/core/types.d.ts +32 -0
- package/dist/esm/core/types.js +1 -0
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/legacy.d.ts +19 -0
- package/dist/esm/legacy.js +18 -0
- package/dist/esm/middleware/express/expressMiddleware.d.ts +3 -0
- package/dist/esm/middleware/express/expressMiddleware.js +9 -0
- package/dist/types/adapters/express/ExpressResponder.d.ts +18 -0
- package/dist/types/constants/httpStatus.d.ts +54 -0
- package/dist/types/core/BaseResponder.d.ts +31 -0
- package/dist/types/core/config.d.ts +15 -0
- package/dist/types/core/factory.d.ts +3 -0
- package/dist/types/core/types.d.ts +32 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/legacy.d.ts +19 -0
- package/dist/types/middleware/express/expressMiddleware.d.ts +3 -0
- package/package.json +53 -0
- package/src/adapters/express/ExpressResponder.ts +64 -0
- package/src/constants/httpStatus.ts +42 -0
- package/src/core/BaseResponder.ts +198 -0
- package/src/core/config.ts +29 -0
- package/src/core/factory.ts +11 -0
- package/src/core/types.ts +34 -0
- package/src/index.ts +13 -0
- package/src/middleware/express/expressMiddleware.ts +14 -0
- package/tsconfig.base.json +11 -0
- package/tsconfig.cjs.json +9 -0
- package/tsconfig.esm.json +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# @naman_deep_singh/http-response
|
|
2
|
+
|
|
3
|
+
**Version:** 3.0.0
|
|
4
|
+
|
|
5
|
+
A flexible, framework-agnostic **TypeScript response utility library** for building consistent, typed, and configurable API responses.
|
|
6
|
+
|
|
7
|
+
Designed with a clean **core + adapter** architecture.
|
|
8
|
+
First-class support for **Express.js**, pagination, and standardized HTTP status handling.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🚀 Features
|
|
13
|
+
|
|
14
|
+
| Feature | Status |
|
|
15
|
+
|---------------------------------------------|:------:|
|
|
16
|
+
| Fully typesafe response envelopes | ✅ |
|
|
17
|
+
| Framework-agnostic core | ✅ |
|
|
18
|
+
| Express.js responder adapter | ✅ |
|
|
19
|
+
| Express middleware injection | ✅ |
|
|
20
|
+
| Pagination helpers | ✅ |
|
|
21
|
+
| Centralized responder configuration | ✅ |
|
|
22
|
+
| HTTP status constants | ✅ |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 📦 Installation
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npm install @naman_deep_singh/http-response
|
|
30
|
+
|
|
31
|
+
📄 Response Envelope (Default Shape)
|
|
32
|
+
interface ResponseEnvelope<P = unknown, M = Record<string, unknown>> {
|
|
33
|
+
success: boolean
|
|
34
|
+
message?: string
|
|
35
|
+
data?: P
|
|
36
|
+
error: {
|
|
37
|
+
message: string
|
|
38
|
+
code?: string
|
|
39
|
+
details?: unknown
|
|
40
|
+
} | null
|
|
41
|
+
meta: M | null
|
|
42
|
+
}
|
|
43
|
+
🛠️ Usage
|
|
44
|
+
✔ Framework-Agnostic (No Express)
|
|
45
|
+
import { BaseResponder } from '@naman_deep_singh/http-response'
|
|
46
|
+
|
|
47
|
+
const responder = new BaseResponder()
|
|
48
|
+
|
|
49
|
+
const result = responder.ok({ user: 'John' }, 'Loaded')
|
|
50
|
+
|
|
51
|
+
// When no sender is attached, returns:
|
|
52
|
+
// { status: 200, body: ResponseEnvelope }
|
|
53
|
+
console.log(result)
|
|
54
|
+
🌐 Express Integration
|
|
55
|
+
1️⃣ Register Middleware
|
|
56
|
+
import express from 'express'
|
|
57
|
+
import { responderMiddleware } from '@naman_deep_singh/http-response'
|
|
58
|
+
|
|
59
|
+
const app = express()
|
|
60
|
+
|
|
61
|
+
app.use(responderMiddleware())
|
|
62
|
+
This injects a res.responder() factory into every request.
|
|
63
|
+
|
|
64
|
+
2️⃣ Controller Usage
|
|
65
|
+
app.get('/user', (req, res) => {
|
|
66
|
+
const responder = res.responder<{ id: number; name: string }>()
|
|
67
|
+
|
|
68
|
+
responder.okAndSend(
|
|
69
|
+
{ id: 1, name: 'John Doe' },
|
|
70
|
+
'User found'
|
|
71
|
+
)
|
|
72
|
+
})
|
|
73
|
+
✅ okAndSend() automatically:
|
|
74
|
+
|
|
75
|
+
sets HTTP status
|
|
76
|
+
|
|
77
|
+
sends JSON response
|
|
78
|
+
|
|
79
|
+
returns void for clean controller ergonomics
|
|
80
|
+
|
|
81
|
+
⚙️ Middleware Configuration
|
|
82
|
+
app.use(
|
|
83
|
+
responderMiddleware({
|
|
84
|
+
timestamp: true,
|
|
85
|
+
extra: { service: 'user-service' },
|
|
86
|
+
})
|
|
87
|
+
)
|
|
88
|
+
Example response:
|
|
89
|
+
|
|
90
|
+
{
|
|
91
|
+
"success": true,
|
|
92
|
+
"data": { ... },
|
|
93
|
+
"error": null,
|
|
94
|
+
"meta": {
|
|
95
|
+
"timestamp": "2025-11-22T12:00:00Z"
|
|
96
|
+
},
|
|
97
|
+
"service": "user-service"
|
|
98
|
+
}
|
|
99
|
+
🔢 Pagination Support
|
|
100
|
+
responder.paginateAndSend(
|
|
101
|
+
[{ id: 1 }],
|
|
102
|
+
1, // page
|
|
103
|
+
10, // limit
|
|
104
|
+
42, // total
|
|
105
|
+
'Loaded'
|
|
106
|
+
)
|
|
107
|
+
Pagination metadata is automatically calculated.
|
|
108
|
+
|
|
109
|
+
📚 Available Methods
|
|
110
|
+
✅ Success Responses
|
|
111
|
+
Method HTTP
|
|
112
|
+
ok() 200
|
|
113
|
+
created() 201
|
|
114
|
+
noContent() 204
|
|
115
|
+
paginate() 200
|
|
116
|
+
Each method has an Express variant:
|
|
117
|
+
|
|
118
|
+
okAndSend()
|
|
119
|
+
|
|
120
|
+
createdAndSend()
|
|
121
|
+
|
|
122
|
+
paginateAndSend()
|
|
123
|
+
|
|
124
|
+
❌ Error Responses
|
|
125
|
+
Method HTTP
|
|
126
|
+
badRequest() 400
|
|
127
|
+
unauthorized() 401
|
|
128
|
+
forbidden() 403
|
|
129
|
+
notFound() 404
|
|
130
|
+
conflict() 409
|
|
131
|
+
unprocessableEntity() 422
|
|
132
|
+
tooManyRequests() 429
|
|
133
|
+
serverError() 500
|
|
134
|
+
Each also has a *AndSend() variant.
|
|
135
|
+
|
|
136
|
+
🧩 HTTP Status Constants
|
|
137
|
+
import { HTTP_STATUS } from '@naman_deep_singh/http-response'
|
|
138
|
+
|
|
139
|
+
HTTP_STATUS.SUCCESS.OK // 200
|
|
140
|
+
HTTP_STATUS.CLIENT_ERROR.NOT_FOUND // 404
|
|
141
|
+
HTTP_STATUS.SERVER_ERROR.INTERNAL_SERVER_ERROR // 500
|
|
142
|
+
Categories
|
|
143
|
+
SUCCESS
|
|
144
|
+
|
|
145
|
+
REDIRECTION
|
|
146
|
+
|
|
147
|
+
CLIENT_ERROR
|
|
148
|
+
|
|
149
|
+
SERVER_ERROR
|
|
150
|
+
|
|
151
|
+
✔ Object.freeze() protected
|
|
152
|
+
✔ Fully literal-typed (as const)
|
|
153
|
+
✔ IDE autocomplete friendly
|
|
154
|
+
|
|
155
|
+
🧩 TypeScript: Express Response Augmentation (Recommended)
|
|
156
|
+
For full type safety, add this once in your project:
|
|
157
|
+
|
|
158
|
+
import type { ExpressResponder } from '@naman_deep_singh/http-response'
|
|
159
|
+
|
|
160
|
+
declare global {
|
|
161
|
+
namespace Express {
|
|
162
|
+
interface Response {
|
|
163
|
+
responder: <P = unknown>() => ExpressResponder<P>
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
⚠️ Do not use for new projects.
|
|
169
|
+
|
|
170
|
+
🔗 Integration with Other Packages
|
|
171
|
+
With @naman_deep_singh/server-utils
|
|
172
|
+
import { responderMiddleware } from '@naman_deep_singh/http-response'
|
|
173
|
+
|
|
174
|
+
server.app.use(responderMiddleware())
|
|
175
|
+
With @naman_deep_singh/errors-utils
|
|
176
|
+
import { expressErrorHandler } from '@naman_deep_singh/errors-utils'
|
|
177
|
+
|
|
178
|
+
server.app.use(expressErrorHandler)
|
|
179
|
+
Provides consistent error responses across services.
|
|
180
|
+
|
|
181
|
+
🔜 Roadmap
|
|
182
|
+
Fastify adapter
|
|
183
|
+
|
|
184
|
+
Hono adapter
|
|
185
|
+
|
|
186
|
+
Configurable envelope key mapping
|
|
187
|
+
|
|
188
|
+
📄 License
|
|
189
|
+
MIT © Naman Deep Singh
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Response } from 'express';
|
|
2
|
+
import { BaseResponder } from '../../core/BaseResponder';
|
|
3
|
+
import type { ResponderConfig } from '../../core/config';
|
|
4
|
+
export declare class ExpressResponder<P = unknown> extends BaseResponder<P> {
|
|
5
|
+
private readonly res;
|
|
6
|
+
constructor(cfg: Partial<ResponderConfig> | undefined, res: Response);
|
|
7
|
+
okAndSend(data?: P, message?: string): void;
|
|
8
|
+
createdAndSend(data?: P, message?: string): void;
|
|
9
|
+
badRequestAndSend(message?: string, error?: unknown): void;
|
|
10
|
+
unauthorizedAndSend(message?: string): void;
|
|
11
|
+
forbiddenAndSend(message?: string): void;
|
|
12
|
+
notFoundAndSend(message?: string): void;
|
|
13
|
+
conflictAndSend(message?: string): void;
|
|
14
|
+
unprocessableEntityAndSend(message?: string, error?: unknown): void;
|
|
15
|
+
tooManyRequestsAndSend(message?: string): void;
|
|
16
|
+
serverErrorAndSend(message?: string, error?: unknown): void;
|
|
17
|
+
paginateAndSend(data: P[], page: number, limit: number, total: number, message?: string): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExpressResponder = void 0;
|
|
4
|
+
const BaseResponder_1 = require("../../core/BaseResponder");
|
|
5
|
+
class ExpressResponder extends BaseResponder_1.BaseResponder {
|
|
6
|
+
constructor(cfg, res) {
|
|
7
|
+
// attach sender which calls res.status().json()
|
|
8
|
+
super(cfg, (status, body) => res.status(status).json(body));
|
|
9
|
+
this.res = res;
|
|
10
|
+
}
|
|
11
|
+
// convenience methods that return void for middleware/controller ergonomics
|
|
12
|
+
okAndSend(data, message) {
|
|
13
|
+
void this.ok(data, message);
|
|
14
|
+
}
|
|
15
|
+
createdAndSend(data, message) {
|
|
16
|
+
void this.created(data, message);
|
|
17
|
+
}
|
|
18
|
+
badRequestAndSend(message, error) {
|
|
19
|
+
void this.badRequest(message, error);
|
|
20
|
+
}
|
|
21
|
+
unauthorizedAndSend(message) {
|
|
22
|
+
void this.unauthorized(message);
|
|
23
|
+
}
|
|
24
|
+
forbiddenAndSend(message) {
|
|
25
|
+
void this.forbidden(message);
|
|
26
|
+
}
|
|
27
|
+
notFoundAndSend(message) {
|
|
28
|
+
void this.notFound(message);
|
|
29
|
+
}
|
|
30
|
+
conflictAndSend(message) {
|
|
31
|
+
void this.conflict(message);
|
|
32
|
+
}
|
|
33
|
+
unprocessableEntityAndSend(message, error) {
|
|
34
|
+
void this.unprocessableEntity(message, error);
|
|
35
|
+
}
|
|
36
|
+
tooManyRequestsAndSend(message) {
|
|
37
|
+
void this.tooManyRequests(message);
|
|
38
|
+
}
|
|
39
|
+
serverErrorAndSend(message, error) {
|
|
40
|
+
void this.serverError(message, error);
|
|
41
|
+
}
|
|
42
|
+
paginateAndSend(data, page, limit, total, message) {
|
|
43
|
+
void this.paginate(data, page, limit, total, message);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.ExpressResponder = ExpressResponder;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
declare const SUCCESS: Readonly<{
|
|
2
|
+
readonly OK: 200;
|
|
3
|
+
readonly CREATED: 201;
|
|
4
|
+
readonly ACCEPTED: 202;
|
|
5
|
+
readonly NO_CONTENT: 204;
|
|
6
|
+
}>;
|
|
7
|
+
declare const REDIRECTION: Readonly<{
|
|
8
|
+
readonly NOT_MODIFIED: 304;
|
|
9
|
+
}>;
|
|
10
|
+
declare const CLIENT_ERROR: Readonly<{
|
|
11
|
+
readonly BAD_REQUEST: 400;
|
|
12
|
+
readonly UNAUTHORIZED: 401;
|
|
13
|
+
readonly FORBIDDEN: 403;
|
|
14
|
+
readonly NOT_FOUND: 404;
|
|
15
|
+
readonly METHOD_NOT_ALLOWED: 405;
|
|
16
|
+
readonly CONFLICT: 409;
|
|
17
|
+
readonly UNPROCESSABLE_ENTITY: 422;
|
|
18
|
+
readonly TOO_MANY_REQUESTS: 429;
|
|
19
|
+
}>;
|
|
20
|
+
declare const SERVER_ERROR: Readonly<{
|
|
21
|
+
readonly INTERNAL_SERVER_ERROR: 500;
|
|
22
|
+
readonly NOT_IMPLEMENTED: 501;
|
|
23
|
+
readonly BAD_GATEWAY: 502;
|
|
24
|
+
readonly SERVICE_UNAVAILABLE: 503;
|
|
25
|
+
}>;
|
|
26
|
+
export declare const HTTP_STATUS: Readonly<{
|
|
27
|
+
readonly SUCCESS: Readonly<{
|
|
28
|
+
readonly OK: 200;
|
|
29
|
+
readonly CREATED: 201;
|
|
30
|
+
readonly ACCEPTED: 202;
|
|
31
|
+
readonly NO_CONTENT: 204;
|
|
32
|
+
}>;
|
|
33
|
+
readonly REDIRECTION: Readonly<{
|
|
34
|
+
readonly NOT_MODIFIED: 304;
|
|
35
|
+
}>;
|
|
36
|
+
readonly CLIENT_ERROR: Readonly<{
|
|
37
|
+
readonly BAD_REQUEST: 400;
|
|
38
|
+
readonly UNAUTHORIZED: 401;
|
|
39
|
+
readonly FORBIDDEN: 403;
|
|
40
|
+
readonly NOT_FOUND: 404;
|
|
41
|
+
readonly METHOD_NOT_ALLOWED: 405;
|
|
42
|
+
readonly CONFLICT: 409;
|
|
43
|
+
readonly UNPROCESSABLE_ENTITY: 422;
|
|
44
|
+
readonly TOO_MANY_REQUESTS: 429;
|
|
45
|
+
}>;
|
|
46
|
+
readonly SERVER_ERROR: Readonly<{
|
|
47
|
+
readonly INTERNAL_SERVER_ERROR: 500;
|
|
48
|
+
readonly NOT_IMPLEMENTED: 501;
|
|
49
|
+
readonly BAD_GATEWAY: 502;
|
|
50
|
+
readonly SERVICE_UNAVAILABLE: 503;
|
|
51
|
+
}>;
|
|
52
|
+
}>;
|
|
53
|
+
export type HttpStatusCode = (typeof SUCCESS)[keyof typeof SUCCESS] | (typeof REDIRECTION)[keyof typeof REDIRECTION] | (typeof CLIENT_ERROR)[keyof typeof CLIENT_ERROR] | (typeof SERVER_ERROR)[keyof typeof SERVER_ERROR];
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HTTP_STATUS = void 0;
|
|
4
|
+
const SUCCESS = Object.freeze({
|
|
5
|
+
OK: 200,
|
|
6
|
+
CREATED: 201,
|
|
7
|
+
ACCEPTED: 202,
|
|
8
|
+
NO_CONTENT: 204,
|
|
9
|
+
});
|
|
10
|
+
const REDIRECTION = Object.freeze({
|
|
11
|
+
NOT_MODIFIED: 304,
|
|
12
|
+
});
|
|
13
|
+
const CLIENT_ERROR = Object.freeze({
|
|
14
|
+
BAD_REQUEST: 400,
|
|
15
|
+
UNAUTHORIZED: 401,
|
|
16
|
+
FORBIDDEN: 403,
|
|
17
|
+
NOT_FOUND: 404,
|
|
18
|
+
METHOD_NOT_ALLOWED: 405,
|
|
19
|
+
CONFLICT: 409,
|
|
20
|
+
UNPROCESSABLE_ENTITY: 422,
|
|
21
|
+
TOO_MANY_REQUESTS: 429,
|
|
22
|
+
});
|
|
23
|
+
const SERVER_ERROR = Object.freeze({
|
|
24
|
+
INTERNAL_SERVER_ERROR: 500,
|
|
25
|
+
NOT_IMPLEMENTED: 501,
|
|
26
|
+
BAD_GATEWAY: 502,
|
|
27
|
+
SERVICE_UNAVAILABLE: 503,
|
|
28
|
+
});
|
|
29
|
+
exports.HTTP_STATUS = Object.freeze({
|
|
30
|
+
SUCCESS,
|
|
31
|
+
REDIRECTION,
|
|
32
|
+
CLIENT_ERROR,
|
|
33
|
+
SERVER_ERROR,
|
|
34
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ResponderConfig } from './config';
|
|
2
|
+
import type { PaginationMeta, ResponseEnvelope, Sender } from './types';
|
|
3
|
+
export declare class BaseResponder<P = unknown, M = PaginationMeta> {
|
|
4
|
+
protected readonly cfg: ResponderConfig;
|
|
5
|
+
protected sender?: Sender;
|
|
6
|
+
constructor(cfg?: Partial<ResponderConfig>, sender?: Sender);
|
|
7
|
+
attachSender(sender: Sender): void;
|
|
8
|
+
protected normalizeError(err: unknown): {
|
|
9
|
+
message: string;
|
|
10
|
+
code?: string;
|
|
11
|
+
details?: unknown;
|
|
12
|
+
};
|
|
13
|
+
protected buildEnvelope(data?: P, message?: string, error?: unknown, meta?: M): ResponseEnvelope<P, M>;
|
|
14
|
+
protected send(status: number, envelope: ResponseEnvelope<P, M>): any;
|
|
15
|
+
/** -----------------------------
|
|
16
|
+
* Standard REST Response Helpers
|
|
17
|
+
* ----------------------------- */
|
|
18
|
+
ok(data?: P, message?: string): any;
|
|
19
|
+
created(data?: P, message?: string): any;
|
|
20
|
+
noContent(message?: string): any;
|
|
21
|
+
badRequest(message?: string, error?: unknown): any;
|
|
22
|
+
unauthorized(message?: string): any;
|
|
23
|
+
forbidden(message?: string): any;
|
|
24
|
+
notFound(message?: string): any;
|
|
25
|
+
conflict(message?: string): any;
|
|
26
|
+
unprocessableEntity(message?: string, error?: unknown): any;
|
|
27
|
+
tooManyRequests(message?: string): any;
|
|
28
|
+
serverError(message?: string, error?: unknown): any;
|
|
29
|
+
paginate(data: P[], page: number, limit: number, total: number, message?: string): any;
|
|
30
|
+
paginateOffset(data: P[], offset: number, limit: number, total: number, message?: string): any;
|
|
31
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseResponder = void 0;
|
|
4
|
+
const httpStatus_1 = require("../constants/httpStatus");
|
|
5
|
+
const config_1 = require("./config");
|
|
6
|
+
class BaseResponder {
|
|
7
|
+
constructor(cfg, sender) {
|
|
8
|
+
this.cfg = { ...config_1.defaultConfig, ...(cfg ?? {}) };
|
|
9
|
+
this.sender = sender;
|
|
10
|
+
}
|
|
11
|
+
attachSender(sender) {
|
|
12
|
+
this.sender = sender;
|
|
13
|
+
}
|
|
14
|
+
normalizeError(err) {
|
|
15
|
+
// errors-utils AppError compatibility
|
|
16
|
+
if (typeof err === 'object' && err !== null) {
|
|
17
|
+
const e = err;
|
|
18
|
+
if (typeof e.message === 'string') {
|
|
19
|
+
return {
|
|
20
|
+
message: e.message,
|
|
21
|
+
code: typeof e.code === 'string' ? e.code : undefined,
|
|
22
|
+
details: e.details,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (err instanceof Error) {
|
|
27
|
+
return { message: err.message };
|
|
28
|
+
}
|
|
29
|
+
if (typeof err === 'string') {
|
|
30
|
+
return { message: err };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
message: 'Internal server error',
|
|
34
|
+
details: err,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
buildEnvelope(data, message, error, meta) {
|
|
38
|
+
const env = {
|
|
39
|
+
success: !error,
|
|
40
|
+
message: message ?? (error ? 'Error' : undefined),
|
|
41
|
+
data: error ? undefined : data,
|
|
42
|
+
error: error ? this.normalizeError(error) : null,
|
|
43
|
+
meta: meta ?? null,
|
|
44
|
+
};
|
|
45
|
+
if (this.cfg.timestamp) {
|
|
46
|
+
env.meta = {
|
|
47
|
+
...(env.meta ?? {}),
|
|
48
|
+
timestamp: new Date().toISOString(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (this.cfg.extra) {
|
|
52
|
+
Object.assign(env, this.cfg.extra);
|
|
53
|
+
}
|
|
54
|
+
return env;
|
|
55
|
+
}
|
|
56
|
+
send(status, envelope) {
|
|
57
|
+
if (!this.sender)
|
|
58
|
+
return { status, body: envelope };
|
|
59
|
+
return this.sender(status, envelope);
|
|
60
|
+
}
|
|
61
|
+
/** -----------------------------
|
|
62
|
+
* Standard REST Response Helpers
|
|
63
|
+
* ----------------------------- */
|
|
64
|
+
ok(data, message = 'Success') {
|
|
65
|
+
return this.send(httpStatus_1.HTTP_STATUS.SUCCESS.OK, this.buildEnvelope(data, message));
|
|
66
|
+
}
|
|
67
|
+
created(data, message = 'Created successfully') {
|
|
68
|
+
return this.send(httpStatus_1.HTTP_STATUS.SUCCESS.CREATED, this.buildEnvelope(data, message));
|
|
69
|
+
}
|
|
70
|
+
noContent(message = 'No Content') {
|
|
71
|
+
return this.send(httpStatus_1.HTTP_STATUS.SUCCESS.NO_CONTENT, this.buildEnvelope(undefined, message));
|
|
72
|
+
}
|
|
73
|
+
badRequest(message = 'Bad request', error) {
|
|
74
|
+
return this.send(httpStatus_1.HTTP_STATUS.CLIENT_ERROR.BAD_REQUEST, this.buildEnvelope(undefined, message, error));
|
|
75
|
+
}
|
|
76
|
+
unauthorized(message = 'Unauthorized') {
|
|
77
|
+
return this.send(httpStatus_1.HTTP_STATUS.CLIENT_ERROR.UNAUTHORIZED, this.buildEnvelope(undefined, message));
|
|
78
|
+
}
|
|
79
|
+
forbidden(message = 'Forbidden') {
|
|
80
|
+
return this.send(httpStatus_1.HTTP_STATUS.CLIENT_ERROR.FORBIDDEN, this.buildEnvelope(undefined, message));
|
|
81
|
+
}
|
|
82
|
+
notFound(message = 'Not found') {
|
|
83
|
+
return this.send(httpStatus_1.HTTP_STATUS.CLIENT_ERROR.NOT_FOUND, this.buildEnvelope(undefined, message));
|
|
84
|
+
}
|
|
85
|
+
conflict(message = 'Conflict') {
|
|
86
|
+
return this.send(httpStatus_1.HTTP_STATUS.CLIENT_ERROR.CONFLICT, this.buildEnvelope(undefined, message));
|
|
87
|
+
}
|
|
88
|
+
unprocessableEntity(message = 'Unprocessable Entity', error) {
|
|
89
|
+
return this.send(httpStatus_1.HTTP_STATUS.CLIENT_ERROR.UNPROCESSABLE_ENTITY, this.buildEnvelope(undefined, message, error));
|
|
90
|
+
}
|
|
91
|
+
tooManyRequests(message = 'Too Many Requests') {
|
|
92
|
+
return this.send(httpStatus_1.HTTP_STATUS.CLIENT_ERROR.TOO_MANY_REQUESTS, this.buildEnvelope(undefined, message));
|
|
93
|
+
}
|
|
94
|
+
serverError(message = 'Internal server error', error) {
|
|
95
|
+
return this.send(httpStatus_1.HTTP_STATUS.SERVER_ERROR.INTERNAL_SERVER_ERROR, this.buildEnvelope(undefined, message, error));
|
|
96
|
+
}
|
|
97
|
+
paginate(data, page, limit, total, message = 'Success') {
|
|
98
|
+
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
99
|
+
const offset = (page - 1) * limit;
|
|
100
|
+
const pagination = {
|
|
101
|
+
page,
|
|
102
|
+
limit,
|
|
103
|
+
total,
|
|
104
|
+
totalPages,
|
|
105
|
+
offset,
|
|
106
|
+
hasNext: page < totalPages,
|
|
107
|
+
hasPrev: page > 1,
|
|
108
|
+
};
|
|
109
|
+
return this.send(httpStatus_1.HTTP_STATUS.SUCCESS.OK, this.buildEnvelope(data, message, undefined, pagination));
|
|
110
|
+
}
|
|
111
|
+
paginateOffset(data, offset, limit, total, message = 'Success') {
|
|
112
|
+
const page = Math.floor(offset / limit) + 1;
|
|
113
|
+
return this.paginate(data, page, limit, total, message);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.BaseResponder = BaseResponder;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PlainObject } from './types';
|
|
2
|
+
export type EnvelopeKeys = {
|
|
3
|
+
success?: string;
|
|
4
|
+
message?: string;
|
|
5
|
+
data?: string;
|
|
6
|
+
error?: string;
|
|
7
|
+
meta?: string;
|
|
8
|
+
};
|
|
9
|
+
export type ResponderConfig = {
|
|
10
|
+
envelopeKeys?: EnvelopeKeys;
|
|
11
|
+
defaultStatus?: number;
|
|
12
|
+
timestamp?: boolean;
|
|
13
|
+
extra?: PlainObject | null;
|
|
14
|
+
};
|
|
15
|
+
export declare const defaultConfig: ResponderConfig;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultConfig = void 0;
|
|
4
|
+
exports.defaultConfig = {
|
|
5
|
+
envelopeKeys: {
|
|
6
|
+
success: 'success',
|
|
7
|
+
message: 'message',
|
|
8
|
+
data: 'data',
|
|
9
|
+
error: 'error',
|
|
10
|
+
meta: 'meta',
|
|
11
|
+
},
|
|
12
|
+
defaultStatus: 200,
|
|
13
|
+
timestamp: false,
|
|
14
|
+
extra: null,
|
|
15
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ExpressResponder } from '../adapters/express/ExpressResponder';
|
|
2
|
+
import type { ResponderConfig } from './config';
|
|
3
|
+
export declare const createResponderFactory: (cfg?: Partial<ResponderConfig>) => <P = unknown, _M = Record<string, unknown>>(res: import("express").Response) => ExpressResponder<P>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createResponderFactory = void 0;
|
|
4
|
+
const ExpressResponder_1 = require("../adapters/express/ExpressResponder");
|
|
5
|
+
const createResponderFactory = (cfg) => {
|
|
6
|
+
return (res) => {
|
|
7
|
+
return new ExpressResponder_1.ExpressResponder(cfg, res);
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
exports.createResponderFactory = createResponderFactory;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type PlainObject = Record<string, unknown>;
|
|
2
|
+
export type ErrorShape = {
|
|
3
|
+
code?: string;
|
|
4
|
+
message: string;
|
|
5
|
+
details?: unknown;
|
|
6
|
+
};
|
|
7
|
+
export type ResponseEnvelope<T = unknown, M = PlainObject> = {
|
|
8
|
+
success: boolean;
|
|
9
|
+
message?: string;
|
|
10
|
+
data?: T;
|
|
11
|
+
error?: ErrorShape | null;
|
|
12
|
+
meta?: M | null;
|
|
13
|
+
};
|
|
14
|
+
export type TransportResult = {
|
|
15
|
+
status: number;
|
|
16
|
+
body: unknown;
|
|
17
|
+
};
|
|
18
|
+
export type Sender = (status: number, body: unknown) => Promise<any> | any;
|
|
19
|
+
export interface PaginationMeta {
|
|
20
|
+
page: number;
|
|
21
|
+
limit: number;
|
|
22
|
+
total: number;
|
|
23
|
+
totalPages: number;
|
|
24
|
+
hasNext?: boolean;
|
|
25
|
+
hasPrev?: boolean;
|
|
26
|
+
offset?: number;
|
|
27
|
+
links?: {
|
|
28
|
+
next?: string;
|
|
29
|
+
prev?: string;
|
|
30
|
+
self: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { BaseResponder } from './core/BaseResponder';
|
|
2
|
+
export { createResponderFactory } from './core/factory';
|
|
3
|
+
export * from './core/types';
|
|
4
|
+
export * from './core/config';
|
|
5
|
+
export { ExpressResponder } from './adapters/express/ExpressResponder';
|
|
6
|
+
export { responderMiddleware } from './middleware/express/expressMiddleware';
|
|
7
|
+
export { HTTP_STATUS } from './constants/httpStatus';
|
|
8
|
+
export type { HttpStatusCode } from './constants/httpStatus';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.HTTP_STATUS = exports.responderMiddleware = exports.ExpressResponder = exports.createResponderFactory = exports.BaseResponder = void 0;
|
|
18
|
+
// Core Responders
|
|
19
|
+
var BaseResponder_1 = require("./core/BaseResponder");
|
|
20
|
+
Object.defineProperty(exports, "BaseResponder", { enumerable: true, get: function () { return BaseResponder_1.BaseResponder; } });
|
|
21
|
+
var factory_1 = require("./core/factory");
|
|
22
|
+
Object.defineProperty(exports, "createResponderFactory", { enumerable: true, get: function () { return factory_1.createResponderFactory; } });
|
|
23
|
+
__exportStar(require("./core/types"), exports);
|
|
24
|
+
__exportStar(require("./core/config"), exports);
|
|
25
|
+
// Adapters (Express)
|
|
26
|
+
var ExpressResponder_1 = require("./adapters/express/ExpressResponder");
|
|
27
|
+
Object.defineProperty(exports, "ExpressResponder", { enumerable: true, get: function () { return ExpressResponder_1.ExpressResponder; } });
|
|
28
|
+
var expressMiddleware_1 = require("./middleware/express/expressMiddleware");
|
|
29
|
+
Object.defineProperty(exports, "responderMiddleware", { enumerable: true, get: function () { return expressMiddleware_1.responderMiddleware; } });
|
|
30
|
+
// HTTP Status Constants
|
|
31
|
+
var httpStatus_1 = require("./constants/httpStatus");
|
|
32
|
+
Object.defineProperty(exports, "HTTP_STATUS", { enumerable: true, get: function () { return httpStatus_1.HTTP_STATUS; } });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const success: <T>(data: T, message?: string, status?: number, res?: {
|
|
2
|
+
status: (code: number) => any;
|
|
3
|
+
json: (body: unknown) => any;
|
|
4
|
+
}) => any;
|
|
5
|
+
export declare const error: (message: string, status?: number, err?: string, res?: {
|
|
6
|
+
status: (code: number) => any;
|
|
7
|
+
json: (body: unknown) => any;
|
|
8
|
+
}) => any;
|
|
9
|
+
declare const _default: {
|
|
10
|
+
success: <T>(data: T, message?: string, status?: number, res?: {
|
|
11
|
+
status: (code: number) => any;
|
|
12
|
+
json: (body: unknown) => any;
|
|
13
|
+
}) => any;
|
|
14
|
+
error: (message: string, status?: number, err?: string, res?: {
|
|
15
|
+
status: (code: number) => any;
|
|
16
|
+
json: (body: unknown) => any;
|
|
17
|
+
}) => any;
|
|
18
|
+
};
|
|
19
|
+
export default _default;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.error = exports.success = void 0;
|
|
4
|
+
const success = (data, message = 'Success', status = 200, res) => {
|
|
5
|
+
if (res)
|
|
6
|
+
return res
|
|
7
|
+
.status(status)
|
|
8
|
+
.json({ success: true, message, data, statusCode: status });
|
|
9
|
+
return { success: true, message, data, statusCode: status };
|
|
10
|
+
};
|
|
11
|
+
exports.success = success;
|
|
12
|
+
const error = (message, status = 500, err, res) => {
|
|
13
|
+
if (res)
|
|
14
|
+
return res
|
|
15
|
+
.status(status)
|
|
16
|
+
.json({ success: false, message, error: err, statusCode: status });
|
|
17
|
+
return { success: false, message, error: err, statusCode: status };
|
|
18
|
+
};
|
|
19
|
+
exports.error = error;
|
|
20
|
+
exports.default = {
|
|
21
|
+
success: exports.success,
|
|
22
|
+
error: exports.error,
|
|
23
|
+
};
|