diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3c43494 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Junjie Wu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index e69de29..064b267 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,124 @@ +# handle-http-errors + +HTTP error handling library with TypeScript support, providing error classes, handlers, and middleware support. + +## ☘️ Features + +- 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 + +## 📥 Installation + +```bash +npm install handle-http-errors +# or +yarn add handle-http-errors +# or +pnpm add handle-http-errors +``` + +## 📖 Usage + +### 🔧 Error Handler + +```typescript +import express from 'express'; +import { errorHandler, ValidationError } from 'handle-http-errors'; + +const app = express(); + +app.post('/users', async (req, res) => { + try { + const { email } = req.body; + if (!email) { + throw new ValidationError('Email is required'); + } + } catch (error) { + return errorHandler(error, res); + } +}); +``` + +### 🌐 Middleware + +```typescript +import express from 'express'; +import { errorMiddleware, NotFoundError } from 'handle-http-errors'; + +const app = express(); + +app.get('/users/:id', (req, res, next) => { + try { + throw new NotFoundError('User not found', { id: req.params.id }); + } catch (error) { + next(error); + } +}); + +app.use(errorMiddleware()); +``` + +## 🗂️ 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 +} from 'handle-http-errors'; +``` + +## 📋 Error Response Format + +```typescript +{ + status: number; // HTTP status code + code: string; // Error code (e.g., 'VALIDATION_ERROR') + message: string; // Error message + timestamp: string; // ISO timestamp + details?: object; // Optional error details + stack?: string; // Stack trace (development only) +} +``` + +## ⚙️ Configuration + +```typescript +// Middleware options +interface ErrorHandlerOptions { + includeStack?: boolean; // Include stack traces + onError?: (error: unknown) => void; // Error callback +} + +// Using with options +app.use(errorMiddleware({ + includeStack: process.env.NODE_ENV !== 'production', + onError: (error) => console.error(error) +})); +``` + +## 🔍 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 new file mode 100644 index 0000000..2bd311f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,144 @@ +# handle-http-errors Examples + +Examples of using [handle-http-errors](https://www.npmjs.com/package/handle-http-errors) with different approaches. + +## 🚀 Running 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) +``` + +### 🔧 Error Handler Example (Port 3001) + +```bash +npm run dev:handler +``` + +Test endpoints: + +```bash +# Test NotFoundError +curl http://localhost:3001/with-handler + +# Test ValidationError with invalid product ID +curl http://localhost:3001/products/abc + +# Test NotFoundError with valid product ID +curl http://localhost:3001/products/123 + +# Test ValidationError with invalid product data +curl -X POST \ + -H "Content-Type: application/json" \ + -d '{"name":"","price":-10}' \ + http://localhost:3001/products + +# Test InternalServerError with valid product data +curl -X POST \ + -H "Content-Type: application/json" \ + -d '{"name":"Test Product","price":10}' \ + http://localhost:3001/products +``` + +### 🌐 Middleware Example (Port 3002) + +```bash +npm run dev:middleware +``` + +Test endpoints: + +```bash +# Test NotFoundError +curl http://localhost:3002/with-middleware + +# Test ValidationError with invalid user ID +curl http://localhost:3002/users/abc + +# Test NotFoundError with valid user ID +curl http://localhost:3002/users/123 + +# Test ValidationError with missing data +curl -X POST \ + -H "Content-Type: application/json" \ + -d '{}' \ + http://localhost:3002/users/register + +# Test ValidationError with invalid email +curl -X POST \ + -H "Content-Type: application/json" \ + -d '{"email":"invalid-email","username":"test"}' \ + http://localhost:3002/users/register + +# Test ValidationError with short username +curl -X POST \ + -H "Content-Type: application/json" \ + -d '{"email":"test@example.com","username":"a"}' \ + http://localhost:3002/users/register + +# Test InternalServerError with valid data +curl -X POST \ + -H "Content-Type: application/json" \ + -d '{"email":"test@example.com","username":"testuser"}' \ + http://localhost:3002/users/register +``` + +### 🛠️ Custom Middleware Example (Port 3003) + +```bash +npm run dev:custom-middleware +``` + +Test endpoints: + +```bash +# Test UnauthorizedError without token +curl http://localhost:3003/users/123 + +# Test UnauthorizedError with invalid token +curl -H "Authorization: invalid-token" \ + http://localhost:3003/users/123 + +# Test ValidationError with valid token but invalid ID +curl -H "Authorization: valid-token" \ + http://localhost:3003/users/abc + +# Test NotFoundError with valid token and valid ID +curl -H "Authorization: valid-token" \ + http://localhost:3003/users/123 + +# Test UnauthorizedError in registration +curl -X POST \ + -H "Content-Type: application/json" \ + -d '{"email":"test@example.com","username":"test"}' \ + http://localhost:3003/users/register + +# Test ValidationError in registration +curl -X POST \ + -H "Authorization: valid-token" \ + -H "Content-Type: application/json" \ + -d '{"email":"invalid-email","username":"a"}' \ + http://localhost:3003/users/register + +# Test ForbiddenError when creating admin +curl -X POST \ + -H "Authorization: valid-token" \ + -H "Content-Type: application/json" \ + -d '{"email":"admin@example.com","username":"admin"}' \ + "http://localhost:3003/users/register?role=admin" + +# Test successful registration +curl -X POST \ + -H "Authorization: valid-token" \ + -H "Content-Type: application/json" \ + -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 67da87a..27ded2c 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -8,7 +8,7 @@ "name": "express-typescript-example", "dependencies": { "express": "^4.18.2", - "http-error-handler": "file:../" + "handle-http-errors": "file:../" }, "devDependencies": { "@types/express": "^4.17.17", @@ -17,15 +17,18 @@ } }, "..": { - "name": "http-error-handler", + "name": "handle-http-errors", "version": "1.0.0", - "license": "ISC", + "license": "MIT", "dependencies": { "http-status-codes": "^2.3.0" }, "devDependencies": { "@commitlint/cli": "^19.6.1", "@commitlint/config-conventional": "^19.6.0", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/git": "^10.0.1", + "@semantic-release/release-notes-generator": "^14.0.2", "@types/express": "^5.0.0", "@types/jest": "^29.5.14", "@types/node": "^22.10.2", @@ -36,6 +39,7 @@ "husky": "^9.1.7", "jest": "^29.7.0", "lint-staged": "^15.2.10", + "semantic-release": "^24.2.0", "ts-jest": "^29.2.5", "typescript": "^5.7.2" } @@ -562,6 +566,10 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/handle-http-errors": { + "resolved": "..", + "link": true + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -584,10 +592,6 @@ "node": ">= 0.4" } }, - "node_modules/http-error-handler": { - "resolved": "..", - "link": true - }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", diff --git a/examples/package.json b/examples/package.json index aafc119..46776a3 100644 --- a/examples/package.json +++ b/examples/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "express": "^4.18.2", - "http-error-handler": "file:../" + "handle-http-errors": "file:../" }, "devDependencies": { "@types/express": "^4.17.17", diff --git a/examples/src/withCustomMiddleware.js b/examples/src/withCustomMiddleware.js index 1b503d2..7b5b5ef 100644 --- a/examples/src/withCustomMiddleware.js +++ b/examples/src/withCustomMiddleware.js @@ -5,7 +5,7 @@ import { ForbiddenError, ValidationError, NotFoundError, -} from "http-error-handler"; +} from "handle-http-errors"; const app = express(); app.use(express.json()); diff --git a/examples/src/withHandler.js b/examples/src/withHandler.js index 8a76dbe..20a6a13 100644 --- a/examples/src/withHandler.js +++ b/examples/src/withHandler.js @@ -3,7 +3,7 @@ import { errorHandler, NotFoundError, ValidationError, -} from "http-error-handler"; +} from "handle-http-errors"; const app = express(); app.use(express.json()); diff --git a/examples/src/withMiddleware.js b/examples/src/withMiddleware.js index 9cb0a33..ae54a62 100644 --- a/examples/src/withMiddleware.js +++ b/examples/src/withMiddleware.js @@ -3,7 +3,7 @@ import { errorMiddleware, NotFoundError, ValidationError, -} from "http-error-handler"; +} from "handle-http-errors"; const app = express(); app.use(express.json()); diff --git a/package.json b/package.json index 6cd9bf8..5f71105 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,9 @@ { - "name": "http-error-handler", + "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.", + "author": "Junjie Wu", + "license": "MIT", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -8,32 +11,42 @@ "dist", "README.md" ], + "repository": { + "type": "git", + "url": "git+https://github.com/junjie-w/handle-http-errors.git" + }, + "homepage": "https://github.com/junjie-w/handle-http-errors#readme", + "bugs": { + "url": "https://github.com/junjie-w/handle-http-errors/issues" + }, "publishConfig": { "access": "public" }, - "description": "", "scripts": { + "prebuild": "rm -rf dist", "build": "tsc", + "build:watch": "tsc -w", "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage", "lint": "eslint .", "lint:fix": "eslint . --fix", + "prepublishOnly": "npm run build", "prepare": "husky install" }, "keywords": [ - "typescript", "error-handling", + "http-errors", + "http-error-handler", "middleware", - "express", + "typescript", "node", - "http-errors", - "error-handling-middleware", - "express-middleware", - "express-error-handler" + "express", + "error-response" ], - "author": "", - "license": "ISC", + "dependencies": { + "http-status-codes": "^2.3.0" + }, "devDependencies": { "@commitlint/cli": "^19.6.1", "@commitlint/config-conventional": "^19.6.0", @@ -53,8 +66,5 @@ "semantic-release": "^24.2.0", "ts-jest": "^29.2.5", "typescript": "^5.7.2" - }, - "dependencies": { - "http-status-codes": "^2.3.0" } }