backend-error 1.0.4 → 1.1.1
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 +86 -46
- package/dist/core/httpErrorFormatter.d.ts +2 -4
- package/dist/core/httpErrorFormatter.js +32 -70
- package/package.json +15 -10
package/README.md
CHANGED
|
@@ -1,33 +1,58 @@
|
|
|
1
|
-
#
|
|
1
|
+
# backend-error
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
[](https://www.npmjs.com/package/backend-error)
|
|
7
|
+
`backend-error` is a lightweight Node.js / TypeScript utility that formats all errors—custom or native—into standardized HTTP responses with correct status codes and user-friendly messages. The `httpErrorFormatter` ensures secure, consistent error output by controlling what is exposed to the frontend.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 📦 Installation
|
|
9
12
|
|
|
10
13
|
```bash
|
|
11
14
|
npm install backend-error
|
|
12
15
|
```
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Use `BackendError` class for standardized backend error handling:
|
|
17
|
+
---
|
|
17
18
|
|
|
18
|
-
##
|
|
19
|
+
## 🚀 Throw `BackendError`, catch with `httpErrorFormatter`
|
|
19
20
|
|
|
20
21
|
```ts
|
|
21
|
-
import { BackendError } from "backend-error";
|
|
22
|
+
import { BackendError, httpErrorFormatter } from "backend-error";
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
app.post("/signup", async (req, res) => {
|
|
25
|
+
try {
|
|
26
|
+
const auth = req.headers.authorization;
|
|
27
|
+
const { email, id } = req.body;
|
|
28
|
+
if (!auth) throw BackendError.Unauthorized("Missing auth token"); // 401, showUser:true
|
|
29
|
+
if (!email) throw BackendError.BadRequest("Email is required"); // 400, showUser:true
|
|
30
|
+
const user = await getUser(req.params.id);
|
|
31
|
+
if (!user) throw BackendError.NotFound("User not found"); // 404, showUser:true
|
|
32
|
+
|
|
33
|
+
// Normal logic...
|
|
34
|
+
} catch (err) {
|
|
35
|
+
const { status, body } = httpErrorFormatter(err); // Handles BackendError and native Error safely
|
|
36
|
+
res.status(status).json(body);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
24
39
|
```
|
|
25
40
|
|
|
26
|
-
|
|
41
|
+
✅ No manual showUser checks — handled automatically by the formatter
|
|
42
|
+
✅ Returns generic 500 for critical or unknown errors (or if `showUser` is false)
|
|
43
|
+
✅ Formatter supports both BackendError instances and native Error objects
|
|
44
|
+
|
|
45
|
+
> The httpErrorFormatter inspects any error, formats it consistently, and decides what message is safe to show to users.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## ✨ Custom BackendError creation
|
|
50
|
+
|
|
51
|
+
If you prefer, create your own error with full control including custom metadata:
|
|
27
52
|
|
|
28
53
|
```ts
|
|
29
54
|
const error = new BackendError({
|
|
30
|
-
message: "Something went
|
|
55
|
+
message: "Something went wrong",
|
|
31
56
|
severity: "critical",
|
|
32
57
|
showUser: true,
|
|
33
58
|
code: 500,
|
|
@@ -35,35 +60,37 @@ const error = new BackendError({
|
|
|
35
60
|
});
|
|
36
61
|
```
|
|
37
62
|
|
|
38
|
-
|
|
63
|
+
### Selected BackendError options
|
|
39
64
|
|
|
40
|
-
- `message`:
|
|
65
|
+
- `message`: Error message
|
|
41
66
|
- `code`: HTTP status code
|
|
42
|
-
- `
|
|
43
|
-
- `showUser`: Whether frontend should show the message
|
|
67
|
+
- `showUser`: Whether to expose the message to frontend clients
|
|
44
68
|
- `severity`: "info" | "warning" | "error" | "critical"
|
|
45
|
-
- `data`:
|
|
69
|
+
- `data`: Optional metadata
|
|
46
70
|
|
|
47
|
-
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## ⚙️ Static error helpers
|
|
48
74
|
|
|
49
75
|
```ts
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
76
|
+
BackendError.BadRequest("..."); // 400, showUser: true
|
|
77
|
+
BackendError.Unauthorized("..."); // 401, showUser: true
|
|
78
|
+
BackendError.Forbidden("..."); // 403, showUser: true
|
|
79
|
+
BackendError.NotFound("..."); // 404, showUser: true
|
|
80
|
+
BackendError.Conflict("..."); // 409, showUser: true
|
|
81
|
+
BackendError.UnprocessableEntity("..."); // 422, showUser: true
|
|
82
|
+
BackendError.Internal("..."); // 500, showUser: false
|
|
83
|
+
BackendError.ServiceUnavailable("..."); // 503, showUser: false
|
|
59
84
|
```
|
|
60
85
|
|
|
61
|
-
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 🧠 Manual error handling (if not using `httpErrorFormatter`)
|
|
62
89
|
|
|
63
90
|
```ts
|
|
64
91
|
import { BackendError } from "backend-error";
|
|
65
92
|
|
|
66
|
-
app.get("/user/:id", async (req, res
|
|
93
|
+
app.get("/user/:id", async (req, res) => {
|
|
67
94
|
try {
|
|
68
95
|
const user = null;
|
|
69
96
|
if (!user) throw BackendError.NotFound("User not found");
|
|
@@ -78,24 +105,13 @@ app.get("/user/:id", async (req, res, next) => {
|
|
|
78
105
|
});
|
|
79
106
|
```
|
|
80
107
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
- `BackendError.BadRequest(message: string)` // 400, showUser: true
|
|
84
|
-
- `BackendError.Unauthorized(message: string)` // 401, showUser: true
|
|
85
|
-
- `BackendError.Forbidden(message: string)` // 403, showUser: true
|
|
86
|
-
- `BackendError.NotFound(message: string)` // 404, showUser: true
|
|
87
|
-
- `BackendError.Conflict(message: string)` // 409, showUser: true
|
|
88
|
-
- `BackendError.UnprocessableEntity(message: string)`// 422, showUser: true
|
|
89
|
-
- `BackendError.Internal(message: string)` // 500, showUser: false
|
|
90
|
-
- `BackendError.ServiceUnavailable(message: string)` // 503, showUser: false
|
|
108
|
+
---
|
|
91
109
|
|
|
92
110
|
## 🧩 Types
|
|
93
111
|
|
|
94
112
|
```ts
|
|
95
113
|
export type Severity = "info" | "warning" | "error" | "critical";
|
|
96
|
-
```
|
|
97
114
|
|
|
98
|
-
```ts
|
|
99
115
|
export interface BackendErrorOptions {
|
|
100
116
|
message: string;
|
|
101
117
|
isOperational?: boolean;
|
|
@@ -106,14 +122,38 @@ export interface BackendErrorOptions {
|
|
|
106
122
|
}
|
|
107
123
|
```
|
|
108
124
|
|
|
109
|
-
> 💬 Tip: This package doesn't handle headers or CORS. If you're building an API for browsers, remember to configure CORS separately.
|
|
110
|
-
|
|
111
125
|
---
|
|
112
126
|
|
|
113
|
-
##
|
|
127
|
+
## 🎨 Works well with [error-drawings](https://www.npmjs.com/package/error-drawings)
|
|
114
128
|
|
|
115
|
-
[
|
|
129
|
+

|
|
130
|
+

|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm install error-drawings
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Use for dev-friendly terminal logs — with a bit of dramatic flair for critical errors:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { BackendError, httpErrorFormatter } from "backend-error";
|
|
140
|
+
import drawLog from "error-drawings";
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
throw BackendError.Forbidden("No access to resource");
|
|
144
|
+
} catch (err) {
|
|
145
|
+
const isCritical = !(err instanceof BackendError && err.isOperational) || err.code >= 500;
|
|
146
|
+
if (isCritical) drawLog(err); // Dramatic terminal art for critical errors!
|
|
147
|
+
|
|
148
|
+
const { status, body } = httpErrorFormatter(err);
|
|
149
|
+
res.status(status).json(body);
|
|
150
|
+
}
|
|
151
|
+
```
|
|
116
152
|
|
|
117
153
|
---
|
|
118
154
|
|
|
155
|
+
## 🌐 Repo
|
|
156
|
+
|
|
157
|
+
[GitHub](https://github.com/eriksturesson/backendError)
|
|
158
|
+
|
|
119
159
|
Created by [@eriksturesson](https://eriksturesson.se)
|
|
@@ -10,81 +10,43 @@ var __assign = (this && this.__assign) || function () {
|
|
|
10
10
|
};
|
|
11
11
|
return __assign.apply(this, arguments);
|
|
12
12
|
};
|
|
13
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
-
function step(op) {
|
|
27
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
-
switch (op[0]) {
|
|
32
|
-
case 0: case 1: t = op; break;
|
|
33
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
-
default:
|
|
37
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
-
if (t[2]) _.ops.pop();
|
|
42
|
-
_.trys.pop(); continue;
|
|
43
|
-
}
|
|
44
|
-
op = body.call(thisArg, _);
|
|
45
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
14
|
exports.httpErrorFormatter = void 0;
|
|
51
15
|
var BackendError_1 = require("./BackendError");
|
|
52
16
|
function isValidStatusCode(code) {
|
|
53
17
|
return typeof code === "number" && Number.isInteger(code) && code >= 100 && code <= 599;
|
|
54
18
|
}
|
|
55
|
-
function httpErrorFormatter(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
var status_1
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
88
|
-
});
|
|
19
|
+
function httpErrorFormatter(err) {
|
|
20
|
+
// Handle your custom BackendError type
|
|
21
|
+
if (err instanceof BackendError_1.BackendError) {
|
|
22
|
+
var status_1 = isValidStatusCode(err.code) ? err.code : 400;
|
|
23
|
+
// If developer explicitly set showUser, trust that; otherwise default: true for 4xx, false for 5xx
|
|
24
|
+
var showUser_1 = typeof err.showUser === "boolean" ? err.showUser : status_1 < 500;
|
|
25
|
+
var message_1 = err.message || "Error";
|
|
26
|
+
// If showUser is true, return detailed info including data, code, severity; else generic message
|
|
27
|
+
var body_1 = showUser_1
|
|
28
|
+
? __assign(__assign({ message: message_1 }, (err.data !== undefined && { data: err.data })), { code: status_1, severity: err.severity }) : { message: "Internal Server Error" };
|
|
29
|
+
return { status: status_1, body: body_1, showUser: showUser_1, message: message_1 };
|
|
30
|
+
}
|
|
31
|
+
// Handle generic Error or string or unknown
|
|
32
|
+
var status = 500;
|
|
33
|
+
var message = "Internal Server Error";
|
|
34
|
+
var showUser = false;
|
|
35
|
+
if (err instanceof Error) {
|
|
36
|
+
message = err.message;
|
|
37
|
+
// Try to extract HTTP status code from common fields 'status' or 'code'
|
|
38
|
+
var maybeStatus = err.status || err.code;
|
|
39
|
+
if (isValidStatusCode(maybeStatus)) {
|
|
40
|
+
status = maybeStatus;
|
|
41
|
+
showUser = status < 500; // show message for 4xx errors by default
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (typeof err === "string") {
|
|
45
|
+
message = err;
|
|
46
|
+
status = 400;
|
|
47
|
+
showUser = true;
|
|
48
|
+
}
|
|
49
|
+
var body = showUser ? { message: message } : { message: "Internal Server Error" };
|
|
50
|
+
return { status: status, body: body, showUser: showUser, message: message };
|
|
89
51
|
}
|
|
90
52
|
exports.httpErrorFormatter = httpErrorFormatter;
|
package/package.json
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
{
|
|
2
|
+
"version": "1.1.1",
|
|
2
3
|
"name": "backend-error",
|
|
3
4
|
"license": "MIT",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"error",
|
|
7
|
+
"error-handler",
|
|
8
|
+
"http-errors",
|
|
9
|
+
"express",
|
|
10
|
+
"firebase-functions",
|
|
11
|
+
"nodejs",
|
|
12
|
+
"api",
|
|
13
|
+
"backend",
|
|
14
|
+
"error-handling",
|
|
15
|
+
"http-status",
|
|
16
|
+
"custom-error",
|
|
17
|
+
"structured-error"
|
|
18
|
+
],
|
|
4
19
|
"maintainers": [
|
|
5
20
|
"Erik Sturesson"
|
|
6
21
|
],
|
|
@@ -9,17 +24,7 @@
|
|
|
9
24
|
"email": "hej@eriksturesson.se",
|
|
10
25
|
"url": "https://eriksturesson.se"
|
|
11
26
|
},
|
|
12
|
-
"version": "1.0.4",
|
|
13
27
|
"description": "Simple Error handling library.",
|
|
14
|
-
"keywords": [
|
|
15
|
-
"error",
|
|
16
|
-
"fail",
|
|
17
|
-
"drawing",
|
|
18
|
-
"drawings",
|
|
19
|
-
"summarized",
|
|
20
|
-
"help",
|
|
21
|
-
"fun"
|
|
22
|
-
],
|
|
23
28
|
"repository": {
|
|
24
29
|
"type": "git",
|
|
25
30
|
"url": "https://github.com/eriksturesson/backendError"
|