node-responder 1.0.0 → 1.2.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 +437 -82
- package/package.json +2 -2
- package/src/index.js +235 -93
- package/types/index.d.ts +59 -27
package/README.md
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
> Modern, standardized API response middleware for Express.js
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/node-responder)
|
|
6
|
+
[](https://www.npmjs.com/package/node-responder)
|
|
6
7
|
[](LICENSE)
|
|
7
8
|
[](types/index.d.ts)
|
|
8
9
|
[]()
|
|
9
10
|
|
|
10
|
-
Stop writing repetitive `res.status(200).json({ success: true, data: ... })` in every route.
|
|
11
|
+
Stop writing repetitive `res.status(200).json({ success: true, data: ... })` in every route.
|
|
12
|
+
`node-responder` adds clean, consistent response helpers directly to Express's `res` object.
|
|
11
13
|
|
|
12
14
|
---
|
|
13
15
|
|
|
@@ -16,9 +18,10 @@ Stop writing repetitive `res.status(200).json({ success: true, data: ... })` in
|
|
|
16
18
|
- ✅ **Zero dependencies** — only Express as a peer dependency
|
|
17
19
|
- ✅ **TypeScript support** — full type definitions included
|
|
18
20
|
- ✅ **ESM + CommonJS** — works with both `require` and `import`
|
|
21
|
+
- ✅ **Request Logger** — logs method, URL, status, and response time to terminal
|
|
19
22
|
- ✅ **Pagination built-in** — `res.paginate()` with full meta
|
|
23
|
+
- ✅ **asyncHandler** — write async routes without try-catch boilerplate
|
|
20
24
|
- ✅ **Consistent format** — every response follows the same structure
|
|
21
|
-
- ✅ **Shorthand methods** — `res.ok()`, `res.notFound()`, `res.unauthorized()` and more
|
|
22
25
|
- ✅ **Node.js 14+** supported
|
|
23
26
|
|
|
24
27
|
---
|
|
@@ -38,15 +41,14 @@ const express = require("express");
|
|
|
38
41
|
const responder = require("node-responder");
|
|
39
42
|
|
|
40
43
|
const app = express();
|
|
44
|
+
app.use(express.json());
|
|
41
45
|
|
|
42
46
|
// Apply middleware globally
|
|
43
47
|
app.use(responder());
|
|
44
48
|
|
|
45
49
|
app.get("/user/:id", async (req, res) => {
|
|
46
50
|
const user = await User.findById(req.params.id);
|
|
47
|
-
|
|
48
51
|
if (!user) return res.notFound("User not found");
|
|
49
|
-
|
|
50
52
|
return res.ok(user);
|
|
51
53
|
});
|
|
52
54
|
|
|
@@ -57,23 +59,23 @@ app.listen(3000);
|
|
|
57
59
|
|
|
58
60
|
## 📋 Response Format
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
Every response follows the same consistent structure:
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
### Success Response
|
|
63
65
|
|
|
64
66
|
```json
|
|
65
67
|
{
|
|
66
68
|
"success": true,
|
|
67
69
|
"message": "Success",
|
|
68
|
-
"data": {
|
|
70
|
+
"data": { "id": 1, "name": "John" },
|
|
69
71
|
"meta": {
|
|
70
|
-
"timestamp": "
|
|
72
|
+
"timestamp": "2025-01-15T10:30:00.000Z",
|
|
71
73
|
"statusCode": 200
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
```
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
### Error Response
|
|
77
79
|
|
|
78
80
|
```json
|
|
79
81
|
{
|
|
@@ -82,21 +84,21 @@ All responses follow this consistent structure:
|
|
|
82
84
|
"data": null,
|
|
83
85
|
"errors": null,
|
|
84
86
|
"meta": {
|
|
85
|
-
"timestamp": "
|
|
87
|
+
"timestamp": "2025-01-15T10:30:00.000Z",
|
|
86
88
|
"statusCode": 404
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
```
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
### Paginated Response
|
|
92
94
|
|
|
93
95
|
```json
|
|
94
96
|
{
|
|
95
97
|
"success": true,
|
|
96
98
|
"message": "Users fetched",
|
|
97
|
-
"data": [
|
|
99
|
+
"data": [{ "id": 1 }, { "id": 2 }],
|
|
98
100
|
"meta": {
|
|
99
|
-
"timestamp": "
|
|
101
|
+
"timestamp": "2025-01-15T10:30:00.000Z",
|
|
100
102
|
"statusCode": 200,
|
|
101
103
|
"pagination": {
|
|
102
104
|
"page": 1,
|
|
@@ -112,121 +114,447 @@ All responses follow this consistent structure:
|
|
|
112
114
|
|
|
113
115
|
---
|
|
114
116
|
|
|
115
|
-
##
|
|
116
|
-
|
|
117
|
-
### Middleware Setup
|
|
117
|
+
## ⚙️ Middleware Setup
|
|
118
118
|
|
|
119
119
|
```js
|
|
120
120
|
const responder = require("node-responder");
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
// Apply to all routes
|
|
123
|
+
app.use(responder());
|
|
124
|
+
|
|
125
|
+
// With logger enabled
|
|
126
|
+
app.use(responder({ logger: true }));
|
|
127
|
+
|
|
128
|
+
// Apply to a specific router only
|
|
129
|
+
router.use(responder());
|
|
125
130
|
```
|
|
126
131
|
|
|
132
|
+
### Options
|
|
133
|
+
|
|
134
|
+
| Option | Type | Default | Description |
|
|
135
|
+
| -------- | --------- | ------- | ------------------------------------------------------------------------- |
|
|
136
|
+
| `logger` | `boolean` | `false` | Logs each request to terminal with method, URL, status, and response time |
|
|
137
|
+
|
|
127
138
|
---
|
|
128
139
|
|
|
140
|
+
## 📖 API Reference
|
|
141
|
+
|
|
129
142
|
### ✅ Success Methods
|
|
130
143
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
#### `res.ok(data?, message?)`
|
|
147
|
+
|
|
148
|
+
**Status: 200** — Use for standard successful GET requests.
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
// With data only
|
|
152
|
+
res.ok({ id: 1, name: "John" });
|
|
153
|
+
|
|
154
|
+
// With data and custom message
|
|
155
|
+
res.ok({ id: 1, name: "John" }, "User fetched successfully");
|
|
156
|
+
|
|
157
|
+
// Response:
|
|
158
|
+
// { success: true, message: "User fetched successfully", data: { id: 1, name: "John" }, meta: { statusCode: 200, ... } }
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
#### `res.created(data?, message?)`
|
|
164
|
+
|
|
165
|
+
**Status: 201** — Use when a new resource has been successfully created.
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
const user = await User.create({ name: "John", email: "john@example.com" });
|
|
169
|
+
|
|
170
|
+
res.created(user);
|
|
171
|
+
|
|
172
|
+
// With custom message
|
|
173
|
+
res.created(user, "Account created successfully");
|
|
174
|
+
|
|
175
|
+
// Response:
|
|
176
|
+
// { success: true, message: "Created successfully", data: { ...user }, meta: { statusCode: 201, ... } }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
#### `res.noContent()`
|
|
182
|
+
|
|
183
|
+
**Status: 204** — Use after a successful delete or update when no data needs to be returned.
|
|
137
184
|
|
|
138
185
|
```js
|
|
139
|
-
|
|
140
|
-
res.
|
|
141
|
-
|
|
186
|
+
await User.findByIdAndDelete(req.params.id);
|
|
187
|
+
res.noContent();
|
|
188
|
+
|
|
189
|
+
// Response: empty body (HTTP 204 No Content)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
#### `res.success(data?, message?, statusCode?)`
|
|
195
|
+
|
|
196
|
+
**Status: custom** — Use when you need a custom success status code.
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
res.success({ accepted: true }, "Request accepted", 202);
|
|
200
|
+
|
|
201
|
+
// Response:
|
|
202
|
+
// { success: true, message: "Request accepted", data: { accepted: true }, meta: { statusCode: 202, ... } }
|
|
142
203
|
```
|
|
143
204
|
|
|
144
205
|
---
|
|
145
206
|
|
|
146
207
|
### ❌ Error Methods
|
|
147
208
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
#### `res.badRequest(message?, errors?)`
|
|
212
|
+
|
|
213
|
+
**Status: 400** — Use when the request is malformed or validation fails.
|
|
214
|
+
|
|
215
|
+
```js
|
|
216
|
+
// Simple message
|
|
217
|
+
res.badRequest("Name is required");
|
|
218
|
+
|
|
219
|
+
// With validation errors object
|
|
220
|
+
res.badRequest("Validation failed", {
|
|
221
|
+
name: "Name is required",
|
|
222
|
+
email: "Invalid email format",
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Response:
|
|
226
|
+
// { success: false, message: "Validation failed", data: null, errors: { name: "...", email: "..." }, meta: { statusCode: 400, ... } }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
#### `res.unauthorized(message?)`
|
|
232
|
+
|
|
233
|
+
**Status: 401** — Use when the user is not authenticated (no token or invalid token).
|
|
234
|
+
|
|
235
|
+
```js
|
|
236
|
+
// Default message
|
|
237
|
+
res.unauthorized();
|
|
238
|
+
|
|
239
|
+
// Custom message
|
|
240
|
+
res.unauthorized("Please login to continue");
|
|
241
|
+
|
|
242
|
+
// Response:
|
|
243
|
+
// { success: false, message: "Unauthorized", data: null, errors: null, meta: { statusCode: 401, ... } }
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
#### `res.forbidden(message?)`
|
|
249
|
+
|
|
250
|
+
**Status: 403** — Use when the user is authenticated but does not have permission.
|
|
251
|
+
|
|
252
|
+
```js
|
|
253
|
+
// Default message
|
|
254
|
+
res.forbidden();
|
|
255
|
+
|
|
256
|
+
// Custom message
|
|
257
|
+
res.forbidden("You do not have permission to access this resource");
|
|
258
|
+
|
|
259
|
+
// Response:
|
|
260
|
+
// { success: false, message: "Forbidden", data: null, errors: null, meta: { statusCode: 403, ... } }
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
#### `res.notFound(message?)`
|
|
266
|
+
|
|
267
|
+
**Status: 404** — Use when a requested resource does not exist.
|
|
268
|
+
|
|
269
|
+
```js
|
|
270
|
+
const user = await User.findById(req.params.id);
|
|
271
|
+
|
|
272
|
+
if (!user) {
|
|
273
|
+
return res.notFound("User not found");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Response:
|
|
277
|
+
// { success: false, message: "User not found", data: null, errors: null, meta: { statusCode: 404, ... } }
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
#### `res.conflict(message?)`
|
|
283
|
+
|
|
284
|
+
**Status: 409** — Use when the request conflicts with existing data (e.g. duplicate email).
|
|
285
|
+
|
|
286
|
+
```js
|
|
287
|
+
const exists = await User.findOne({ email: req.body.email });
|
|
288
|
+
|
|
289
|
+
if (exists) {
|
|
290
|
+
return res.conflict("Email already registered");
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Response:
|
|
294
|
+
// { success: false, message: "Email already registered", data: null, errors: null, meta: { statusCode: 409, ... } }
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
#### `res.unprocessable(message?, errors?)`
|
|
300
|
+
|
|
301
|
+
**Status: 422** — Use when the data format is correct but business logic validation fails.
|
|
302
|
+
|
|
303
|
+
```js
|
|
304
|
+
res.unprocessable("Cannot process this request", {
|
|
305
|
+
age: "Must be at least 18 years old",
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Response:
|
|
309
|
+
// { success: false, message: "Cannot process this request", data: null, errors: { age: "..." }, meta: { statusCode: 422, ... } }
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
#### `res.tooManyRequests(message?, retryAfter?)`
|
|
315
|
+
|
|
316
|
+
**Status: 429** — Use when a client exceeds the rate limit.
|
|
317
|
+
|
|
318
|
+
```js
|
|
319
|
+
// Basic usage
|
|
320
|
+
res.tooManyRequests("Too many requests, please slow down");
|
|
321
|
+
|
|
322
|
+
// With retryAfter in seconds — sets the Retry-After header automatically
|
|
323
|
+
res.tooManyRequests("Rate limit exceeded", 60);
|
|
324
|
+
|
|
325
|
+
// Response:
|
|
326
|
+
// Header: Retry-After: 60
|
|
327
|
+
// { success: false, message: "Rate limit exceeded", data: null, errors: null, meta: { statusCode: 429, ... } }
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
#### `res.serverError(message?)`
|
|
333
|
+
|
|
334
|
+
**Status: 500** — Use when an unexpected server-side error occurs.
|
|
335
|
+
|
|
336
|
+
```js
|
|
337
|
+
try {
|
|
338
|
+
await someRiskyOperation();
|
|
339
|
+
} catch (err) {
|
|
340
|
+
console.error(err);
|
|
341
|
+
return res.serverError("Something went wrong, please try again later");
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Response:
|
|
345
|
+
// { success: false, message: "Something went wrong, please try again later", data: null, errors: null, meta: { statusCode: 500, ... } }
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
#### `res.error(message?, statusCode?, errors?)`
|
|
351
|
+
|
|
352
|
+
**Status: custom** — Use when you need a custom error status code.
|
|
158
353
|
|
|
159
354
|
```js
|
|
160
|
-
res.
|
|
161
|
-
res.
|
|
162
|
-
res.badRequest("Validation failed", { email: "Email is required" });
|
|
355
|
+
res.error("Gone", 410);
|
|
356
|
+
res.error("Validation failed", 400, { field: "required" });
|
|
163
357
|
```
|
|
164
358
|
|
|
165
359
|
---
|
|
166
360
|
|
|
167
|
-
### 📄 Pagination
|
|
361
|
+
### 📄 Pagination — `res.paginate(data, message?, pagination?)`
|
|
362
|
+
|
|
363
|
+
Use for returning paginated lists with full pagination metadata.
|
|
168
364
|
|
|
169
365
|
```js
|
|
170
|
-
|
|
171
|
-
const
|
|
366
|
+
router.get("/users", async (req, res) => {
|
|
367
|
+
const page = parseInt(req.query.page) || 1;
|
|
368
|
+
const limit = parseInt(req.query.limit) || 10;
|
|
369
|
+
const skip = (page - 1) * limit;
|
|
370
|
+
|
|
371
|
+
const [users, total] = await Promise.all([
|
|
372
|
+
User.find().skip(skip).limit(limit),
|
|
373
|
+
User.countDocuments(),
|
|
374
|
+
]);
|
|
172
375
|
|
|
173
|
-
res.paginate(users, "Users fetched", {
|
|
174
|
-
page: 1,
|
|
175
|
-
limit: 10,
|
|
176
|
-
total: total,
|
|
376
|
+
return res.paginate(users, "Users fetched", { page, limit, total });
|
|
177
377
|
});
|
|
378
|
+
|
|
379
|
+
// Response:
|
|
380
|
+
// {
|
|
381
|
+
// success: true,
|
|
382
|
+
// message: "Users fetched",
|
|
383
|
+
// data: [...users],
|
|
384
|
+
// meta: {
|
|
385
|
+
// timestamp: "...",
|
|
386
|
+
// statusCode: 200,
|
|
387
|
+
// pagination: {
|
|
388
|
+
// page: 1, limit: 10, total: 100,
|
|
389
|
+
// totalPages: 10,
|
|
390
|
+
// hasNextPage: true,
|
|
391
|
+
// hasPrevPage: false
|
|
392
|
+
// }
|
|
393
|
+
// }
|
|
394
|
+
// }
|
|
178
395
|
```
|
|
179
396
|
|
|
397
|
+
| Pagination Field | Type | Description |
|
|
398
|
+
| ---------------- | -------- | ------------------------- |
|
|
399
|
+
| `page` | `number` | Current page number |
|
|
400
|
+
| `limit` | `number` | Number of items per page |
|
|
401
|
+
| `total` | `number` | Total number of documents |
|
|
402
|
+
|
|
180
403
|
---
|
|
181
404
|
|
|
182
|
-
|
|
405
|
+
### ⚡ asyncHandler
|
|
406
|
+
|
|
407
|
+
Eliminates try-catch boilerplate from every async route.
|
|
408
|
+
Errors are automatically forwarded to Express's `next(err)`.
|
|
409
|
+
|
|
410
|
+
```js
|
|
411
|
+
const { asyncHandler } = require("node-responder");
|
|
412
|
+
|
|
413
|
+
// ❌ Before — repetitive try-catch in every route:
|
|
414
|
+
router.get("/users", async (req, res) => {
|
|
415
|
+
try {
|
|
416
|
+
const users = await User.find();
|
|
417
|
+
res.ok(users);
|
|
418
|
+
} catch (err) {
|
|
419
|
+
res.serverError(err.message);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// ✅ After — clean and concise with asyncHandler:
|
|
424
|
+
router.get(
|
|
425
|
+
"/users",
|
|
426
|
+
asyncHandler(async (req, res) => {
|
|
427
|
+
const users = await User.find(); // errors automatically go to next(err)
|
|
428
|
+
res.ok(users);
|
|
429
|
+
}),
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
// Catch all errors in one global error handler:
|
|
433
|
+
app.use((err, req, res, next) => {
|
|
434
|
+
console.error(err);
|
|
435
|
+
res.serverError(err.message);
|
|
436
|
+
});
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
### 📝 Logger — `{ logger: true }`
|
|
442
|
+
|
|
443
|
+
Logs every incoming request to the terminal with method, URL, status code, and response time.
|
|
444
|
+
|
|
445
|
+
```js
|
|
446
|
+
app.use(responder({ logger: true }));
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Terminal output:
|
|
450
|
+
|
|
451
|
+
```
|
|
452
|
+
GET /api/users 200 23ms ✔
|
|
453
|
+
POST /api/users 201 45ms ✔
|
|
454
|
+
GET /api/users/abc123 404 12ms ✖
|
|
455
|
+
POST /api/auth/login 401 8ms ✖
|
|
456
|
+
DELETE /api/products/999 500 5ms ✖
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Tip:** Enable logger in development, disable in production:
|
|
460
|
+
|
|
461
|
+
```js
|
|
462
|
+
app.use(
|
|
463
|
+
responder({
|
|
464
|
+
logger: process.env.NODE_ENV !== "production",
|
|
465
|
+
}),
|
|
466
|
+
);
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
## 🔧 Real-World MERN Example
|
|
183
472
|
|
|
184
473
|
```js
|
|
185
474
|
const express = require("express");
|
|
186
475
|
const responder = require("node-responder");
|
|
187
|
-
const
|
|
476
|
+
const { asyncHandler } = require("node-responder");
|
|
188
477
|
|
|
189
|
-
router.
|
|
478
|
+
const router = express.Router();
|
|
479
|
+
router.use(responder({ logger: true }));
|
|
190
480
|
|
|
191
|
-
// GET
|
|
192
|
-
router.get(
|
|
193
|
-
|
|
481
|
+
// GET /api/users?page=1&limit=10
|
|
482
|
+
router.get(
|
|
483
|
+
"/",
|
|
484
|
+
asyncHandler(async (req, res) => {
|
|
194
485
|
const page = parseInt(req.query.page) || 1;
|
|
195
486
|
const limit = parseInt(req.query.limit) || 10;
|
|
196
487
|
const skip = (page - 1) * limit;
|
|
197
488
|
|
|
198
489
|
const [users, total] = await Promise.all([
|
|
199
|
-
User.find().skip(skip).limit(limit),
|
|
490
|
+
User.find().skip(skip).limit(limit).select("-password"),
|
|
200
491
|
User.countDocuments(),
|
|
201
492
|
]);
|
|
202
493
|
|
|
203
494
|
return res.paginate(users, "Users fetched", { page, limit, total });
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
495
|
+
}),
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
// GET /api/users/:id
|
|
499
|
+
router.get(
|
|
500
|
+
"/:id",
|
|
501
|
+
asyncHandler(async (req, res) => {
|
|
502
|
+
const user = await User.findById(req.params.id).select("-password");
|
|
503
|
+
if (!user) return res.notFound("User not found");
|
|
504
|
+
return res.ok(user, "User fetched");
|
|
505
|
+
}),
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
// POST /api/users
|
|
509
|
+
router.post(
|
|
510
|
+
"/",
|
|
511
|
+
asyncHandler(async (req, res) => {
|
|
512
|
+
const { name, email, password } = req.body;
|
|
513
|
+
|
|
514
|
+
const errors = {};
|
|
515
|
+
if (!name) errors.name = "Name is required";
|
|
516
|
+
if (!email) errors.email = "Email is required";
|
|
517
|
+
if (!password) errors.password = "Password is required";
|
|
518
|
+
|
|
519
|
+
if (Object.keys(errors).length > 0) {
|
|
520
|
+
return res.badRequest("Validation failed", errors);
|
|
219
521
|
}
|
|
220
522
|
|
|
221
523
|
const exists = await User.findOne({ email });
|
|
222
524
|
if (exists) return res.conflict("Email already registered");
|
|
223
525
|
|
|
224
|
-
const user = await User.create({ name, email });
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
526
|
+
const user = await User.create({ name, email, password });
|
|
527
|
+
|
|
528
|
+
return res.created(
|
|
529
|
+
{ id: user._id, name: user.name, email: user.email },
|
|
530
|
+
"Account created successfully",
|
|
531
|
+
);
|
|
532
|
+
}),
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
// PUT /api/users/:id
|
|
536
|
+
router.put(
|
|
537
|
+
"/:id",
|
|
538
|
+
asyncHandler(async (req, res) => {
|
|
539
|
+
const user = await User.findByIdAndUpdate(req.params.id, req.body, {
|
|
540
|
+
new: true,
|
|
541
|
+
});
|
|
542
|
+
if (!user) return res.notFound("User not found");
|
|
543
|
+
return res.ok(user, "User updated successfully");
|
|
544
|
+
}),
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
// DELETE /api/users/:id
|
|
548
|
+
router.delete(
|
|
549
|
+
"/:id",
|
|
550
|
+
asyncHandler(async (req, res) => {
|
|
551
|
+
const user = await User.findByIdAndDelete(req.params.id);
|
|
552
|
+
if (!user) return res.notFound("User not found");
|
|
553
|
+
return res.noContent();
|
|
554
|
+
}),
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
module.exports = router;
|
|
230
558
|
```
|
|
231
559
|
|
|
232
560
|
---
|
|
@@ -235,19 +563,46 @@ router.post("/", async (req, res) => {
|
|
|
235
563
|
|
|
236
564
|
```ts
|
|
237
565
|
import express from "express";
|
|
238
|
-
import responder from "node-responder";
|
|
566
|
+
import responder, { asyncHandler } from "node-responder";
|
|
239
567
|
|
|
240
568
|
const app = express();
|
|
241
|
-
app.use(
|
|
569
|
+
app.use(express.json());
|
|
570
|
+
app.use(responder({ logger: true }));
|
|
571
|
+
|
|
572
|
+
app.get(
|
|
573
|
+
"/users",
|
|
574
|
+
asyncHandler(async (req, res) => {
|
|
575
|
+
const users = await User.find();
|
|
576
|
+
res.ok(users, "Users fetched");
|
|
577
|
+
}),
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
// Global error handler
|
|
581
|
+
app.use(
|
|
582
|
+
(
|
|
583
|
+
err: Error,
|
|
584
|
+
req: express.Request,
|
|
585
|
+
res: express.Response,
|
|
586
|
+
next: express.NextFunction,
|
|
587
|
+
) => {
|
|
588
|
+
console.error(err);
|
|
589
|
+
res.serverError(err.message);
|
|
590
|
+
},
|
|
591
|
+
);
|
|
242
592
|
|
|
243
|
-
app.
|
|
244
|
-
const users = await User.find();
|
|
245
|
-
res.ok(users, "Users fetched");
|
|
246
|
-
});
|
|
593
|
+
app.listen(3000);
|
|
247
594
|
```
|
|
248
595
|
|
|
249
596
|
---
|
|
250
597
|
|
|
598
|
+
## 🔗 Links
|
|
599
|
+
|
|
600
|
+
- [npm](https://www.npmjs.com/package/node-responder)
|
|
601
|
+
- [GitHub](https://github.com/hammadsadi/node-responder)
|
|
602
|
+
- [Report an Issue](https://github.com/hammadsadi/node-responder/issues)
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
251
606
|
## 📄 License
|
|
252
607
|
|
|
253
608
|
MIT © [Hammad Sadi](https://github.com/hammadsadi)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-responder",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Modern, standardized API response middleware for Express.js — with TypeScript support, pagination, and shorthand methods.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -56,4 +56,4 @@
|
|
|
56
56
|
"engines": {
|
|
57
57
|
"node": ">=14.0.0"
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
}
|
package/src/index.js
CHANGED
|
@@ -3,140 +3,282 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* node-responder
|
|
5
5
|
* Standardized, modern API response middleware for Express.js
|
|
6
|
-
* @
|
|
6
|
+
* @version 1.1.0
|
|
7
7
|
* @license MIT
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
// ANSI Colors
|
|
11
|
+
|
|
12
|
+
const COLORS = {
|
|
13
|
+
reset: "\x1b[0m",
|
|
14
|
+
dim: "\x1b[2m",
|
|
15
|
+
bold: "\x1b[1m",
|
|
16
|
+
green: "\x1b[32m",
|
|
17
|
+
yellow: "\x1b[33m",
|
|
18
|
+
red: "\x1b[31m",
|
|
19
|
+
cyan: "\x1b[36m",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const isTTY = () => process.stdout && process.stdout.isTTY === true;
|
|
23
|
+
|
|
24
|
+
const colorize = (str, color) =>
|
|
25
|
+
isTTY() ? `${color}${str}${COLORS.reset}` : str;
|
|
26
|
+
|
|
27
|
+
const colorStatus = (code) => {
|
|
28
|
+
if (code >= 500) return colorize(code, COLORS.red);
|
|
29
|
+
if (code >= 400) return colorize(code, COLORS.yellow);
|
|
30
|
+
if (code >= 300) return colorize(code, COLORS.cyan);
|
|
31
|
+
return colorize(code, COLORS.green);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const colorMethod = (method) => {
|
|
35
|
+
const m = (method || "UNKNOWN").toUpperCase().padEnd(7);
|
|
36
|
+
return colorize(m, COLORS.cyan);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const logRequest = (req, statusCode, startTime) => {
|
|
40
|
+
try {
|
|
41
|
+
const ms = Date.now() - startTime;
|
|
42
|
+
const method = colorMethod(req.method);
|
|
43
|
+
const url = (req.originalUrl || req.url || "/").padEnd(35);
|
|
44
|
+
const status = colorStatus(statusCode);
|
|
45
|
+
const time = colorize(`${ms}ms`, COLORS.dim);
|
|
46
|
+
const icon =
|
|
47
|
+
statusCode >= 400
|
|
48
|
+
? colorize("✖", COLORS.red)
|
|
49
|
+
: colorize("✔", COLORS.green);
|
|
50
|
+
process.stdout.write(` ${method} ${url} ${status} ${time} ${icon}\n`);
|
|
51
|
+
} catch (_) {}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Validation helpers
|
|
55
|
+
|
|
56
|
+
const safeMessage = (val, fallback) => {
|
|
57
|
+
if (val === undefined || val === null) return fallback;
|
|
58
|
+
return String(val);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const safeStatus = (val, fallback) => {
|
|
62
|
+
const n = Number(val);
|
|
63
|
+
return Number.isFinite(n) && n >= 100 && n <= 599 ? n : fallback;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Core response builders
|
|
67
|
+
|
|
68
|
+
const successResponse = (res, data, message, statusCode) => {
|
|
69
|
+
const code = safeStatus(statusCode, 200);
|
|
70
|
+
const msg = safeMessage(message, "Success");
|
|
71
|
+
const body = data === undefined ? null : data;
|
|
72
|
+
|
|
73
|
+
return res.status(code).json({
|
|
20
74
|
success: true,
|
|
21
|
-
message,
|
|
22
|
-
data,
|
|
75
|
+
message: msg,
|
|
76
|
+
data: body,
|
|
23
77
|
meta: {
|
|
24
78
|
timestamp: new Date().toISOString(),
|
|
25
|
-
statusCode,
|
|
79
|
+
statusCode: code,
|
|
26
80
|
},
|
|
27
81
|
});
|
|
28
|
-
}
|
|
82
|
+
};
|
|
29
83
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
statusCode = 500,
|
|
37
|
-
errors = null,
|
|
38
|
-
) {
|
|
39
|
-
return res.status(statusCode).json({
|
|
84
|
+
const errorResponse = (res, message, statusCode, errors) => {
|
|
85
|
+
const code = safeStatus(statusCode, 500);
|
|
86
|
+
const msg = safeMessage(message, "Something went wrong");
|
|
87
|
+
const errs = errors === undefined || errors === null ? null : errors;
|
|
88
|
+
|
|
89
|
+
return res.status(code).json({
|
|
40
90
|
success: false,
|
|
41
|
-
message,
|
|
91
|
+
message: msg,
|
|
42
92
|
data: null,
|
|
43
|
-
errors,
|
|
93
|
+
errors: errs,
|
|
44
94
|
meta: {
|
|
45
95
|
timestamp: new Date().toISOString(),
|
|
46
|
-
statusCode,
|
|
96
|
+
statusCode: code,
|
|
47
97
|
},
|
|
48
98
|
});
|
|
49
|
-
}
|
|
99
|
+
};
|
|
50
100
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
res,
|
|
56
|
-
data = [],
|
|
57
|
-
message = "Success",
|
|
58
|
-
pagination = {},
|
|
59
|
-
) {
|
|
60
|
-
const { page = 1, limit = 10, total = 0 } = pagination;
|
|
101
|
+
const paginatedResponse = (res, data, message, pagination) => {
|
|
102
|
+
const arr = Array.isArray(data) ? data : [];
|
|
103
|
+
const msg = safeMessage(message, "Success");
|
|
104
|
+
const p = pagination && typeof pagination === "object" ? pagination : {};
|
|
61
105
|
|
|
62
|
-
const
|
|
106
|
+
const page = Math.max(1, parseInt(p.page, 10) || 1);
|
|
107
|
+
const limit = Math.max(1, parseInt(p.limit, 10) || 10);
|
|
108
|
+
const total = Math.max(0, parseInt(p.total, 10) || 0);
|
|
109
|
+
const totalPages = limit > 0 ? Math.ceil(total / limit) : 0;
|
|
63
110
|
|
|
64
111
|
return res.status(200).json({
|
|
65
112
|
success: true,
|
|
66
|
-
message,
|
|
67
|
-
data,
|
|
113
|
+
message: msg,
|
|
114
|
+
data: arr,
|
|
68
115
|
meta: {
|
|
69
116
|
timestamp: new Date().toISOString(),
|
|
70
117
|
statusCode: 200,
|
|
71
118
|
pagination: {
|
|
72
|
-
page
|
|
73
|
-
limit
|
|
74
|
-
total
|
|
119
|
+
page,
|
|
120
|
+
limit,
|
|
121
|
+
total,
|
|
75
122
|
totalPages,
|
|
76
123
|
hasNextPage: page < totalPages,
|
|
77
124
|
hasPrevPage: page > 1,
|
|
78
125
|
},
|
|
79
126
|
},
|
|
80
127
|
});
|
|
81
|
-
}
|
|
128
|
+
};
|
|
82
129
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
130
|
+
// asyncHandler
|
|
131
|
+
|
|
132
|
+
const asyncHandler = (fn) => {
|
|
133
|
+
if (typeof fn !== "function") {
|
|
134
|
+
throw new TypeError(
|
|
135
|
+
`[node-responder] asyncHandler expects a function, got "${typeof fn}"`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return (req, res, next) => {
|
|
139
|
+
try {
|
|
140
|
+
const result = fn(req, res, next);
|
|
141
|
+
if (result && typeof result.catch === "function") {
|
|
142
|
+
result.catch(next);
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
next(err);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Middleware factory
|
|
151
|
+
|
|
152
|
+
const responder = (options) => {
|
|
153
|
+
const opts = options && typeof options === "object" ? options : {};
|
|
154
|
+
const logger = opts.logger === true;
|
|
155
|
+
|
|
156
|
+
if (process.env.NODE_ENV !== "production") {
|
|
157
|
+
const known = ["logger"];
|
|
158
|
+
for (const key of Object.keys(opts)) {
|
|
159
|
+
if (!known.includes(key)) {
|
|
160
|
+
process.stderr.write(
|
|
161
|
+
`[node-responder] Unknown option "${key}" — valid options: ${known.join(", ")}\n`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return (req, res, next) => {
|
|
168
|
+
const startTime = Date.now();
|
|
169
|
+
|
|
170
|
+
const log = (code) => {
|
|
171
|
+
if (logger) logRequest(req, code, startTime);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// 2xx Success
|
|
175
|
+
|
|
176
|
+
res.success = (data, message, statusCode) => {
|
|
177
|
+
const code = safeStatus(statusCode, 200);
|
|
178
|
+
log(code);
|
|
179
|
+
return successResponse(res, data, message, code);
|
|
95
180
|
};
|
|
96
181
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
statusCode = 500,
|
|
101
|
-
errors = null,
|
|
102
|
-
) {
|
|
103
|
-
return errorResponse(res, message, statusCode, errors);
|
|
182
|
+
res.ok = (data, message) => {
|
|
183
|
+
log(200);
|
|
184
|
+
return successResponse(res, data, safeMessage(message, "Success"), 200);
|
|
104
185
|
};
|
|
105
186
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return
|
|
187
|
+
res.created = (data, message) => {
|
|
188
|
+
log(201);
|
|
189
|
+
return successResponse(
|
|
190
|
+
res,
|
|
191
|
+
data,
|
|
192
|
+
safeMessage(message, "Created successfully"),
|
|
193
|
+
201,
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
res.noContent = () => {
|
|
198
|
+
log(204);
|
|
199
|
+
return res.status(204).send();
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// 4xx / 5xx Error
|
|
203
|
+
|
|
204
|
+
res.error = (message, statusCode, errors) => {
|
|
205
|
+
const code = safeStatus(statusCode, 500);
|
|
206
|
+
log(code);
|
|
207
|
+
return errorResponse(res, message, code, errors);
|
|
109
208
|
};
|
|
110
209
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
errorResponse(res, message,
|
|
210
|
+
res.badRequest = (message, errors) => {
|
|
211
|
+
log(400);
|
|
212
|
+
return errorResponse(
|
|
213
|
+
res,
|
|
214
|
+
safeMessage(message, "Bad request"),
|
|
215
|
+
400,
|
|
216
|
+
errors ?? null,
|
|
217
|
+
);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
res.unauthorized = (message) => {
|
|
221
|
+
log(401);
|
|
222
|
+
return errorResponse(res, safeMessage(message, "Unauthorized"), 401);
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
res.forbidden = (message) => {
|
|
226
|
+
log(403);
|
|
227
|
+
return errorResponse(res, safeMessage(message, "Forbidden"), 403);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
res.notFound = (message) => {
|
|
231
|
+
log(404);
|
|
232
|
+
return errorResponse(res, safeMessage(message, "Not found"), 404);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
res.conflict = (message) => {
|
|
236
|
+
log(409);
|
|
237
|
+
return errorResponse(res, safeMessage(message, "Conflict"), 409);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
res.unprocessable = (message, errors) => {
|
|
241
|
+
log(422);
|
|
242
|
+
return errorResponse(
|
|
243
|
+
res,
|
|
244
|
+
safeMessage(message, "Unprocessable entity"),
|
|
245
|
+
422,
|
|
246
|
+
errors ?? null,
|
|
247
|
+
);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
res.tooManyRequests = (message, retryAfter) => {
|
|
251
|
+
if (retryAfter != null) res.set("Retry-After", String(retryAfter));
|
|
252
|
+
log(429);
|
|
253
|
+
return errorResponse(res, safeMessage(message, "Too many requests"), 429);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
res.serverError = (message) => {
|
|
257
|
+
log(500);
|
|
258
|
+
return errorResponse(
|
|
259
|
+
res,
|
|
260
|
+
safeMessage(message, "Internal server error"),
|
|
261
|
+
500,
|
|
262
|
+
);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// Pagination
|
|
266
|
+
|
|
267
|
+
res.paginate = (data, message, pagination) => {
|
|
268
|
+
log(200);
|
|
269
|
+
return paginatedResponse(res, data, message, pagination);
|
|
270
|
+
};
|
|
129
271
|
|
|
130
272
|
next();
|
|
131
273
|
};
|
|
132
|
-
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// Exports
|
|
133
277
|
|
|
134
|
-
|
|
135
|
-
module.exports =
|
|
136
|
-
module.exports.
|
|
278
|
+
module.exports = responder;
|
|
279
|
+
module.exports.default = responder;
|
|
280
|
+
module.exports.responder = responder;
|
|
281
|
+
module.exports.asyncHandler = asyncHandler;
|
|
137
282
|
module.exports.successResponse = successResponse;
|
|
138
283
|
module.exports.errorResponse = errorResponse;
|
|
139
284
|
module.exports.paginatedResponse = paginatedResponse;
|
|
140
|
-
|
|
141
|
-
// ESM default export support
|
|
142
|
-
module.exports.default = apiResponse;
|
package/types/index.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import { Request, Response, NextFunction
|
|
1
|
+
import { RequestHandler, Request, Response, NextFunction } from "express";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
// Options
|
|
4
|
+
|
|
5
|
+
export interface ResponderOptions {
|
|
6
|
+
/**
|
|
7
|
+
* When true, logs every request to stdout with method, URL, status, and response time.
|
|
8
|
+
* @default false
|
|
9
|
+
*/
|
|
10
|
+
logger?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Pagination
|
|
14
|
+
|
|
15
|
+
export interface PaginationInput {
|
|
16
|
+
page?: number | string;
|
|
17
|
+
limit?: number | string;
|
|
18
|
+
total?: number | string;
|
|
7
19
|
}
|
|
8
20
|
|
|
9
21
|
export interface PaginationMeta {
|
|
@@ -15,13 +27,15 @@ export interface PaginationMeta {
|
|
|
15
27
|
hasPrevPage: boolean;
|
|
16
28
|
}
|
|
17
29
|
|
|
30
|
+
// Response shapes
|
|
31
|
+
|
|
18
32
|
export interface ApiMeta {
|
|
19
33
|
timestamp: string;
|
|
20
34
|
statusCode: number;
|
|
21
35
|
pagination?: PaginationMeta;
|
|
22
36
|
}
|
|
23
37
|
|
|
24
|
-
export interface ApiSuccessResponse<T =
|
|
38
|
+
export interface ApiSuccessResponse<T = unknown> {
|
|
25
39
|
success: true;
|
|
26
40
|
message: string;
|
|
27
41
|
data: T;
|
|
@@ -32,40 +46,42 @@ export interface ApiErrorResponse {
|
|
|
32
46
|
success: false;
|
|
33
47
|
message: string;
|
|
34
48
|
data: null;
|
|
35
|
-
errors:
|
|
49
|
+
errors: unknown | null;
|
|
36
50
|
meta: ApiMeta;
|
|
37
51
|
}
|
|
38
52
|
|
|
53
|
+
// Express augmentation
|
|
54
|
+
|
|
39
55
|
declare global {
|
|
40
56
|
namespace Express {
|
|
41
57
|
interface Response {
|
|
42
|
-
|
|
43
|
-
success
|
|
58
|
+
// Generic
|
|
59
|
+
/** Send a success response with optional data, message, and status code */
|
|
60
|
+
success<T = unknown>(
|
|
44
61
|
data?: T,
|
|
45
62
|
message?: string,
|
|
46
63
|
statusCode?: number,
|
|
47
64
|
): Response;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
/** Send a paginated response */
|
|
53
|
-
paginate<T = any>(
|
|
65
|
+
/** Send an error response with optional message, status code, and errors */
|
|
66
|
+
error(message?: string, statusCode?: number, errors?: unknown): Response;
|
|
67
|
+
/** Send a paginated success response */
|
|
68
|
+
paginate<T = unknown>(
|
|
54
69
|
data?: T[],
|
|
55
70
|
message?: string,
|
|
56
|
-
pagination?:
|
|
71
|
+
pagination?: PaginationInput,
|
|
57
72
|
): Response;
|
|
58
73
|
|
|
59
|
-
//
|
|
74
|
+
// 2xx
|
|
60
75
|
/** 200 OK */
|
|
61
|
-
ok<T =
|
|
76
|
+
ok<T = unknown>(data?: T, message?: string): Response;
|
|
62
77
|
/** 201 Created */
|
|
63
|
-
created<T =
|
|
78
|
+
created<T = unknown>(data?: T, message?: string): Response;
|
|
64
79
|
/** 204 No Content */
|
|
65
80
|
noContent(): Response;
|
|
66
81
|
|
|
82
|
+
// 4xx
|
|
67
83
|
/** 400 Bad Request */
|
|
68
|
-
badRequest(message?: string, errors?:
|
|
84
|
+
badRequest(message?: string, errors?: unknown): Response;
|
|
69
85
|
/** 401 Unauthorized */
|
|
70
86
|
unauthorized(message?: string): Response;
|
|
71
87
|
/** 403 Forbidden */
|
|
@@ -75,18 +91,34 @@ declare global {
|
|
|
75
91
|
/** 409 Conflict */
|
|
76
92
|
conflict(message?: string): Response;
|
|
77
93
|
/** 422 Unprocessable Entity */
|
|
78
|
-
unprocessable(message?: string, errors?:
|
|
94
|
+
unprocessable(message?: string, errors?: unknown): Response;
|
|
95
|
+
/** 429 Too Many Requests — optionally sets Retry-After header */
|
|
96
|
+
tooManyRequests(message?: string, retryAfter?: number | string): Response;
|
|
97
|
+
|
|
98
|
+
// 5xx
|
|
79
99
|
/** 500 Internal Server Error */
|
|
80
100
|
serverError(message?: string): Response;
|
|
81
101
|
}
|
|
82
102
|
}
|
|
83
103
|
}
|
|
84
104
|
|
|
85
|
-
|
|
105
|
+
// Exported functions
|
|
106
|
+
|
|
107
|
+
/** Express middleware — attaches all response helpers to res */
|
|
108
|
+
export declare function responder(options?: ResponderOptions): RequestHandler;
|
|
109
|
+
|
|
110
|
+
/** Wraps an async route handler and forwards errors to next() automatically */
|
|
111
|
+
export declare function asyncHandler(
|
|
112
|
+
fn: (
|
|
113
|
+
req: Request,
|
|
114
|
+
res: Response,
|
|
115
|
+
next: NextFunction,
|
|
116
|
+
) => Promise<unknown> | unknown,
|
|
117
|
+
): RequestHandler;
|
|
86
118
|
|
|
87
119
|
export declare function successResponse(
|
|
88
120
|
res: Response,
|
|
89
|
-
data?:
|
|
121
|
+
data?: unknown,
|
|
90
122
|
message?: string,
|
|
91
123
|
statusCode?: number,
|
|
92
124
|
): Response;
|
|
@@ -95,14 +127,14 @@ export declare function errorResponse(
|
|
|
95
127
|
res: Response,
|
|
96
128
|
message?: string,
|
|
97
129
|
statusCode?: number,
|
|
98
|
-
errors?:
|
|
130
|
+
errors?: unknown,
|
|
99
131
|
): Response;
|
|
100
132
|
|
|
101
133
|
export declare function paginatedResponse(
|
|
102
134
|
res: Response,
|
|
103
|
-
data?:
|
|
135
|
+
data?: unknown[],
|
|
104
136
|
message?: string,
|
|
105
|
-
pagination?:
|
|
137
|
+
pagination?: PaginationInput,
|
|
106
138
|
): Response;
|
|
107
139
|
|
|
108
|
-
export default
|
|
140
|
+
export default responder;
|