devdad-express-utils 1.1.0 → 1.1.2
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 +53 -5
- package/dist/authWrapper.d.ts +17 -0
- package/dist/authWrapper.js +48 -0
- package/dist/catchAsync.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/responseFormatter.d.ts +17 -0
- package/dist/responseFormatter.js +30 -0
- package/dist/validateRequest.d.ts +9 -0
- package/dist/validateRequest.js +19 -0
- package/package.json +16 -4
package/README.md
CHANGED
|
@@ -52,13 +52,35 @@ import { AppError } from "devdad-express-utils";
|
|
|
52
52
|
throw new AppError("Validation failed", 400, ["Email is required"]);
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
### Response Formatting
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
const { AppError, catchAsync, errorHandler } = require("devdad-express-utils");
|
|
57
|
+
Standardize API responses with consistent JSON structure.
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
import {
|
|
59
|
+
```typescript
|
|
60
|
+
import { sendSuccess, sendError, sendPaginated } from "devdad-express-utils";
|
|
61
|
+
|
|
62
|
+
// Success response
|
|
63
|
+
sendSuccess(res, { id: 1, name: 'John' }, 'User fetched', 200);
|
|
64
|
+
|
|
65
|
+
// Error response
|
|
66
|
+
sendError(res, 'User not found', 404);
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Authentication Middleware
|
|
72
|
+
|
|
73
|
+
JWT-based authentication wrapper.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { requireAuth } from "devdad-express-utils";
|
|
77
|
+
|
|
78
|
+
const authMiddleware = requireAuth({ secret: process.env.JWT_SECRET });
|
|
79
|
+
|
|
80
|
+
app.get('/profile', authMiddleware, (req, res) => {
|
|
81
|
+
// req.user contains decoded JWT
|
|
82
|
+
res.json(req.user);
|
|
83
|
+
});
|
|
62
84
|
```
|
|
63
85
|
|
|
64
86
|
## Error Handling Patterns
|
|
@@ -142,6 +164,32 @@ Express error handling middleware with detailed logging in development.
|
|
|
142
164
|
errorHandler(err: any, req: Request, res: Response, next: NextFunction) => void
|
|
143
165
|
```
|
|
144
166
|
|
|
167
|
+
### sendSuccess
|
|
168
|
+
|
|
169
|
+
Sends a standardized success response.
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
sendSuccess(res: Response, data: any, message?: string, statusCode?: number) => void
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### sendError
|
|
176
|
+
|
|
177
|
+
Sends a standardized error response.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
sendError(res: Response, message: string, statusCode?: number, data?: any) => void
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
### requireAuth
|
|
186
|
+
|
|
187
|
+
Middleware for JWT authentication.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
requireAuth(options: { secret: string, algorithms?: Algorithm[] }) => (req, res, next) => void
|
|
191
|
+
```
|
|
192
|
+
|
|
145
193
|
## Development
|
|
146
194
|
|
|
147
195
|
```bash
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { JwtPayload, Algorithm } from "jsonwebtoken";
|
|
2
|
+
import { Request, Response, NextFunction } from "express";
|
|
3
|
+
interface AuthOptions {
|
|
4
|
+
secret: string;
|
|
5
|
+
algorithms?: Algorithm[];
|
|
6
|
+
}
|
|
7
|
+
interface AuthenticatedRequest extends Request {
|
|
8
|
+
user?: JwtPayload | string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Middleware to require JWT authentication.
|
|
12
|
+
* Verifies the JWT token from Authorization header and attaches decoded payload to req.user.
|
|
13
|
+
* @param {AuthOptions} options - JWT verification options.
|
|
14
|
+
* @returns {(req: Request, res: Response, next: NextFunction) => void} - Middleware function.
|
|
15
|
+
*/
|
|
16
|
+
export declare const requireAuth: (options: AuthOptions) => (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import jwt, { JsonWebTokenError, TokenExpiredError, } from "jsonwebtoken";
|
|
2
|
+
import { AppError } from "./AppError.js";
|
|
3
|
+
const defaultAlgorithms = ["HS256"];
|
|
4
|
+
/**
|
|
5
|
+
* Middleware to require JWT authentication.
|
|
6
|
+
* Verifies the JWT token from Authorization header and attaches decoded payload to req.user.
|
|
7
|
+
* @param {AuthOptions} options - JWT verification options.
|
|
8
|
+
* @returns {(req: Request, res: Response, next: NextFunction) => void} - Middleware function.
|
|
9
|
+
*/
|
|
10
|
+
export const requireAuth = (options) => {
|
|
11
|
+
return (req, res, next) => {
|
|
12
|
+
const authHeader = req.headers.authorization;
|
|
13
|
+
if (!authHeader) {
|
|
14
|
+
throw new AppError("Access denied. No token provided.", 401);
|
|
15
|
+
}
|
|
16
|
+
if (!authHeader.startsWith("Bearer ")) {
|
|
17
|
+
throw new AppError("Malformed authorization header.", 401);
|
|
18
|
+
}
|
|
19
|
+
const token = authHeader.substring(7);
|
|
20
|
+
if (!token) {
|
|
21
|
+
throw new AppError("Access denied. No token provided.", 401);
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
let decoded;
|
|
25
|
+
if (options.algorithms) {
|
|
26
|
+
decoded = jwt.verify(token, options.secret, {
|
|
27
|
+
algorithms: options.algorithms,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
decoded = jwt.verify(token, options.secret);
|
|
32
|
+
}
|
|
33
|
+
req.user = decoded;
|
|
34
|
+
next();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error instanceof JsonWebTokenError) {
|
|
38
|
+
throw new AppError("Invalid token.", 401);
|
|
39
|
+
}
|
|
40
|
+
else if (error instanceof TokenExpiredError) {
|
|
41
|
+
throw new AppError("Token expired.", 401);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
throw new AppError("Authentication failed.", 401);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
};
|
package/dist/catchAsync.js
CHANGED
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
* @returns {(req: Request, res: Response, next: NextFunction) => void} - The wrapped handler that catches errors.
|
|
6
6
|
*/
|
|
7
7
|
export const catchAsync = (fn) => (req, res, next) => {
|
|
8
|
-
fn(req, res, next).catch(next);
|
|
8
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
9
9
|
};
|
|
10
10
|
//#endregion
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Response } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* Sends a success response.
|
|
4
|
+
* @param {Response} res - Express response object.
|
|
5
|
+
* @param {any} data - Response data.
|
|
6
|
+
* @param {string} [message='Success'] - Response message.
|
|
7
|
+
* @param {number} [statusCode=200] - HTTP status code.
|
|
8
|
+
*/
|
|
9
|
+
export declare const sendSuccess: (res: Response, data: any, message?: string, statusCode?: number) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Sends an error response.
|
|
12
|
+
* @param {Response} res - Express response object.
|
|
13
|
+
* @param {string} message - Error message.
|
|
14
|
+
* @param {number} [statusCode=400] - HTTP status code.
|
|
15
|
+
* @param {any} [data] - Additional error data.
|
|
16
|
+
*/
|
|
17
|
+
export declare const sendError: (res: Response, message: string, statusCode?: number, data?: any) => void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sends a success response.
|
|
3
|
+
* @param {Response} res - Express response object.
|
|
4
|
+
* @param {any} data - Response data.
|
|
5
|
+
* @param {string} [message='Success'] - Response message.
|
|
6
|
+
* @param {number} [statusCode=200] - HTTP status code.
|
|
7
|
+
*/
|
|
8
|
+
export const sendSuccess = (res, data, message = "Success", statusCode = 200) => {
|
|
9
|
+
const response = {
|
|
10
|
+
status: "success",
|
|
11
|
+
message,
|
|
12
|
+
data,
|
|
13
|
+
};
|
|
14
|
+
res.status(statusCode).json(response);
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Sends an error response.
|
|
18
|
+
* @param {Response} res - Express response object.
|
|
19
|
+
* @param {string} message - Error message.
|
|
20
|
+
* @param {number} [statusCode=400] - HTTP status code.
|
|
21
|
+
* @param {any} [data] - Additional error data.
|
|
22
|
+
*/
|
|
23
|
+
export const sendError = (res, message, statusCode = 400, data) => {
|
|
24
|
+
const response = {
|
|
25
|
+
status: "error",
|
|
26
|
+
message,
|
|
27
|
+
...(data && { data }),
|
|
28
|
+
};
|
|
29
|
+
res.status(statusCode).json(response);
|
|
30
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ValidationChain } from 'express-validator';
|
|
2
|
+
import { Request, Response, NextFunction } from 'express';
|
|
3
|
+
/**
|
|
4
|
+
* Middleware wrapper for express-validator validations.
|
|
5
|
+
* Runs the provided validations and throws AppError if any fail.
|
|
6
|
+
* @param {ValidationChain[]} validations - Array of express-validator validation chains.
|
|
7
|
+
* @returns {(req: Request, res: Response, next: NextFunction) => Promise<void>} - Middleware function.
|
|
8
|
+
*/
|
|
9
|
+
export declare const validateRequest: (validations: ValidationChain[]) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { validationResult } from 'express-validator';
|
|
2
|
+
import { AppError } from './AppError.js';
|
|
3
|
+
/**
|
|
4
|
+
* Middleware wrapper for express-validator validations.
|
|
5
|
+
* Runs the provided validations and throws AppError if any fail.
|
|
6
|
+
* @param {ValidationChain[]} validations - Array of express-validator validation chains.
|
|
7
|
+
* @returns {(req: Request, res: Response, next: NextFunction) => Promise<void>} - Middleware function.
|
|
8
|
+
*/
|
|
9
|
+
export const validateRequest = (validations) => {
|
|
10
|
+
return async (req, res, next) => {
|
|
11
|
+
await Promise.all(validations.map(validation => validation.run(req)));
|
|
12
|
+
const errors = validationResult(req);
|
|
13
|
+
if (!errors.isEmpty()) {
|
|
14
|
+
const errorMessages = errors.array().map(err => err.msg);
|
|
15
|
+
throw new AppError('Validation failed', 400, errorMessages);
|
|
16
|
+
}
|
|
17
|
+
next();
|
|
18
|
+
};
|
|
19
|
+
};
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devdad-express-utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Reusable Express.js utilities for error handling, async wrapping, and more",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "tsc",
|
|
10
|
-
"test": "
|
|
10
|
+
"test": "vitest",
|
|
11
|
+
"test:ui": "vitest --ui",
|
|
12
|
+
"test:coverage": "vitest run --coverage",
|
|
11
13
|
"prepublishOnly": "npm run build"
|
|
12
14
|
},
|
|
13
15
|
"keywords": [
|
|
@@ -18,13 +20,23 @@
|
|
|
18
20
|
"javascript"
|
|
19
21
|
],
|
|
20
22
|
"author": "Oliver Metz",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/Devdad-Main/Express-Utils.git"
|
|
26
|
+
},
|
|
21
27
|
"license": "ISC",
|
|
22
28
|
"dependencies": {
|
|
23
|
-
"express": "^5.1.0"
|
|
29
|
+
"express": "^5.1.0",
|
|
30
|
+
"jsonwebtoken": "^9.0.2"
|
|
24
31
|
},
|
|
25
32
|
"devDependencies": {
|
|
26
33
|
"@types/express": "^5.0.5",
|
|
27
|
-
"
|
|
34
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
35
|
+
"@types/supertest": "^6.0.3",
|
|
36
|
+
"@vitest/ui": "^4.0.14",
|
|
37
|
+
"supertest": "^7.1.4",
|
|
38
|
+
"typescript": "^5.9.3",
|
|
39
|
+
"vitest": "^4.0.14"
|
|
28
40
|
},
|
|
29
41
|
"files": [
|
|
30
42
|
"dist"
|