UNPKG

@ingeze/api-error

Version:

A TypeScript library for handling HTTP errors in Express, NestJS, and Fastify APIs.

400 lines (313 loc) โ€ข 9.95 kB
# @ingeze/api-error Add commentMore actions [![CI](https://github.com/ingeze/api-error/actions/workflows/nodejs.yml/badge.svg)](https://github.com/ingeze/api-error/actions/workflows/nodejs.yml) [![npm version](https://img.shields.io/npm/v/@ingeze/api-error)](https://www.npmjs.com/package/@ingeze/api-error) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) A comprehensive TypeScript library for **consistent HTTP error handling** across Express, NestJS, and Fastify APIs. Built with developer experience in mind, providing type-safe error classes and framework-specific middleware for seamless API error management. ## โœจ Features - ๐ŸŽฏ **Pre-built error classes** for common HTTP status codes (400, 401, 403, 404, 422, etc.) - ๐Ÿ”ง **Framework integrations** - Ready-to-use middleware for Express, NestJS, and Fastify - ๐Ÿ›ก๏ธ **Type-safe** - Full TypeScript support with detailed error types - ๐Ÿ“ฆ **Consistent responses** - Standardized error response format across your API - ๐ŸŽจ **Customizable** - Optional details object for additional error context - ๐Ÿ—๏ธ **Extensible** - Create custom error classes with the factory function - ๐Ÿ“ **Well-tested** - Every function and class is thoroughly tested ## ๐Ÿ“ฆ Installation ```bash npm install @ingeze/api-error ``` ```bash yarn add @ingeze/api-error ``` ```bash pnpm add @ingeze/api-error ``` ## ๐Ÿ“ฆ Subpath Exports This package uses [subpath exports](https://nodejs.org/api/packages.html#subpath-exports) to expose integrations for different frameworks (Express, Nest, Fastify, etc.). ### Example usage ```ts import { expressErrorMiddleware } from '@ingeze/api-error/express' // or import { NestErrorFilter, NestGenericErrorFilter } from '@ingeze/api-error/nest' // or import { fastifyErrorMiddleware } from '@ingeze/api-error/fastify' ``` > This lets you import only what you need, without loading unnecessary code. ### โš ๏ธ Required TypeScript Configuration To make TypeScript properly recognize subpath exports (like @ingeze/api-error/express), you must use a modern module resolution strategy. Make sure your tsconfig.json includes: ```tsconfig.json { "compilerOptions": { "moduleResolution": "node16", // or "nodenext" if using ESM "module": "esnext", // or "commonjs" if using require "target": "es2020", "esModuleInterop": true } } ``` > Starting from TypeScript 4.7+, *node16* and *nodenext* support the *exports* field in *package.json*. ### โœ… Minimum Requirements - Node.js โ‰ฅ 16 - TypeScript โ‰ฅ 4.7 - Modern bundlers like esbuild, tsup, vite, etc. ## ๐Ÿš€ Quick Start ### Basic Usage ```typescript import { BadRequestError, NotFoundError } from '@ingeze/api-error' // Simple error throwing throw new BadRequestError() // With custom details throw new NotFoundError({ resource: 'user', id: '12345' }) // Response format: { "success": false, "type": "NOT_FOUND_USER", "statusCode": 404, "message": "User not found", "details": { "resource": "user", "id": "12345" } } ``` ### Express Integration ```typescript import express from 'express' import { expressErrorMiddleware } from '@ingeze/api-error/express' import { UserNotFoundError } from '@ingeze/api-error' const app = express() // Your routes app.get('/users/:id', async (req, res) => { const user = await findUser(req.params.id) if (!user) { throw new UserNotFoundError({ userId: req.params.id }) } res.json(user) }) // Error handling middleware (must be last) app.use(expressErrorMiddleware) ``` ### NestJS Integration ```typescript import { Module } from '@nestjs/common' import { APP_FILTER } from '@nestjs/core' import { NestErrorFilter, NestGenericErrorFilter } from '@ingeze/api-error/nest' @Module({ providers: [ { provide: APP_FILTER, useClass: NestErrorFilter, }, { provide: APP_FILTER, useClass: NestGenericErrorFilter, }, ], }) export class AppModule {} ``` ### Fastify Integration ```typescript import fastify from 'fastify' import { fastifyErrorMiddleware } from '@ingeze/api-error/fastify' const app = fastify() // Set the error handler app.setErrorHandler(fastifyErrorMiddleware) ``` ## ๐Ÿ“š Available Error Classes ### ๐Ÿ”ด Bad Request Errors (400) ```typescript import { BadRequestError, InvalidUserDataError, InvalidEmailError, InvalidProductDataError, InvalidPostData, InvalidCommentDataError, InvalidCategoryDataError, InvalidFileError, InvalidImageError, InvalidAddressError } from '@ingeze/api-error' ``` ### ๐Ÿ” Unauthorized Errors (401) ```typescript import { UnauthorizedError, InvalidTokenError, InvalidCredentialsError, AccessTokenError, RefreshTokenError, APIKeyError, UnauthorizedDeviceError } from '@ingeze/api-error' ``` ### ๐Ÿšซ Forbidden Errors (403) ```typescript import { ForbiddenError, ForbiddenUserError, ForbiddenEmailError, ForbiddenProductError, ForbiddenPostError, ForbiddenCommentError, ForbiddenCategoryError, ForbiddenFileError, ForbiddenImageError, ForbiddenAddressError } from '@ingeze/api-error' ``` ### ๐Ÿ” Not Found Errors (404) ```typescript import { NotFoundError, UserNotFoundError, EmailNotFoundError, ProductNotFoundError, PostNotFoundError, CommentNotFoundError, CategoryNotFoundError, FileNotFoundError, ImageNotFoundError, AddressNotFoundError } from '@ingeze/api-error' ``` ### โœ… Validation Errors (422) ```typescript import { ValidationError, ValidationUserError, ValidationEmailError, ValidationProductError, ValidationPostError, ValidationCommentError, ValidationCategoryError, ValidationFileError, ValidationImageError, ValidationAddressError } from '@ingeze/api-error' ``` ## ๐ŸŽจ Custom Error Creation Create your own error classes with the factory function: ```typescript import { createHandleError } from '@ingeze/api-error' // Define error types type PaymentErrorType = 'PAYMENT_FAILED' | 'PAYMENT_DECLINED' | 'INSUFFICIENT_FUNDS' // Create custom error class const PaymentError = createHandleError<PaymentErrorType>({ name: 'PaymentError', statusCode: 402, defaultType: 'PAYMENT_FAILED', defaultMessage: 'Payment could not be processed' }) // Usage throw new PaymentError() throw new PaymentError('Card declined', 'PAYMENT_DECLINED', { cardLast4: '4242', retryAllowed: false }) ``` ## ๐Ÿ”ง Advanced Usage ### Custom Error Handler ```typescript import { ErrorHandler } from '@ingeze/api-error' throw new ErrorHandler( 'Custom error message', 418, // I'm a teapot 'CUSTOM_TYPE', { customField: 'value' } ) ``` ### Error Response Format All errors follow this consistent structure: ```typescript interface ErrorResponse { success: boolean // Always false type: string // Error type identifier statusCode: number // HTTP status code message: string // Human-readable message details?: object // Optional additional context } ``` ## ๐Ÿ—๏ธ Framework-Specific Examples ### Express with Async/Await ```typescript import express from 'express' import { UserNotFoundError, ValidationUserError } from '@ingeze/api-error' import { expressErrorMiddleware } from '@ingeze/api-error/express' const app = express() app.post('/users', async (req, res) => { const { email, name } = req.body // Validate required fields const missing = []; if (!email) missing.push("Email can't be empty"); if (!name) missing.push("Name can't be empty"); if (missing.length) { throw new ValidationUserError({ missing: missing.join('. '), missing }); } // Check if user already exists const existingUser = await findUserByEmail(email) if (existingUser) { throw new ValidationUserError({ field: 'email', reason: 'already exists' }) } // Create and return the new user const user = await createUser({ email, name }) res.status(201).json(user) }) // Global error handler app.use(expressErrorMiddleware) ``` ### NestJS Service ```typescript import { Injectable } from '@nestjs/common' import { UserNotFoundError, ValidationUserError } from '@ingeze/api-error' @Injectable() export class UserService { async findUser(id: string) { const user = await this.userRepository.findById(id) if (!user) { throw new UserNotFoundError({ userId: id }) } return user } async updateUser(id: string, data: UpdateUserDto) { if (!data.email && !data.name) { throw new ValidationUserError({ message: 'At least one field must be provided' }) } const user = await this.findUser(id) // Will throw if not found return this.userRepository.update(id, data) } } ``` ## ๐Ÿงช Testing The library is thoroughly tested with comprehensive test coverage: ```bash npm test ``` ## Changelog For a detailed list of changes and version history, please see the [Changelog](./CHANGELOG.md) file. This project follows [Semantic Versioning](https://semver.org/) and documents all notable updates in the changelog to help you stay informed about new features, fixes, and improvements. ## ๐Ÿ“„ License MIT ยฉ [ingEze](https://github.com/ingEze) ## ๐Ÿค Contributing Contributions are welcome! Please feel free to submit a Pull Request. ## ๐Ÿ“ž Support - ๐Ÿ› [Report bugs](https://github.com/ingeze/api-error/issues) - ๐Ÿ’ก [Request features](https://github.com/ingeze/api-error/issues) - ๐Ÿ–ฅ [Documentation Web](https://ingeze.github.io/api-error) - ๐Ÿ‘‰ [Documentation DeepWiki](https://deepwiki.com/ingEze/api-error) - ๐Ÿ“– [Documentation](https://github.com/ingeze/api-error#readme) ### ๐Ÿ“ฌ Contact Me - ๐Ÿ“ง **Email**: [ezequielrodrigosaucedo@gmail.com](mailto:ezequielrodrigosaucedo@gmail.com) - ๐Ÿ’ผ **LinkedIn**: [linkedin.com/in/ezequiel-rodrigo-saucedo](https://www.linkedin.com/in/ezequiel-rodrigo-saucedo-50451a294) --- **Built with โค๏ธ by the Ingeze**