diff --git a/README.md b/README.md index 064b267..f01d349 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,17 @@ # handle-http-errors -HTTP error handling library with TypeScript support, providing error classes, handlers, and middleware support. +Type-safe HTTP error handling package providing error classes, standardized responses, error handler, and built-in Express middleware support. -## ☘️ Features +Available as [NPM package](https://www.npmjs.com/package/handle-http-errors). -- Error Classes - Built-in HTTP error classes with type support -- Error Handler - Flexible error handling with standardized responses -- Middleware Support - Ready-to-use Express middleware -- TypeScript Support - Full type safety with TypeScript +## 🚂 Features -## 📥 Installation +- Error Classes +- Error Handler +- Express Middleware Support +- TypeScript Support -```bash -npm install handle-http-errors -# or -yarn add handle-http-errors -# or -pnpm add handle-http-errors -``` - -## 📖 Usage +## ☘️ Usage ### 🔧 Error Handler @@ -41,7 +33,7 @@ app.post('/users', async (req, res) => { }); ``` -### 🌐 Middleware +### 🌐 Express Middleware ```typescript import express from 'express'; @@ -60,17 +52,44 @@ app.get('/users/:id', (req, res, next) => { app.use(errorMiddleware()); ``` +## ⚙️ Configuration + +```typescript +interface ErrorHandlerOptions { + includeStack?: boolean; // Include stack traces + onError?: (error: unknown) => void; // onError callback +} + +// Use with handler +app.post('/users', async (req, res) => { + try { + throw new ValidationError('Invalid data'); + } catch (error) { + return errorHandler(error, res, { + includeStack: process.env.NODE_ENV !== 'production', + onError: (error) => console.error(error) + }); + } +}); + +// Use with middleware +app.use(errorMiddleware({ + includeStack: process.env.NODE_ENV !== 'production', + onError: (error) => console.error(error) +})); +``` + ## 🗂️ Error Classes ```typescript import { - HttpError, // Base error class - ValidationError, // 400 - Validation errors - BadRequestError, // 400 - Malformed requests - UnauthorizedError, // 401 - Authentication errors - ForbiddenError, // 403 - Authorization errors - NotFoundError, // 404 - Resource not found - InternalServerError // 500 - Server errors + HttpError, // Base error class + ValidationError, // 400 - Validation errors + BadRequestError, // 400 - Malformed requests + UnauthorizedError, // 401 - Authentication errors + ForbiddenError, // 403 - Authorization errors + NotFoundError, // 404 - Resource not found + InternalServerError // 500 - Server errors } from 'handle-http-errors'; ``` @@ -87,38 +106,21 @@ import { } ``` -## ⚙️ Configuration +## 🪺 Examples -```typescript -// Middleware options -interface ErrorHandlerOptions { - includeStack?: boolean; // Include stack traces - onError?: (error: unknown) => void; // Error callback -} +Check out the [examples](https://github.com/junjie-w/handle-http-errors/tree/main/examples) directory for detailed usage examples: -// Using with options -app.use(errorMiddleware({ - includeStack: process.env.NODE_ENV !== 'production', - onError: (error) => console.error(error) -})); +```bash +git clone https://github.com/junjie-w/handle-http-errors.git +cd handle-http-errors/examples +npm install + +# Try different examples +npm run dev:handler # Error handler usage +npm run dev:middleware # Express middleware usage +npm run dev:custom-middleware # Creating custom error-throwing middlewares ``` -## 🔍 Development vs Production - -Development Mode (`NODE_ENV !== 'production'`): -- Detailed error messages -- Stack traces (when enabled) -- Error details included - -Production Mode (`NODE_ENV === 'production'`): -- Generic error messages -- No stack traces -- Limited error details - -## 📚 Examples - -Check out the [examples](https://github.com/junjie-w/handle-http-errors/tree/main/examples) directory for detailed usage examples. - ## 📄 License MIT diff --git a/examples/README.md b/examples/README.md index 2bd311f..5a2fc42 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,24 +1,24 @@ # handle-http-errors Examples -Examples of using [handle-http-errors](https://www.npmjs.com/package/handle-http-errors) with different approaches. +Examples of using [handle-http-errors](https://www.npmjs.com/package/handle-http-errors) package. -## 🚀 Running Examples +## 🚂 Run Examples ```bash npm install -npm run dev:handler # Error handler usage (Port 3001) -npm run dev:middleware # Middleware usage (Port 3002) -npm run dev:custom # Custom middlewares usage (Port 3003) +npm run dev:handler # Error handler usage +npm run dev:middleware # Express middleware usage +npm run dev:custom-middleware # Creating custom error-throwing middlewares ``` -### 🔧 Error Handler Example (Port 3001) +### 🔧 Error Handler Example ```bash npm run dev:handler ``` -Test endpoints: +#### 🌱 Test endpoints: ```bash # Test NotFoundError @@ -43,13 +43,13 @@ curl -X POST \ http://localhost:3001/products ``` -### 🌐 Middleware Example (Port 3002) +### 🌐 Express Middleware Example ```bash npm run dev:middleware ``` -Test endpoints: +#### 🌱 Test endpoints: ```bash # Test NotFoundError @@ -73,7 +73,7 @@ curl -X POST \ -d '{"email":"invalid-email","username":"test"}' \ http://localhost:3002/users/register -# Test ValidationError with short username +# Test ValidationError with invalid username curl -X POST \ -H "Content-Type: application/json" \ -d '{"email":"test@example.com","username":"a"}' \ @@ -86,13 +86,13 @@ curl -X POST \ http://localhost:3002/users/register ``` -### 🛠️ Custom Middleware Example (Port 3003) +### 🛠️ Creating custom error-throwing middlewares ```bash npm run dev:custom-middleware ``` -Test endpoints: +#### 🌱 Test endpoints: ```bash # Test UnauthorizedError without token @@ -123,7 +123,7 @@ curl -X POST \ -d '{"email":"invalid-email","username":"a"}' \ http://localhost:3003/users/register -# Test ForbiddenError when creating admin +# Test ForbiddenError when creating admin users curl -X POST \ -H "Authorization: valid-token" \ -H "Content-Type: application/json" \ @@ -137,8 +137,3 @@ curl -X POST \ -d '{"email":"test@example.com","username":"testuser"}' \ http://localhost:3003/users/register ``` - -Each example demonstrates different aspects of error handling: -- Error Handler: Direct usage of errorHandler -- Middleware: Global error handling with middleware -- Custom Middleware: Creating custom error-throwing middlewares diff --git a/examples/package-lock.json b/examples/package-lock.json index 27ded2c..aae9d28 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -1,23 +1,17 @@ { - "name": "express-typescript-example", + "name": "handle-http-errors-examples", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "express-typescript-example", + "name": "handle-http-errors-examples", "dependencies": { "express": "^4.18.2", "handle-http-errors": "file:../" - }, - "devDependencies": { - "@types/express": "^4.17.17", - "ts-node": "^10.9.1", - "typescript": "^5.0.4" } }, "..": { - "name": "handle-http-errors", "version": "1.0.0", "license": "MIT", "dependencies": { @@ -45,164 +39,6 @@ } }, "../../..": {}, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, - "node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", - "dev": true, - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/qs": { - "version": "6.9.17", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", - "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -215,36 +51,6 @@ "node": ">= 0.6" } }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -340,12 +146,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -371,15 +171,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -631,12 +422,6 @@ "node": ">= 0.10" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "node_modules/math-intrinsics": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.0.0.tgz", @@ -958,49 +743,6 @@ "node": ">=0.6" } }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1013,25 +755,6 @@ "node": ">= 0.6" } }, - "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1048,12 +771,6 @@ "node": ">= 0.4.0" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -1061,15 +778,6 @@ "engines": { "node": ">= 0.8" } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } } } } diff --git a/examples/package.json b/examples/package.json index 46776a3..606be44 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,19 +1,14 @@ { - "name": "express-typescript-example", + "name": "handle-http-errors-examples", "private": true, "type": "module", "scripts": { "dev:handler": "node src/withHandler.js", "dev:middleware": "node src/withMiddleware.js", - "dev:custom-middlware": "node src/withCustomMiddleware.js" + "dev:custom-middleware": "node src/withCustomMiddleware.js" }, "dependencies": { "express": "^4.18.2", "handle-http-errors": "file:../" - }, - "devDependencies": { - "@types/express": "^4.17.17", - "ts-node": "^10.9.1", - "typescript": "^5.0.4" } } diff --git a/package.json b/package.json index 5f71105..d6ab1c6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "handle-http-errors", "version": "1.0.0", - "description": "TypeScript-enabled HTTP error handling library providing error classes, standardized responses, flexible handlers, and built-in Express middleware support.", + "description": "Type-safe HTTP error handling package providing error classes, standardized responses, error handler, and built-in Express middleware support.", "author": "Junjie Wu", "license": "MIT", "type": "module", diff --git a/src/__tests__/error-processor.test.ts b/src/__tests__/error-processor.test.ts index d0e5317..a9ed9ac 100644 --- a/src/__tests__/error-processor.test.ts +++ b/src/__tests__/error-processor.test.ts @@ -10,7 +10,8 @@ jest.mock('../config', () => ({ describe('errorProcessor', () => { const options = { includeStack: false }; - let originalEnv = process.env + const timestamp = expect.any(String); + describe('HttpError handling', () => { it('should process HttpError with stack trace when includeStack is true', async () => { const error = new HttpError(400, 'TEST_ERROR', 'Test message'); @@ -20,7 +21,7 @@ describe('errorProcessor', () => { status: 400, code: 'TEST_ERROR', message: 'Test message', - timestamp: expect.any(String), + timestamp, stack: expect.any(String) }); }); @@ -33,7 +34,7 @@ describe('errorProcessor', () => { status: 400, code: 'TEST_ERROR', message: 'Test message', - timestamp: expect.any(String) + timestamp }); expect(result.stack).toBeUndefined(); }); @@ -47,7 +48,7 @@ describe('errorProcessor', () => { status: 400, code: 'TEST_ERROR', message: 'Test message', - timestamp: expect.any(String) + timestamp }); expect(result.stack).toBeUndefined(); }); @@ -60,10 +61,18 @@ describe('errorProcessor', () => { status: 400, code: 'TEST_ERROR', message: 'Test message', - timestamp: expect.any(String), + timestamp, details: { foo: 'bar' } }); }); + + it('should call onError if provided', async () => { + const onError = jest.fn(); + const error = new Error('Test error'); + + await errorProcessor(error, { onError }); + expect(onError).toHaveBeenCalledWith(error); + }); it('should handle error when parsing HttpError fails', async () => { const error = new HttpError(400, 'TEST', 'Test'); @@ -77,49 +86,12 @@ describe('errorProcessor', () => { status: 400, code: 'PARSE_ERROR', message: 'Could not parse request', - timestamp: expect.any(String), + timestamp, details: isDevelopment ? { error: 'Parse error' } : undefined }); }); - - it('should call onError if provided', async () => { - const onError = jest.fn(); - const error = new Error('Test error'); - - await errorProcessor(error, { onError }); - expect(onError).toHaveBeenCalledWith(error); - }); describe('parse error handling', () => { - beforeEach(() => { - jest.resetModules(); - jest.resetAllMocks(); - process.env = { ...originalEnv, NODE_ENV: 'development' }; - }); - - afterEach(() => { - process.env = originalEnv - }); - - - it('should include parse error details in development', async () => { - const error = new HttpError(400, 'TEST', 'Test'); - Object.defineProperty(error, 'details', { - get: () => { throw new Error('Custom parse error message'); } - }); - - const result = await errorProcessor(error, options); - - expect(result).toEqual({ - status: 400, - code: 'PARSE_ERROR', - message: 'Could not parse request', - timestamp: expect.any(String), - details: { error: 'Custom parse error message' } - }); - }); - - it('should include parse error details in development', async () => { const error = new HttpError(400, 'TEST', 'Test'); Object.defineProperty(error, 'details', { @@ -132,7 +104,7 @@ describe('errorProcessor', () => { status: 400, code: 'PARSE_ERROR', message: 'Could not parse request', - timestamp: expect.any(String), + timestamp, details: { error: 'Custom parse error message' } @@ -151,7 +123,7 @@ describe('errorProcessor', () => { status: 400, code: 'PARSE_ERROR', message: 'Could not parse request', - timestamp: expect.any(String), + timestamp, details: { error: 'Parse error' } }); }); @@ -169,7 +141,7 @@ describe('errorProcessor', () => { status: 400, code: 'PARSE_ERROR', message: 'Could not parse request', - timestamp: expect.any(String), + timestamp, details: isDevelopment ? { error: 'Parse error with stack' } : undefined, stack: expect.any(String) }); @@ -187,7 +159,7 @@ describe('errorProcessor', () => { status: 400, code: 'PARSE_ERROR', message: 'Could not parse request', - timestamp: expect.any(String), + timestamp, details: isDevelopment ? { error: 'Parse error' } : undefined, stack: undefined }); @@ -204,7 +176,7 @@ describe('errorProcessor', () => { status: StatusCodes.BAD_REQUEST, code: 'VALIDATION_ERROR', message: 'Validation failed', - timestamp: expect.any(String) + timestamp }); }); @@ -216,7 +188,7 @@ describe('errorProcessor', () => { status: StatusCodes.BAD_REQUEST, code: 'VALIDATION_ERROR', message: ReasonPhrases.BAD_REQUEST, - timestamp: expect.any(String) + timestamp }); }); }); @@ -230,7 +202,7 @@ describe('errorProcessor', () => { status: StatusCodes.BAD_REQUEST, code: 'BAD_REQUEST', message: 'Bad request', - timestamp: expect.any(String), + timestamp, }); }); @@ -242,7 +214,7 @@ describe('errorProcessor', () => { status: StatusCodes.BAD_REQUEST, code: 'BAD_REQUEST', message: ReasonPhrases.BAD_REQUEST, - timestamp: expect.any(String) + timestamp }); }); }); @@ -256,7 +228,7 @@ describe('errorProcessor', () => { status: 401, code: 'UNAUTHORIZED', message: 'Not logged in', - timestamp: expect.any(String) + timestamp }); }); @@ -268,7 +240,7 @@ describe('errorProcessor', () => { status: StatusCodes.UNAUTHORIZED, code: 'UNAUTHORIZED', message: ReasonPhrases.UNAUTHORIZED, - timestamp: expect.any(String) + timestamp }); }); }); @@ -282,7 +254,7 @@ describe('errorProcessor', () => { status: 403, code: 'FORBIDDEN', message: 'No access', - timestamp: expect.any(String) + timestamp }); }); @@ -293,7 +265,7 @@ describe('errorProcessor', () => { status: StatusCodes.FORBIDDEN, code: 'FORBIDDEN', message: ReasonPhrases.FORBIDDEN, - timestamp: expect.any(String) + timestamp }); }) }) @@ -307,7 +279,7 @@ describe('errorProcessor', () => { status: 404, code: 'NOT_FOUND', message: 'User not found', - timestamp: expect.any(String) + timestamp }); }); @@ -318,7 +290,7 @@ describe('errorProcessor', () => { status: StatusCodes.NOT_FOUND, code: 'NOT_FOUND', message: ReasonPhrases.NOT_FOUND, - timestamp: expect.any(String) + timestamp }); }) }); @@ -332,7 +304,7 @@ describe('errorProcessor', () => { status: 500, code: 'INTERNAL_ERROR', message: 'Internal server error', - timestamp: expect.any(String) + timestamp }); }); }) @@ -346,7 +318,7 @@ describe('errorProcessor', () => { status: 500, code: 'INTERNAL_ERROR', message: isDevelopment ? 'Standard error' : 'Internal Server Error', - timestamp: expect.any(String), + timestamp, details: isDevelopment ? { error: 'Standard error' } : undefined, stack: undefined }); @@ -361,22 +333,9 @@ describe('errorProcessor', () => { status: 503, code: 'SERVICE_UNAVAILABLE', message: isDevelopment ? 'unknown error' : 'Service Unavailable', - timestamp: expect.any(String), + timestamp, details: isDevelopment ? { error: 'unknown error' } : undefined }); }); - - it('should process unknown errors as service unavailable in development', async () => { - process.env.NODE_ENV = 'development'; - const result = await errorProcessor('unknown error', options); - - expect(result).toEqual({ - status: 503, - code: 'SERVICE_UNAVAILABLE', - message: 'unknown error', - timestamp: expect.any(String), - details: { error: 'unknown error' } - }); - }); }) });