Skip to content

Commit c46c2dc

Browse files
committed
add readme
1 parent 325e177 commit c46c2dc

File tree

1 file changed

+387
-0
lines changed

1 file changed

+387
-0
lines changed

README.md

Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
# 🚨 Elysia HTTP Exception
2+
3+
[![npm version](https://img.shields.io/npm/v/elysia-http-exception.svg?style=flat-square)](https://www.npmjs.com/package/elysia-http-exception)
4+
[![npm downloads](https://img.shields.io/npm/dm/elysia-http-exception.svg?style=flat-square)](https://www.npmjs.com/package/elysia-http-exception)
5+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
6+
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg?style=flat-square)](https://www.typescriptlang.org/)
7+
8+
A comprehensive Elysia plugin for handling HTTP 4xx and 5xx errors with structured exception classes and automatic error responses. This plugin provides a clean, type-safe way to handle HTTP exceptions in your Elysia applications.
9+
10+
## ✨ Features
11+
12+
- 🎯 **Complete HTTP Status Coverage** - Support for all standard 4xx and 5xx HTTP status codes
13+
- 🔧 **Type-Safe** - Full TypeScript support with proper type definitions
14+
- 🚀 **Easy Integration** - Simple plugin installation with zero configuration required
15+
- 🎨 **Flexible Error Data** - Support for string messages, objects, and Error instances
16+
- 🔄 **Two Usage Patterns** - Use either `throw` statements or the `httpException` decorator
17+
- 🛡️ **Automatic Error Handling** - Built-in error handler for common Elysia errors (PARSE, VALIDATION, NOT_FOUND, etc.)
18+
- 📦 **Lightweight** - Minimal overhead with tree-shakable exports
19+
- 🧪 **Well Tested** - Comprehensive test coverage for reliability
20+
21+
## 📦 Installation
22+
23+
```bash
24+
bun add elysia-http-exception
25+
```
26+
27+
## 🚀 Quick Start
28+
29+
### Basic Setup
30+
31+
```typescript
32+
import { Elysia } from 'elysia';
33+
import { httpExceptionPlugin, NotFoundException, BadRequestException } from 'elysia-http-exception';
34+
35+
const app = new Elysia()
36+
.use(httpExceptionPlugin())
37+
.get('/users/:id', ({ params }) => {
38+
const userId = parseInt(params.id);
39+
40+
if (isNaN(userId)) {
41+
throw new BadRequestException('User ID must be a valid number');
42+
}
43+
44+
if (userId === 404) {
45+
throw new NotFoundException(`User with ID ${userId} not found`);
46+
}
47+
48+
return { id: userId, name: `User ${userId}`, email: `user${userId}@example.com` };
49+
})
50+
.listen(3000);
51+
```
52+
53+
### Using the Decorator Pattern
54+
55+
```typescript
56+
import { Elysia } from 'elysia';
57+
import { httpExceptionPlugin, NotFoundException, BadRequestException } from 'elysia-http-exception';
58+
59+
const app = new Elysia()
60+
.use(httpExceptionPlugin())
61+
.get('/users/:id', ({ params, httpException }) => {
62+
const userId = parseInt(params.id);
63+
64+
if (isNaN(userId)) {
65+
return httpException(new BadRequestException('User ID must be a valid number'));
66+
}
67+
68+
if (userId === 404) {
69+
return httpException(new NotFoundException(`User with ID ${userId} not found`));
70+
}
71+
72+
return { id: userId, name: `User ${userId}`, email: `user${userId}@example.com` };
73+
})
74+
.listen(3000);
75+
```
76+
77+
## 📚 Available Exception Classes
78+
79+
### 4xx Client Error Exceptions
80+
81+
| Exception Class | Status Code | Description |
82+
|---|---|---|
83+
| `BadRequestException` | 400 | Bad Request |
84+
| `UnauthorizedException` | 401 | Unauthorized |
85+
| `PaymentRequiredException` | 402 | Payment Required |
86+
| `ForbiddenException` | 403 | Forbidden |
87+
| `NotFoundException` | 404 | Not Found |
88+
| `MethodNotAllowedException` | 405 | Method Not Allowed |
89+
| `NotAcceptableException` | 406 | Not Acceptable |
90+
| `RequestTimeoutException` | 408 | Request Timeout |
91+
| `ConflictException` | 409 | Conflict |
92+
| `GoneException` | 410 | Gone |
93+
| `LengthRequiredException` | 411 | Length Required |
94+
| `PreconditionFailedException` | 412 | Precondition Failed |
95+
| `PayloadTooLargeException` | 413 | Payload Too Large |
96+
| `UriTooLongException` | 414 | URI Too Long |
97+
| `UnsupportedMediaTypeException` | 415 | Unsupported Media Type |
98+
| `RangeNotSatisfiableException` | 416 | Range Not Satisfiable |
99+
| `ExpectationFailedException` | 417 | Expectation Failed |
100+
| `ImATeapotException` | 418 | I'm a teapot |
101+
| `MisdirectedRequestException` | 421 | Misdirected Request |
102+
| `UnprocessableEntityException` | 422 | Unprocessable Entity |
103+
| `LockedException` | 423 | Locked |
104+
| `FailedDependencyException` | 424 | Failed Dependency |
105+
| `TooEarlyException` | 425 | Too Early |
106+
| `UpgradeRequiredException` | 426 | Upgrade Required |
107+
| `PreconditionRequiredException` | 428 | Precondition Required |
108+
| `TooManyRequestsException` | 429 | Too Many Requests |
109+
| `RequestHeaderFieldsTooLargeException` | 431 | Request Header Fields Too Large |
110+
| `UnavailableForLegalReasonsException` | 451 | Unavailable For Legal Reasons |
111+
112+
### 5xx Server Error Exceptions
113+
114+
| Exception Class | Status Code | Description |
115+
|---|---|---|
116+
| `InternalServerErrorException` | 500 | Internal Server Error |
117+
| `NotImplementedException` | 501 | Not Implemented |
118+
| `BadGatewayException` | 502 | Bad Gateway |
119+
| `ServiceUnavailableException` | 503 | Service Unavailable |
120+
| `GatewayTimeoutException` | 504 | Gateway Timeout |
121+
| `HttpVersionNotSupportedException` | 505 | HTTP Version Not Supported |
122+
| `VariantAlsoNegotiatesException` | 506 | Variant Also Negotiates |
123+
| `InsufficientStorageException` | 507 | Insufficient Storage |
124+
| `LoopDetectedException` | 508 | Loop Detected |
125+
| `NotExtendedException` | 510 | Not Extended |
126+
| `NetworkAuthenticationRequiredException` | 511 | Network Authentication Required |
127+
128+
## 💡 Usage Examples
129+
130+
### 1. Simple String Messages
131+
132+
```typescript
133+
app.get('/simple', () => {
134+
throw new BadRequestException('Invalid input provided');
135+
});
136+
```
137+
138+
**Response:**
139+
```json
140+
{
141+
"statusCode": 400,
142+
"message": "Invalid input provided"
143+
}
144+
```
145+
146+
### 2. Custom Object Data
147+
148+
```typescript
149+
app.post('/validate', ({ body }) => {
150+
throw new UnprocessableEntityException({
151+
error: 'VALIDATION_FAILED',
152+
details: {
153+
field: 'email',
154+
message: 'Invalid email format'
155+
},
156+
timestamp: new Date().toISOString()
157+
});
158+
});
159+
```
160+
161+
**Response:**
162+
```json
163+
{
164+
"error": "VALIDATION_FAILED",
165+
"details": {
166+
"field": "email",
167+
"message": "Invalid email format"
168+
},
169+
"timestamp": "2024-01-15T10:30:00.000Z"
170+
}
171+
```
172+
173+
### 3. Error Object Handling
174+
175+
```typescript
176+
app.get('/error-object', () => {
177+
const validationError = new Error('Database validation failed');
178+
throw new InternalServerErrorException(validationError);
179+
});
180+
```
181+
182+
**Response:**
183+
```json
184+
{
185+
"statusCode": 500,
186+
"message": "Database validation failed"
187+
}
188+
```
189+
190+
### 4. Rate Limiting Example
191+
192+
```typescript
193+
app.get('/api/data', () => {
194+
const rateLimitExceeded = checkRateLimit(); // Your rate limit logic
195+
196+
if (rateLimitExceeded) {
197+
throw new TooManyRequestsException({
198+
message: 'Rate limit exceeded',
199+
retryAfter: 60,
200+
limit: 100,
201+
remaining: 0
202+
});
203+
}
204+
205+
return { data: 'Your API data' };
206+
});
207+
```
208+
209+
### 5. Authentication Example
210+
211+
```typescript
212+
app.get('/profile', ({ headers }) => {
213+
const authHeader = headers['authorization'];
214+
215+
if (!authHeader) {
216+
throw new UnauthorizedException({
217+
error: 'MISSING_AUTH_HEADER',
218+
message: 'Authorization header is required',
219+
requiredFormat: 'Bearer <token>'
220+
});
221+
}
222+
223+
if (!authHeader.startsWith('Bearer ')) {
224+
throw new UnauthorizedException('Invalid authorization format');
225+
}
226+
227+
return { user: { id: 1, name: 'John Doe' } };
228+
});
229+
```
230+
231+
### 6. File Upload Example
232+
233+
```typescript
234+
app.post('/upload', ({ request }) => {
235+
const contentLength = parseInt(request.headers.get('content-length') || '0');
236+
const maxSize = 5 * 1024 * 1024; // 5MB
237+
238+
if (contentLength > maxSize) {
239+
throw new PayloadTooLargeException({
240+
error: 'FILE_TOO_LARGE',
241+
message: 'File size exceeds maximum allowed size',
242+
maxSize: `${maxSize / 1024 / 1024}MB`,
243+
receivedSize: `${(contentLength / 1024 / 1024).toFixed(2)}MB`
244+
});
245+
}
246+
247+
return { message: 'Upload successful' };
248+
});
249+
```
250+
251+
### 7. E-commerce Example
252+
253+
```typescript
254+
app.post('/checkout', ({ body }) => {
255+
const data = body as any;
256+
257+
if (!data.items?.length) {
258+
throw new BadRequestException({
259+
error: 'EMPTY_CART',
260+
message: 'Cart cannot be empty for checkout'
261+
});
262+
}
263+
264+
const outOfStockItem = data.items.find((item: any) => !item.inStock);
265+
if (outOfStockItem) {
266+
throw new ConflictException({
267+
error: 'ITEM_OUT_OF_STOCK',
268+
message: 'Some items in your cart are no longer available',
269+
unavailableItems: [outOfStockItem]
270+
});
271+
}
272+
273+
return { message: 'Checkout successful', orderId: 'order-123' };
274+
});
275+
```
276+
277+
## 🔧 Built-in Error Handling
278+
279+
The plugin automatically handles common Elysia errors:
280+
281+
- **PARSE**: JSON parsing errors → 400 Bad Request
282+
- **VALIDATION**: Schema validation errors → 400 Bad Request
283+
- **NOT_FOUND**: Route not found → 404 Not Found
284+
- **INVALID_COOKIE_SIGNATURE**: Invalid cookies → 400 Bad Request
285+
- **INVALID_FILE_TYPE**: Unsupported file types → 415 Unsupported Media Type
286+
287+
## 🏗️ API Reference
288+
289+
### `httpExceptionPlugin()`
290+
291+
The main plugin function that adds HTTP exception handling to your Elysia app.
292+
293+
```typescript
294+
import { httpExceptionPlugin } from 'elysia-http-exception';
295+
296+
const app = new Elysia().use(httpExceptionPlugin());
297+
```
298+
299+
### `HttpException` Base Class
300+
301+
All exception classes extend from the base `HttpException` class:
302+
303+
```typescript
304+
class HttpException extends Error {
305+
public readonly statusCode: number;
306+
public readonly code: string;
307+
public readonly data?: unknown;
308+
public readonly isHttpException = true;
309+
310+
constructor(httpError: HttpError, message?: string | object | Error | unknown);
311+
toBody(): unknown;
312+
}
313+
```
314+
315+
### Exception Constructor Parameters
316+
317+
Each exception class accepts an optional message parameter:
318+
319+
- **string**: Simple error message
320+
- **object**: Custom error data (returned as-is in response)
321+
- **Error**: Error instance (uses error.message)
322+
- **undefined**: Uses default message for the status code
323+
324+
## 🧪 Testing
325+
326+
The plugin includes comprehensive tests. Run them with:
327+
328+
```bash
329+
# Run all tests
330+
bun test
331+
332+
# Run unit tests only
333+
bun test:unit
334+
335+
# Run e2e tests only
336+
bun test:e2e
337+
338+
# Run tests with coverage
339+
bun test:coverage
340+
341+
# Watch mode
342+
bun test:watch
343+
```
344+
345+
## 📋 Examples
346+
347+
Check out the comprehensive examples in the `/example` directory:
348+
349+
- **example-throw.ts**: Demonstrates using `throw` statements
350+
- **example-decorator.ts**: Demonstrates using the `httpException` decorator
351+
352+
Run the examples:
353+
354+
```bash
355+
cd example
356+
bun run example-throw.ts # Server on http://localhost:3000
357+
bun run example-decorator.ts # Server on http://localhost:3001
358+
```
359+
360+
## 🤝 Contributing
361+
362+
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
363+
364+
1. Fork the repository
365+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
366+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
367+
4. Push to the branch (`git push origin feature/amazing-feature`)
368+
5. Open a Pull Request
369+
370+
## 📄 License
371+
372+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
373+
374+
## 🙏 Acknowledgments
375+
376+
- [Elysia](https://elysiajs.com/) - The fast and friendly Bun web framework
377+
- [HTTP Status Codes](https://httpstatuses.com/) - For comprehensive status code reference
378+
- The Bun and TypeScript communities for their excellent tooling
379+
380+
## 🔗 Links
381+
382+
- [GitHub Repository](https://github.com/codev911/elysia-http-exception)
383+
- [npm Package](https://www.npmjs.com/package/elysia-http-exception)
384+
- [Elysia Documentation](https://elysiajs.com/introduction.html)
385+
- [TypeScript Documentation](https://www.typescriptlang.org/docs/)
386+
387+
---

0 commit comments

Comments
 (0)