Skip to content

Commit d46e3ca

Browse files
PAMulliganclaude
andauthored
docs(api-development): add error handling guide (#111)
Document the standard error response envelope for Nerva APIs: the error code registry and constants pattern, typed AppError classes, global handling via app.onError() and app.notFound(), per-category examples (400/401/403/404/409/429/500), and the mapping to RFC 9457 Problem Details. Update the standards README to the nested envelope shape and link the guide. Closes #56 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent cb0f219 commit d46e3ca

2 files changed

Lines changed: 589 additions & 17 deletions

File tree

docs/api-development/README.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -102,24 +102,19 @@ todosRoutes.post(
102102

103103
### Error Handling
104104

105-
Use Hono's `HTTPException` for expected errors and a global error handler for unexpected ones:
105+
Throw typed errors in handlers and let a single global handler serialize them to the [standard error envelope](./error-handling.md):
106106

107107
```typescript
108-
import { HTTPException } from "hono/http-exception";
108+
import { NotFoundError, errorHandler } from "../middleware/error-handler";
109109

110110
// In route handlers -- throw typed errors
111111
if (!todo) {
112-
throw new HTTPException(404, { message: "Todo not found" });
112+
throw new NotFoundError("Todo", id);
113113
}
114114

115-
// Global error handler in app setup
116-
app.onError((err, c) => {
117-
if (err instanceof HTTPException) {
118-
return c.json({ error: err.message }, err.status);
119-
}
120-
console.error(err);
121-
return c.json({ error: "Internal server error" }, 500);
122-
});
115+
// Global error handler in app setup -- serializes AppError, ZodError,
116+
// HTTPException, and unexpected errors to the standard envelope
117+
app.onError(errorHandler);
123118
```
124119

125120
### Context Typing
@@ -323,15 +318,17 @@ See the [CORS configuration guide](./cors-configuration.md) for environment-spec
323318

324319
### Consistent Response Format
325320

326-
All error responses follow the same shape:
321+
All error responses follow the same shape -- a single top-level `error` object:
327322

328323
```json
329324
{
330-
"error": "Validation failed",
331-
"code": "VALIDATION_ERROR",
332-
"details": [
333-
{ "field": "title", "message": "Required" }
334-
]
325+
"error": {
326+
"code": "VALIDATION_ERROR",
327+
"message": "Request validation failed",
328+
"details": [
329+
{ "field": "title", "message": "Required" }
330+
]
331+
}
335332
}
336333
```
337334

@@ -344,9 +341,12 @@ All error responses follow the same shape:
344341
| `FORBIDDEN` | 403 | Authenticated but insufficient permissions |
345342
| `NOT_FOUND` | 404 | Resource does not exist |
346343
| `CONFLICT` | 409 | Duplicate resource or state conflict |
344+
| `PAYLOAD_TOO_LARGE` | 413 | Request body exceeds the size limit |
347345
| `RATE_LIMITED` | 429 | Too many requests |
348346
| `INTERNAL_ERROR` | 500 | Unexpected server error |
349347

348+
See the [error handling guide](./error-handling.md) for the full format definition, error code constants, typed error classes, the global `app.onError()` handler, per-category examples, and how the format maps to RFC 9457 Problem Details.
349+
350350
## Performance
351351

352352
### Query Optimization

0 commit comments

Comments
 (0)