Throwable HTTP Errors
Throwable HTTP Errors is a Node.js library designed to provide a comprehensive set of strongly-typed HTTP exception classes (e.g., BadRequest, NotFound, InternalServerError) for use in API development. The library, currently at stable version 2.0.3, aims to streamline error handling by integrating naturally with modern JavaScript's `async/await` syntax and `try/catch` blocks. It differentiates itself from older patterns that relied solely on `next(error)` callbacks by promoting the throwing of specific HTTP-related errors, which can then be caught and handled with standard error flow control. This approach enhances code readability and consistency in handling HTTP-specific error conditions across an application. While a specific release cadence isn't published, the current version was released over a year ago, indicating a mature and relatively stable project. The package also ships with TypeScript type definitions, making it suitable for modern TypeScript projects.
Common errors
-
TypeError: Class constructor BadRequest cannot be invoked without 'new'
cause Attempting to call an error class directly as a function instead of instantiating it with `new`.fixAlways use the `new` keyword when creating an instance of an error class, e.g., `throw new Errors.BadRequest('Invalid data')`. -
UnhandledPromiseRejectionWarning: [ErrorName]: [Error Message]
cause An asynchronous function `throw` an `HttpError` but the calling context did not `await` the promise or failed to catch the rejection.fixEnsure all `async` functions that might throw an error are called with `await` within a `try/catch` block, or that the returned promise has a `.catch()` handler. -
Errors is not defined
cause Using `Errors` or specific error classes (e.g., `BadRequest`) without correctly importing them, especially when mixing CommonJS `require` with ESM `import` or vice-versa.fixFor CommonJS, use `const Errors = require('throwable-http-errors');`. For ESM/TypeScript, use named imports: `import { BadRequest } from 'throwable-http-errors;`. -
Property 'status' does not exist on type 'Error'.
cause Trying to access the `status` property on a generic `Error` object in TypeScript without first asserting it's an `HttpError` instance.fixIn TypeScript, use a type guard to check if the error is an instance of `HttpError` before accessing `status`: `if (err instanceof HttpError) { console.log(err.status); }`.
Warnings
- gotcha The library encourages a paradigm shift from passing errors to `next(err)` to explicitly `throw new Errors.SomeError()` within `async` functions. While `next(err)` still works, leveraging `throw` with `try/catch` is the intended modern usage for better code readability and control flow.
- gotcha When migrating from `throwable-http-errors` v1 (if it existed) to v2.x, ensure `instanceof` checks are still valid. Although no explicit breaking changes are documented for v1 to v2 API methods, major version bumps often imply changes in underlying structure or exported symbols. Always test your error handling middleware thoroughly.
- gotcha Unlike some other HTTP error libraries (e.g., `http-errors`), `throwable-http-errors` primarily provides pre-defined error classes for standard HTTP status codes (e.g., `BadRequest`, `NotFound`). While it includes a base `HttpError` class, directly creating generic errors by status code (e.g., `new Errors(400, 'message')`) is not the primary pattern.
Install
-
npm install throwable-http-errors -
yarn add throwable-http-errors -
pnpm add throwable-http-errors
Imports
- AllErrors (CommonJS)
import Errors from 'throwable-http-errors';
const Errors = require('throwable-http-errors'); - SpecificError (ESM/TypeScript)
import Errors from 'throwable-http-errors'; const { BadRequest } = Errors;import { BadRequest, InternalServerError } from 'throwable-http-errors'; - BaseHttpError (TypeScript)
import { HttpError } from 'throwable-http-errors';
Quickstart
import express from 'express';
import { BadRequest, NotFound, InternalServerError } from 'throwable-http-errors';
const app = express();
app.use(express.json());
interface Pet { id: string; name: string; type: string; }
// Dummy database and controller
const pets: Pet[] = [{ id: '1', name: 'Buddy', type: 'Dog' }];
const PetsController = {
create: async (data: any): Promise<Pet> => {
if (!data || !data.name || !data.type) {
throw new BadRequest('Pet name and type are required.');
}
const newPet: Pet = { id: String(pets.length + 1), ...data };
pets.push(newPet);
return newPet;
},
getById: async (id: string): Promise<Pet> => {
const pet = pets.find(p => p.id === id);
if (!pet) {
throw new NotFound(`Pet with ID ${id} not found.`);
}
return pet;
}
};
app.post('/pets', async (req, res, next) => {
try {
const pet = await PetsController.create(req.body);
res.status(201).send({ status: true, data: pet });
} catch (e) {
next(e); // Pass error to the global error handler
}
});
app.get('/pets/:id', async (req, res, next) => {
try {
const pet = await PetsController.getById(req.params.id);
res.status(200).send({ status: true, data: pet });
} catch (e) {
next(e);
}
});
// Global error handling middleware
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
// All throwable-http-errors instances have a 'status' property
if (err.status) {
return res.status(err.status).json({ status: false, message: err.message });
} else {
console.error(err);
return res.status(500).json({ status: false, message: 'Internal Server Error' });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
console.log('Try: POST /pets with { "name": "Fluffy", "type": "Cat" }');
console.log('Then: GET /pets/1');
});