@kemets/response 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.
- package/README.md +152 -0
- package/index.js +46 -0
- package/package.json +30 -0
- package/response/constants/index.js +9 -0
- package/response/constants/messages.js +17 -0
- package/response/constants/statusCodes.js +15 -0
- package/response/error/index.js +85 -0
- package/response/success/index.js +31 -0
- package/response/utilities/builder.js +42 -0
- package/response/utilities/index.js +11 -0
- package/response/utilities/sender.js +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# @kemet/response
|
|
2
|
+
|
|
3
|
+
Standardized JSON API response helpers for Node.js backends (Express.js and compatible frameworks).
|
|
4
|
+
|
|
5
|
+
Every response shares the same shape so clients can parse success and error payloads predictably.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @kemet/response
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Response formats
|
|
14
|
+
|
|
15
|
+
### Success
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"success": true,
|
|
20
|
+
"status": 200,
|
|
21
|
+
"message": "OK",
|
|
22
|
+
"data": {}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Error
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"success": false,
|
|
31
|
+
"status": 404,
|
|
32
|
+
"message": "Resource not found",
|
|
33
|
+
"error": {}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick start (Express)
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
const express = require('express');
|
|
41
|
+
const {
|
|
42
|
+
OK,
|
|
43
|
+
CREATED,
|
|
44
|
+
NOT_FOUND,
|
|
45
|
+
BAD_REQUEST,
|
|
46
|
+
VALIDATION_ERROR,
|
|
47
|
+
INTERNAL_SERVER_ERROR,
|
|
48
|
+
} = require('@kemet/response');
|
|
49
|
+
|
|
50
|
+
const app = express();
|
|
51
|
+
app.use(express.json());
|
|
52
|
+
|
|
53
|
+
app.get('/users/:id', (req, res) => {
|
|
54
|
+
const user = findUser(req.params.id);
|
|
55
|
+
|
|
56
|
+
if (!user) {
|
|
57
|
+
return NOT_FOUND(res, 'User not found');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return OK(res, user, 'User retrieved');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
app.post('/users', (req, res) => {
|
|
64
|
+
const { error, value } = validateUser(req.body);
|
|
65
|
+
|
|
66
|
+
if (error) {
|
|
67
|
+
return VALIDATION_ERROR(res, error.details);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const user = createUser(value);
|
|
71
|
+
return CREATED(res, user, 'User created');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
app.use((err, req, res, next) => {
|
|
75
|
+
INTERNAL_SERVER_ERROR(res, { message: err.message });
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
app.listen(3000);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API reference
|
|
82
|
+
|
|
83
|
+
### Success helpers
|
|
84
|
+
|
|
85
|
+
| Function | Status | Description |
|
|
86
|
+
|----------|--------|-------------|
|
|
87
|
+
| `OK(res, data?, message?)` | 200 | Standard success response |
|
|
88
|
+
| `CREATED(res, data?, message?)` | 201 | Resource created |
|
|
89
|
+
|
|
90
|
+
### Error helpers
|
|
91
|
+
|
|
92
|
+
| Function | Status | Description |
|
|
93
|
+
|----------|--------|-------------|
|
|
94
|
+
| `BAD_REQUEST(res, message?, errors?)` | 400 | Invalid client request |
|
|
95
|
+
| `UNAUTHORIZED(res, message?)` | 401 | Authentication required |
|
|
96
|
+
| `FORBIDDEN(res, message?)` | 403 | Access denied |
|
|
97
|
+
| `NOT_FOUND(res, message?)` | 404 | Resource not found |
|
|
98
|
+
| `VALIDATION_ERROR(res, errors?)` | 422 | Validation failed |
|
|
99
|
+
| `TOO_MANY_REQUESTS(res, message?)` | 429 | Rate limit exceeded |
|
|
100
|
+
| `INTERNAL_SERVER_ERROR(res, error?)` | 500 | Server error |
|
|
101
|
+
|
|
102
|
+
Omitted `message` arguments use built-in defaults (for example, `"Resource not found"` for 404).
|
|
103
|
+
|
|
104
|
+
### Builders (framework-agnostic)
|
|
105
|
+
|
|
106
|
+
Use these when you are not using Express, or when you need the payload object without sending it:
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
const { buildSuccessResponse, buildErrorResponse } = require('@kemet/response');
|
|
110
|
+
|
|
111
|
+
const body = buildSuccessResponse(200, 'OK', { id: 1 });
|
|
112
|
+
// { success: true, status: 200, message: 'OK', data: { id: 1 } }
|
|
113
|
+
|
|
114
|
+
const errBody = buildErrorResponse(400, 'Invalid input', { field: 'email' });
|
|
115
|
+
// { success: false, status: 400, message: 'Invalid input', error: { field: 'email' } }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Constants
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
const { STATUS_CODES, DEFAULT_MESSAGES } = require('@kemet/response');
|
|
122
|
+
|
|
123
|
+
console.log(STATUS_CODES.NOT_FOUND); // 404
|
|
124
|
+
console.log(DEFAULT_MESSAGES[404]); // 'Resource not found'
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Project structure
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
.
|
|
131
|
+
├── index.js # Public entry point
|
|
132
|
+
├── package.json
|
|
133
|
+
├── README.md
|
|
134
|
+
└── response/
|
|
135
|
+
├── constants/ # Status codes and default messages
|
|
136
|
+
├── utilities/ # Builders and senders
|
|
137
|
+
├── success/ # 2xx helpers
|
|
138
|
+
└── error/ # 4xx / 5xx helpers
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Publishing to npm
|
|
142
|
+
|
|
143
|
+
1. Log in: `npm login`
|
|
144
|
+
2. For scoped packages, publish with public access:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npm publish --access public
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
MIT
|
package/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { STATUS_CODES, DEFAULT_MESSAGES } = require('./response/constants');
|
|
4
|
+
const {
|
|
5
|
+
buildSuccessResponse,
|
|
6
|
+
buildErrorResponse,
|
|
7
|
+
sendSuccess,
|
|
8
|
+
sendError,
|
|
9
|
+
} = require('./response/utilities');
|
|
10
|
+
const { OK, CREATED } = require('./response/success');
|
|
11
|
+
const {
|
|
12
|
+
BAD_REQUEST,
|
|
13
|
+
UNAUTHORIZED,
|
|
14
|
+
FORBIDDEN,
|
|
15
|
+
NOT_FOUND,
|
|
16
|
+
VALIDATION_ERROR,
|
|
17
|
+
TOO_MANY_REQUESTS,
|
|
18
|
+
INTERNAL_SERVER_ERROR,
|
|
19
|
+
} = require('./response/error');
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
// Success helpers
|
|
23
|
+
OK,
|
|
24
|
+
CREATED,
|
|
25
|
+
|
|
26
|
+
// Error helpers
|
|
27
|
+
BAD_REQUEST,
|
|
28
|
+
UNAUTHORIZED,
|
|
29
|
+
FORBIDDEN,
|
|
30
|
+
NOT_FOUND,
|
|
31
|
+
VALIDATION_ERROR,
|
|
32
|
+
TOO_MANY_REQUESTS,
|
|
33
|
+
INTERNAL_SERVER_ERROR,
|
|
34
|
+
|
|
35
|
+
// Response builders (use without Express)
|
|
36
|
+
buildSuccessResponse,
|
|
37
|
+
buildErrorResponse,
|
|
38
|
+
|
|
39
|
+
// Low-level senders
|
|
40
|
+
sendSuccess,
|
|
41
|
+
sendError,
|
|
42
|
+
|
|
43
|
+
// Constants
|
|
44
|
+
STATUS_CODES,
|
|
45
|
+
DEFAULT_MESSAGES,
|
|
46
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kemets/response",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Standardized API response helpers for Express.js and Node.js backends",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js",
|
|
8
|
+
"response"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"express",
|
|
15
|
+
"api",
|
|
16
|
+
"response",
|
|
17
|
+
"http",
|
|
18
|
+
"rest",
|
|
19
|
+
"json"
|
|
20
|
+
],
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=14.0.0"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": ""
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const STATUS_CODES = require('./statusCodes');
|
|
4
|
+
|
|
5
|
+
const DEFAULT_MESSAGES = Object.freeze({
|
|
6
|
+
[STATUS_CODES.OK]: 'OK',
|
|
7
|
+
[STATUS_CODES.CREATED]: 'Created',
|
|
8
|
+
[STATUS_CODES.BAD_REQUEST]: 'Bad request',
|
|
9
|
+
[STATUS_CODES.UNAUTHORIZED]: 'Unauthorized',
|
|
10
|
+
[STATUS_CODES.FORBIDDEN]: 'Forbidden',
|
|
11
|
+
[STATUS_CODES.NOT_FOUND]: 'Resource not found',
|
|
12
|
+
[STATUS_CODES.VALIDATION_ERROR]: 'Validation failed',
|
|
13
|
+
[STATUS_CODES.TOO_MANY_REQUESTS]: 'Too many requests',
|
|
14
|
+
[STATUS_CODES.INTERNAL_SERVER_ERROR]: 'Internal server error',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
module.exports = DEFAULT_MESSAGES;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const STATUS_CODES = Object.freeze({
|
|
4
|
+
OK: 200,
|
|
5
|
+
CREATED: 201,
|
|
6
|
+
BAD_REQUEST: 400,
|
|
7
|
+
UNAUTHORIZED: 401,
|
|
8
|
+
FORBIDDEN: 403,
|
|
9
|
+
NOT_FOUND: 404,
|
|
10
|
+
VALIDATION_ERROR: 422,
|
|
11
|
+
TOO_MANY_REQUESTS: 429,
|
|
12
|
+
INTERNAL_SERVER_ERROR: 500,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
module.exports = STATUS_CODES;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { STATUS_CODES } = require('../constants');
|
|
4
|
+
const { sendError } = require('../utilities');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 400 Bad Request — client sent an invalid request.
|
|
8
|
+
*
|
|
9
|
+
* @param {import('express').Response} res
|
|
10
|
+
* @param {string} [message]
|
|
11
|
+
* @param {*} [errors]
|
|
12
|
+
*/
|
|
13
|
+
function BAD_REQUEST(res, message, errors) {
|
|
14
|
+
return sendError(res, STATUS_CODES.BAD_REQUEST, message, errors);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 401 Unauthorized — authentication is required or failed.
|
|
19
|
+
*
|
|
20
|
+
* @param {import('express').Response} res
|
|
21
|
+
* @param {string} [message]
|
|
22
|
+
*/
|
|
23
|
+
function UNAUTHORIZED(res, message) {
|
|
24
|
+
return sendError(res, STATUS_CODES.UNAUTHORIZED, message);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 403 Forbidden — authenticated but not permitted.
|
|
29
|
+
*
|
|
30
|
+
* @param {import('express').Response} res
|
|
31
|
+
* @param {string} [message]
|
|
32
|
+
*/
|
|
33
|
+
function FORBIDDEN(res, message) {
|
|
34
|
+
return sendError(res, STATUS_CODES.FORBIDDEN, message);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 404 Not Found — requested resource does not exist.
|
|
39
|
+
*
|
|
40
|
+
* @param {import('express').Response} res
|
|
41
|
+
* @param {string} [message]
|
|
42
|
+
*/
|
|
43
|
+
function NOT_FOUND(res, message) {
|
|
44
|
+
return sendError(res, STATUS_CODES.NOT_FOUND, message);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 422 Validation Error — request body or params failed validation.
|
|
49
|
+
*
|
|
50
|
+
* @param {import('express').Response} res
|
|
51
|
+
* @param {*} [errors]
|
|
52
|
+
*/
|
|
53
|
+
function VALIDATION_ERROR(res, errors) {
|
|
54
|
+
return sendError(res, STATUS_CODES.VALIDATION_ERROR, undefined, errors);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 429 Too Many Requests — rate limit exceeded.
|
|
59
|
+
*
|
|
60
|
+
* @param {import('express').Response} res
|
|
61
|
+
* @param {string} [message]
|
|
62
|
+
*/
|
|
63
|
+
function TOO_MANY_REQUESTS(res, message) {
|
|
64
|
+
return sendError(res, STATUS_CODES.TOO_MANY_REQUESTS, message);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 500 Internal Server Error — unexpected server failure.
|
|
69
|
+
*
|
|
70
|
+
* @param {import('express').Response} res
|
|
71
|
+
* @param {*} [error]
|
|
72
|
+
*/
|
|
73
|
+
function INTERNAL_SERVER_ERROR(res, error) {
|
|
74
|
+
return sendError(res, STATUS_CODES.INTERNAL_SERVER_ERROR, undefined, error);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
BAD_REQUEST,
|
|
79
|
+
UNAUTHORIZED,
|
|
80
|
+
FORBIDDEN,
|
|
81
|
+
NOT_FOUND,
|
|
82
|
+
VALIDATION_ERROR,
|
|
83
|
+
TOO_MANY_REQUESTS,
|
|
84
|
+
INTERNAL_SERVER_ERROR,
|
|
85
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { STATUS_CODES } = require('../constants');
|
|
4
|
+
const { sendSuccess } = require('../utilities');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 200 OK — successful request with optional data payload.
|
|
8
|
+
*
|
|
9
|
+
* @param {import('express').Response} res
|
|
10
|
+
* @param {*} [data]
|
|
11
|
+
* @param {string} [message]
|
|
12
|
+
*/
|
|
13
|
+
function OK(res, data, message) {
|
|
14
|
+
return sendSuccess(res, STATUS_CODES.OK, data, message);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 201 Created — resource successfully created.
|
|
19
|
+
*
|
|
20
|
+
* @param {import('express').Response} res
|
|
21
|
+
* @param {*} [data]
|
|
22
|
+
* @param {string} [message]
|
|
23
|
+
*/
|
|
24
|
+
function CREATED(res, data, message) {
|
|
25
|
+
return sendSuccess(res, STATUS_CODES.CREATED, data, message);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
OK,
|
|
30
|
+
CREATED,
|
|
31
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { DEFAULT_MESSAGES } = require('../constants');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds a standardized success response payload.
|
|
7
|
+
*
|
|
8
|
+
* @param {number} status - HTTP status code
|
|
9
|
+
* @param {string} [message] - Human-readable message
|
|
10
|
+
* @param {*} [data] - Response payload
|
|
11
|
+
* @returns {object}
|
|
12
|
+
*/
|
|
13
|
+
function buildSuccessResponse(status, message, data) {
|
|
14
|
+
return {
|
|
15
|
+
success: true,
|
|
16
|
+
status,
|
|
17
|
+
message: message ?? DEFAULT_MESSAGES[status] ?? 'OK',
|
|
18
|
+
data: data !== undefined && data !== null ? data : {},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Builds a standardized error response payload.
|
|
24
|
+
*
|
|
25
|
+
* @param {number} status - HTTP status code
|
|
26
|
+
* @param {string} [message] - Human-readable message
|
|
27
|
+
* @param {*} [error] - Error details (validation errors, stack info, etc.)
|
|
28
|
+
* @returns {object}
|
|
29
|
+
*/
|
|
30
|
+
function buildErrorResponse(status, message, error) {
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
status,
|
|
34
|
+
message: message ?? DEFAULT_MESSAGES[status] ?? 'Error',
|
|
35
|
+
error: error !== undefined && error !== null ? error : {},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
buildSuccessResponse,
|
|
41
|
+
buildErrorResponse,
|
|
42
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { buildSuccessResponse, buildErrorResponse } = require('./builder');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sends a standardized success JSON response.
|
|
7
|
+
*
|
|
8
|
+
* @param {import('express').Response} res - Express response object
|
|
9
|
+
* @param {number} status - HTTP status code
|
|
10
|
+
* @param {*} [data] - Response payload
|
|
11
|
+
* @param {string} [message] - Human-readable message
|
|
12
|
+
* @returns {import('express').Response}
|
|
13
|
+
*/
|
|
14
|
+
function sendSuccess(res, status, data, message) {
|
|
15
|
+
const payload = buildSuccessResponse(status, message, data);
|
|
16
|
+
return res.status(status).json(payload);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Sends a standardized error JSON response.
|
|
21
|
+
*
|
|
22
|
+
* @param {import('express').Response} res - Express response object
|
|
23
|
+
* @param {number} status - HTTP status code
|
|
24
|
+
* @param {string} [message] - Human-readable message
|
|
25
|
+
* @param {*} [error] - Error details
|
|
26
|
+
* @returns {import('express').Response}
|
|
27
|
+
*/
|
|
28
|
+
function sendError(res, status, message, error) {
|
|
29
|
+
const payload = buildErrorResponse(status, message, error);
|
|
30
|
+
return res.status(status).json(payload);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
sendSuccess,
|
|
35
|
+
sendError,
|
|
36
|
+
};
|