powr-sdk-api 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 +115 -0
- package/dist/index.js +23 -0
- package/dist/middleware/error.js +36 -0
- package/dist/middleware/logger.js +44 -0
- package/dist/middleware/response.js +44 -0
- package/dist/swagger/index.js +115 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# PowrStack API Core Library
|
|
2
|
+
|
|
3
|
+
A shared library for Express API projects in the PowrStack ecosystem. This library provides common middleware and utilities for error handling, response formatting, logging, and Swagger documentation.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install powr-sdk-api
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Error handling middleware with `APIError` class
|
|
14
|
+
- Response formatting middleware
|
|
15
|
+
- Request/Response logging middleware
|
|
16
|
+
- Swagger documentation setup
|
|
17
|
+
- Async handler utility
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Basic Setup
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
const express = require('express');
|
|
25
|
+
const {
|
|
26
|
+
errorHandler,
|
|
27
|
+
responseHandler,
|
|
28
|
+
logger,
|
|
29
|
+
createSwaggerSpec
|
|
30
|
+
} = require('powr-sdk-api');
|
|
31
|
+
|
|
32
|
+
const app = express();
|
|
33
|
+
|
|
34
|
+
// Apply middleware
|
|
35
|
+
app.use(logger);
|
|
36
|
+
app.use(responseHandler);
|
|
37
|
+
|
|
38
|
+
// Setup Swagger
|
|
39
|
+
const swaggerSpec = createSwaggerSpec({
|
|
40
|
+
title: 'Your API',
|
|
41
|
+
version: '1.0.0',
|
|
42
|
+
description: 'Your API description',
|
|
43
|
+
serverUrl: 'http://localhost:3000'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Apply error handler last
|
|
47
|
+
app.use(errorHandler);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Error Handling
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
const { APIError, asyncHandler } = require('powr-sdk-api');
|
|
54
|
+
|
|
55
|
+
// In your route handler
|
|
56
|
+
app.get('/users/:id', asyncHandler(async (req, res) => {
|
|
57
|
+
const user = await User.findById(req.params.id);
|
|
58
|
+
if (!user) {
|
|
59
|
+
throw new APIError('User not found', 404);
|
|
60
|
+
}
|
|
61
|
+
res.success(user);
|
|
62
|
+
}));
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Response Formatting
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
// Success response
|
|
69
|
+
res.success(data, 'Operation successful', 200);
|
|
70
|
+
|
|
71
|
+
// Error response
|
|
72
|
+
res.error('Something went wrong', 500, { details: 'Error details' });
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Swagger Documentation
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
const swaggerUi = require('swagger-ui-express');
|
|
79
|
+
const { createSwaggerSpec } = require('powr-sdk-api');
|
|
80
|
+
|
|
81
|
+
const swaggerSpec = createSwaggerSpec({
|
|
82
|
+
title: 'Your API',
|
|
83
|
+
version: '1.0.0',
|
|
84
|
+
description: 'Your API description',
|
|
85
|
+
serverUrl: 'http://localhost:3000'
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
app.use('/swagger', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## API Reference
|
|
92
|
+
|
|
93
|
+
### Error Handling
|
|
94
|
+
|
|
95
|
+
- `APIError`: Custom error class for API errors
|
|
96
|
+
- `asyncHandler`: Wrapper for async route handlers
|
|
97
|
+
- `errorHandler`: Global error handling middleware
|
|
98
|
+
|
|
99
|
+
### Response Formatting
|
|
100
|
+
|
|
101
|
+
- `responseHandler`: Middleware for standardizing API responses
|
|
102
|
+
- `res.success()`: Send a success response
|
|
103
|
+
- `res.error()`: Send an error response
|
|
104
|
+
|
|
105
|
+
### Logging
|
|
106
|
+
|
|
107
|
+
- `logger`: Middleware for logging requests and responses
|
|
108
|
+
|
|
109
|
+
### Swagger
|
|
110
|
+
|
|
111
|
+
- `createSwaggerSpec`: Function to create Swagger specification
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
ISC
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PowrStack API Core Library
|
|
5
|
+
* A shared library for Express API projects
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const errorMiddleware = require('./middleware/error');
|
|
9
|
+
const responseMiddleware = require('./middleware/response');
|
|
10
|
+
const loggerMiddleware = require('./middleware/logger');
|
|
11
|
+
const createSwaggerSpec = require('./swagger');
|
|
12
|
+
module.exports = {
|
|
13
|
+
// Error handling
|
|
14
|
+
APIError: errorMiddleware.APIError,
|
|
15
|
+
asyncHandler: errorMiddleware.asyncHandler,
|
|
16
|
+
errorHandler: errorMiddleware.errorHandler,
|
|
17
|
+
// Response formatting
|
|
18
|
+
responseHandler: responseMiddleware,
|
|
19
|
+
// Logging
|
|
20
|
+
logger: loggerMiddleware,
|
|
21
|
+
// Swagger
|
|
22
|
+
createSwaggerSpec
|
|
23
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error handling middleware for Express applications
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class APIError extends Error {
|
|
8
|
+
constructor(message, statusCode = 500, details = null) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.statusCode = statusCode;
|
|
11
|
+
this.details = details;
|
|
12
|
+
this.name = 'APIError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const asyncHandler = fn => (req, res, next) => {
|
|
16
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
17
|
+
};
|
|
18
|
+
const errorHandler = (err, req, res, next) => {
|
|
19
|
+
console.error('Error:', {
|
|
20
|
+
message: err.message,
|
|
21
|
+
stack: err.stack,
|
|
22
|
+
details: err.details
|
|
23
|
+
});
|
|
24
|
+
const statusCode = err.statusCode || 500;
|
|
25
|
+
const message = err.message || 'Internal Server Error';
|
|
26
|
+
res.status(statusCode).json({
|
|
27
|
+
success: false,
|
|
28
|
+
message,
|
|
29
|
+
details: err.details
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
module.exports = {
|
|
33
|
+
APIError,
|
|
34
|
+
asyncHandler,
|
|
35
|
+
errorHandler
|
|
36
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Logging middleware for Express applications
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const logger = (req, res, next) => {
|
|
8
|
+
// Log request
|
|
9
|
+
console.log('Request:', {
|
|
10
|
+
timestamp: new Date().toISOString(),
|
|
11
|
+
method: req.method,
|
|
12
|
+
url: req.url,
|
|
13
|
+
ip: req.ip,
|
|
14
|
+
headers: req.headers,
|
|
15
|
+
body: req.body,
|
|
16
|
+
query: req.query
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Store original send and json methods
|
|
20
|
+
const originalSend = res.send;
|
|
21
|
+
const originalJson = res.json;
|
|
22
|
+
|
|
23
|
+
// Override send method to log response
|
|
24
|
+
res.send = function (body) {
|
|
25
|
+
console.log('Response:', {
|
|
26
|
+
timestamp: new Date().toISOString(),
|
|
27
|
+
statusCode: res.statusCode,
|
|
28
|
+
body: body
|
|
29
|
+
});
|
|
30
|
+
return originalSend.call(this, body);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Override json method to log response
|
|
34
|
+
res.json = function (body) {
|
|
35
|
+
console.log('Response:', {
|
|
36
|
+
timestamp: new Date().toISOString(),
|
|
37
|
+
statusCode: res.statusCode,
|
|
38
|
+
body: body
|
|
39
|
+
});
|
|
40
|
+
return originalJson.call(this, body);
|
|
41
|
+
};
|
|
42
|
+
next();
|
|
43
|
+
};
|
|
44
|
+
module.exports = logger;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Response formatting middleware for Express applications
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const responseHandler = (req, res, next) => {
|
|
8
|
+
// Add success method to response object
|
|
9
|
+
res.success = function (data = null, message = 'Success', statusCode = 200) {
|
|
10
|
+
return this.status(statusCode).json({
|
|
11
|
+
success: true,
|
|
12
|
+
message,
|
|
13
|
+
data
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Add error method to response object
|
|
18
|
+
res.error = function (message = 'Error', statusCode = 500, details = null) {
|
|
19
|
+
return this.status(statusCode).json({
|
|
20
|
+
success: false,
|
|
21
|
+
message,
|
|
22
|
+
details
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Store original json method
|
|
27
|
+
const originalJson = res.json;
|
|
28
|
+
|
|
29
|
+
// Override json method to ensure consistent format
|
|
30
|
+
res.json = function (data) {
|
|
31
|
+
// If response is already formatted, use original json
|
|
32
|
+
if (data && typeof data === 'object' && 'success' in data) {
|
|
33
|
+
return originalJson.call(this, data);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Format the response
|
|
37
|
+
return originalJson.call(this, {
|
|
38
|
+
success: true,
|
|
39
|
+
data
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
next();
|
|
43
|
+
};
|
|
44
|
+
module.exports = responseHandler;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Swagger configuration for Express applications
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const swaggerJsdoc = require('swagger-jsdoc');
|
|
8
|
+
const createSwaggerSpec = (options = {}) => {
|
|
9
|
+
const defaultOptions = {
|
|
10
|
+
definition: {
|
|
11
|
+
openapi: '3.0.0',
|
|
12
|
+
info: {
|
|
13
|
+
title: options.title || 'API Documentation',
|
|
14
|
+
version: options.version || '1.0.0',
|
|
15
|
+
description: options.description || 'API documentation'
|
|
16
|
+
},
|
|
17
|
+
servers: [{
|
|
18
|
+
url: options.serverUrl || 'http://localhost:3000',
|
|
19
|
+
description: process.env.NODE_ENV === 'production' ? 'Production server' : 'Development server'
|
|
20
|
+
}],
|
|
21
|
+
components: {
|
|
22
|
+
schemas: {
|
|
23
|
+
Error: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
success: {
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
example: false
|
|
29
|
+
},
|
|
30
|
+
message: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
example: 'Error message'
|
|
33
|
+
},
|
|
34
|
+
details: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
example: null
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
Success: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
success: {
|
|
44
|
+
type: 'boolean',
|
|
45
|
+
example: true
|
|
46
|
+
},
|
|
47
|
+
message: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
example: 'Operation successful'
|
|
50
|
+
},
|
|
51
|
+
data: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
example: null
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
responses: {
|
|
59
|
+
UnauthorizedError: {
|
|
60
|
+
description: 'Access token is missing or invalid',
|
|
61
|
+
content: {
|
|
62
|
+
'application/json': {
|
|
63
|
+
schema: {
|
|
64
|
+
$ref: '#/components/schemas/Error'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
NotFoundError: {
|
|
70
|
+
description: 'The specified resource was not found',
|
|
71
|
+
content: {
|
|
72
|
+
'application/json': {
|
|
73
|
+
schema: {
|
|
74
|
+
$ref: '#/components/schemas/Error'
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
apis: options.apis || ['./routes/*.js'] // Path to the API routes
|
|
83
|
+
};
|
|
84
|
+
const swaggerSpec = swaggerJsdoc(defaultOptions);
|
|
85
|
+
|
|
86
|
+
// Remove sensitive information in production
|
|
87
|
+
if (process.env.NODE_ENV === 'production') {
|
|
88
|
+
// Remove server URLs in production
|
|
89
|
+
swaggerSpec.servers = [];
|
|
90
|
+
|
|
91
|
+
// Remove any sensitive examples or descriptions
|
|
92
|
+
if (swaggerSpec.paths) {
|
|
93
|
+
Object.keys(swaggerSpec.paths).forEach(path => {
|
|
94
|
+
Object.keys(swaggerSpec.paths[path]).forEach(method => {
|
|
95
|
+
const operation = swaggerSpec.paths[path][method];
|
|
96
|
+
if (operation.responses) {
|
|
97
|
+
Object.keys(operation.responses).forEach(statusCode => {
|
|
98
|
+
const response = operation.responses[statusCode];
|
|
99
|
+
if (response.content) {
|
|
100
|
+
Object.keys(response.content).forEach(contentType => {
|
|
101
|
+
const content = response.content[contentType];
|
|
102
|
+
if (content.examples) {
|
|
103
|
+
delete content.examples;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return swaggerSpec;
|
|
114
|
+
};
|
|
115
|
+
module.exports = createSwaggerSpec;
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "powr-sdk-api",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Shared API core library for PowrStack projects",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "jest --passWithNoTests",
|
|
13
|
+
"lint": "eslint .",
|
|
14
|
+
"build": "babel src -d dist",
|
|
15
|
+
"prepare": "npm run build",
|
|
16
|
+
"prepublishOnly": "npm run test"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"api",
|
|
20
|
+
"express",
|
|
21
|
+
"middleware",
|
|
22
|
+
"error-handling",
|
|
23
|
+
"response-formatting",
|
|
24
|
+
"logging",
|
|
25
|
+
"swagger",
|
|
26
|
+
"documentation"
|
|
27
|
+
],
|
|
28
|
+
"author": "PowrStack",
|
|
29
|
+
"license": "ISC",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/powrstack/powr-sdk-api.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/powrstack/powr-sdk-api/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/powrstack/powr-sdk-api#readme",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"express": "^4.18.2",
|
|
40
|
+
"swagger-jsdoc": "^6.2.8",
|
|
41
|
+
"swagger-ui-express": "^5.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@babel/cli": "^7.23.9",
|
|
45
|
+
"@babel/core": "^7.24.0",
|
|
46
|
+
"@babel/preset-env": "^7.24.0",
|
|
47
|
+
"@types/express": "^4.17.21",
|
|
48
|
+
"@types/swagger-jsdoc": "^6.0.4",
|
|
49
|
+
"@types/swagger-ui-express": "^4.1.6",
|
|
50
|
+
"eslint": "^8.57.0",
|
|
51
|
+
"jest": "^29.7.0",
|
|
52
|
+
"typescript": "^5.3.3"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"express": "^4.18.2"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=14.0.0"
|
|
59
|
+
}
|
|
60
|
+
}
|