backend-error 0.0.5
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 +146 -0
- package/dist/core/BackendError.d.ts +17 -0
- package/dist/core/BackendError.js +60 -0
- package/dist/core/httpErrorFormatter.d.ts +6 -0
- package/dist/core/httpErrorFormatter.js +62 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/interfaces/Types.d.ts +9 -0
- package/dist/interfaces/Types.js +2 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<center>
|
|
2
|
+
|
|
3
|
+
# Backend-error
|
|
4
|
+
|
|
5
|
+
Simple logging
|
|
6
|
+
|
|
7
|
+
<img alt="GitHub package.json version (master)" src="https://img.shields.io/github/package-json/v/eriksturesson/backendError/master">
|
|
8
|
+
<img alt="npm" src="https://img.shields.io/npm/dy/@eriksturesson/backend-error?label=npm%20downloads">
|
|
9
|
+
|
|
10
|
+
</center>
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @eriksturesson/backend-error
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## π₯ Custom BackendError class
|
|
19
|
+
|
|
20
|
+
Use `BackendError` class for standardized backend error handling:
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { BackendError } from "@eriksturesson/backend-error";
|
|
26
|
+
|
|
27
|
+
throw BackendError.BadRequest("Missing required field");
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or construct it manually for full control:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
const error = new BackendError({
|
|
34
|
+
message: "Something went terribly wrong",
|
|
35
|
+
severity: "critical",
|
|
36
|
+
showUser: true,
|
|
37
|
+
code: 500,
|
|
38
|
+
data: { context: "PaymentService", id: 12345 },
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Properties available:
|
|
43
|
+
|
|
44
|
+
- `message`: The error message
|
|
45
|
+
- `code`: HTTP status code
|
|
46
|
+
- `isOperational`: Marks it as a handled error (vs. crash)
|
|
47
|
+
- `showUser`: Whether frontend should show the message
|
|
48
|
+
- `severity`: "info" | "warning" | "error" | "critical"
|
|
49
|
+
- `data`: Additional metadata (optional and anything accepted)
|
|
50
|
+
|
|
51
|
+
You can extend it for custom domains too.
|
|
52
|
+
|
|
53
|
+
## π§ Example: With Express + showUser handling
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { BackendError } from "@eriksturesson/backend-error";
|
|
57
|
+
|
|
58
|
+
app.get("/user/:id", async (req, res, next) => {
|
|
59
|
+
try {
|
|
60
|
+
const user = null;
|
|
61
|
+
if (!user) throw BackendError.NotFound("User not found");
|
|
62
|
+
res.json(user);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (err instanceof BackendError && err.showUser) {
|
|
65
|
+
res.status(err.code ?? 400).json({ error: err.message });
|
|
66
|
+
} else {
|
|
67
|
+
res.status(500).json({ error: "Internal Server Error" });
|
|
68
|
+
}
|
|
69
|
+
next(err);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Available static error constructors
|
|
75
|
+
|
|
76
|
+
- `BackendError.BadRequest(message: string)` // 400, showUser: true
|
|
77
|
+
- `BackendError.Unauthorized(message: string)` // 401, showUser: true
|
|
78
|
+
- `BackendError.Forbidden(message: string)` // 403, showUser: true
|
|
79
|
+
- `BackendError.NotFound(message: string)` // 404, showUser: true
|
|
80
|
+
- `BackendError.Conflict(message: string)` // 409, showUser: true
|
|
81
|
+
- `BackendError.UnprocessableEntity(message: string)`// 422, showUser: true
|
|
82
|
+
- `BackendError.Internal(message: string)` // 500, showUser: false
|
|
83
|
+
- `BackendError.ServiceUnavailable(message: string)` // 503, showUser: false
|
|
84
|
+
|
|
85
|
+
## π§© httpErrorFormatter(error) β Format backend errors for HTTP responses
|
|
86
|
+
|
|
87
|
+
This helper takes an Error (or BackendError) and returns a plain object with:
|
|
88
|
+
|
|
89
|
+
β
status β an HTTP status code (number)
|
|
90
|
+
|
|
91
|
+
β
body β a JSON.stringify'd string representing the error (already parsed)
|
|
92
|
+
|
|
93
|
+
Itβs designed to be simple and universal β you can use it with any framework (Azure Functions, Express, etc).
|
|
94
|
+
|
|
95
|
+
π§ Example usage
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { BackendError, httpErrorFormatter } from "@eriksturesson/backend-error";
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
throw BackendError.Internal("Something went very wrong."); // π your static factory pattern
|
|
102
|
+
} catch (err) {
|
|
103
|
+
const { status, body } = await httpErrorFormatter(err);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
status,
|
|
107
|
+
headers: {
|
|
108
|
+
...getCorsHeaders(request.headers.get("origin")), // Add CORS headers yourself
|
|
109
|
+
},
|
|
110
|
+
body,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
β οΈ Important
|
|
116
|
+
This function does not include any HTTP headers β especially no CORS headers.
|
|
117
|
+
Why? Because every environment has different CORS rules.
|
|
118
|
+
|
|
119
|
+
If you're using Azure Functions, Express, or something else, you'll need to add CORS manually:
|
|
120
|
+
|
|
121
|
+
## π§© Types
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
export type Severity = "info" | "warning" | "error" | "critical";
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
export interface BackendErrorOptions {
|
|
129
|
+
message: string;
|
|
130
|
+
isOperational?: boolean;
|
|
131
|
+
showUser?: boolean;
|
|
132
|
+
severity?: Severity;
|
|
133
|
+
code?: number;
|
|
134
|
+
data?: any;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## π Repo
|
|
141
|
+
|
|
142
|
+
[https://github.com/eriksturesson/backendError](https://github.com/eriksturesson/cloud-logger)
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
Created by [@eriksturesson](https://eriksturesson.se)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BackendErrorOptions, Severity } from "../interfaces/Types";
|
|
2
|
+
export declare class BackendError extends Error {
|
|
3
|
+
isOperational: boolean;
|
|
4
|
+
showUser: boolean;
|
|
5
|
+
severity: Severity;
|
|
6
|
+
code?: number;
|
|
7
|
+
data?: any;
|
|
8
|
+
constructor(options: BackendErrorOptions);
|
|
9
|
+
static BadRequest(msg: string): BackendError;
|
|
10
|
+
static Unauthorized(msg: string): BackendError;
|
|
11
|
+
static Forbidden(msg: string): BackendError;
|
|
12
|
+
static NotFound(msg: string): BackendError;
|
|
13
|
+
static Conflict(msg: string): BackendError;
|
|
14
|
+
static UnprocessableEntity(msg: string): BackendError;
|
|
15
|
+
static Internal(msg: string): BackendError;
|
|
16
|
+
static ServiceUnavailable(msg: string): BackendError;
|
|
17
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __extends = (this && this.__extends) || (function () {
|
|
3
|
+
var extendStatics = function (d, b) {
|
|
4
|
+
extendStatics = Object.setPrototypeOf ||
|
|
5
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
+
return extendStatics(d, b);
|
|
8
|
+
};
|
|
9
|
+
return function (d, b) {
|
|
10
|
+
if (typeof b !== "function" && b !== null)
|
|
11
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
+
extendStatics(d, b);
|
|
13
|
+
function __() { this.constructor = d; }
|
|
14
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.BackendError = void 0;
|
|
19
|
+
var BackendError = /** @class */ (function (_super) {
|
|
20
|
+
__extends(BackendError, _super);
|
|
21
|
+
function BackendError(options) {
|
|
22
|
+
var _this = this;
|
|
23
|
+
var _a, _b, _c;
|
|
24
|
+
_this = _super.call(this, options.message) || this;
|
|
25
|
+
_this.name = "BackendError";
|
|
26
|
+
_this.isOperational = (_a = options.isOperational) !== null && _a !== void 0 ? _a : true;
|
|
27
|
+
_this.showUser = (_b = options.showUser) !== null && _b !== void 0 ? _b : true;
|
|
28
|
+
_this.severity = (_c = options.severity) !== null && _c !== void 0 ? _c : "error";
|
|
29
|
+
_this.code = options.code;
|
|
30
|
+
_this.data = options.data;
|
|
31
|
+
Object.setPrototypeOf(_this, BackendError.prototype);
|
|
32
|
+
return _this;
|
|
33
|
+
}
|
|
34
|
+
BackendError.BadRequest = function (msg) {
|
|
35
|
+
return new BackendError({ message: msg, code: 400, severity: "warning", showUser: true });
|
|
36
|
+
};
|
|
37
|
+
BackendError.Unauthorized = function (msg) {
|
|
38
|
+
return new BackendError({ message: msg, code: 401, severity: "warning", showUser: true });
|
|
39
|
+
};
|
|
40
|
+
BackendError.Forbidden = function (msg) {
|
|
41
|
+
return new BackendError({ message: msg, code: 403, severity: "warning", showUser: true });
|
|
42
|
+
};
|
|
43
|
+
BackendError.NotFound = function (msg) {
|
|
44
|
+
return new BackendError({ message: msg, code: 404, severity: "warning", showUser: true });
|
|
45
|
+
};
|
|
46
|
+
BackendError.Conflict = function (msg) {
|
|
47
|
+
return new BackendError({ message: msg, code: 409, severity: "warning", showUser: true });
|
|
48
|
+
};
|
|
49
|
+
BackendError.UnprocessableEntity = function (msg) {
|
|
50
|
+
return new BackendError({ message: msg, code: 422, severity: "warning", showUser: true });
|
|
51
|
+
};
|
|
52
|
+
BackendError.Internal = function (msg) {
|
|
53
|
+
return new BackendError({ message: msg, code: 500, severity: "critical", showUser: false });
|
|
54
|
+
};
|
|
55
|
+
BackendError.ServiceUnavailable = function (msg) {
|
|
56
|
+
return new BackendError({ message: msg, code: 503, severity: "critical", showUser: false });
|
|
57
|
+
};
|
|
58
|
+
return BackendError;
|
|
59
|
+
}(Error));
|
|
60
|
+
exports.BackendError = BackendError;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
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;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.httpErrorFormatter = void 0;
|
|
40
|
+
var BackendError_1 = require("./BackendError");
|
|
41
|
+
function httpErrorFormatter(_a) {
|
|
42
|
+
var err = _a.err;
|
|
43
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
44
|
+
var parsedCode, status_1, message;
|
|
45
|
+
return __generator(this, function (_b) {
|
|
46
|
+
if (err instanceof BackendError_1.BackendError && err.showUser) {
|
|
47
|
+
parsedCode = Number(err.code);
|
|
48
|
+
status_1 = Number.isInteger(parsedCode) ? parsedCode : 400;
|
|
49
|
+
return [2 /*return*/, {
|
|
50
|
+
status: status_1,
|
|
51
|
+
body: JSON.stringify(err),
|
|
52
|
+
}];
|
|
53
|
+
}
|
|
54
|
+
message = err instanceof Error ? err.message : typeof err === "string" ? err : "Internal Server Error";
|
|
55
|
+
return [2 /*return*/, {
|
|
56
|
+
status: 500,
|
|
57
|
+
body: JSON.stringify({ message: message }),
|
|
58
|
+
}];
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
exports.httpErrorFormatter = httpErrorFormatter;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.httpErrorFormatter = exports.BackendError = void 0;
|
|
4
|
+
var BackendError_1 = require("./core/BackendError");
|
|
5
|
+
Object.defineProperty(exports, "BackendError", { enumerable: true, get: function () { return BackendError_1.BackendError; } });
|
|
6
|
+
var httpErrorFormatter_1 = require("./core/httpErrorFormatter");
|
|
7
|
+
Object.defineProperty(exports, "httpErrorFormatter", { enumerable: true, get: function () { return httpErrorFormatter_1.httpErrorFormatter; } });
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "backend-error",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"maintainers": [
|
|
8
|
+
"Erik Sturesson"
|
|
9
|
+
],
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "Erik Sturesson",
|
|
12
|
+
"email": "hej@eriksturesson.se",
|
|
13
|
+
"url": "https://eriksturesson.se"
|
|
14
|
+
},
|
|
15
|
+
"version": "0.0.5",
|
|
16
|
+
"description": "Logger library supporting multiple cloud platforms with simple error handling.",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"error",
|
|
19
|
+
"fail",
|
|
20
|
+
"drawing",
|
|
21
|
+
"drawings",
|
|
22
|
+
"summarized",
|
|
23
|
+
"help",
|
|
24
|
+
"fun"
|
|
25
|
+
],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/eriksturesson/backendError"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"email": "hej@eriksturesson.se",
|
|
32
|
+
"url": "https://github.com/eriksturesson/backendError/issues"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"npm": "^10.0.0",
|
|
36
|
+
"node": "^22.0.0"
|
|
37
|
+
},
|
|
38
|
+
"main": "dist/index.js",
|
|
39
|
+
"types": "dist/index.d.ts",
|
|
40
|
+
"files": [
|
|
41
|
+
"dist"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc",
|
|
45
|
+
"test": "ts-node src/test/test.ts"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"dotenv": "^16.5.0",
|
|
49
|
+
"npm": "^11.4.1",
|
|
50
|
+
"typescript": "^4.7.4"
|
|
51
|
+
},
|
|
52
|
+
"exports": {
|
|
53
|
+
".": "./dist/index.js"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^22.15.27"
|
|
57
|
+
}
|
|
58
|
+
}
|