p2f: add Error Handling tutorial — HTTPException, custom handlers, validation errors, error format reference
swadhinbiswas committed 3 months ago
312a9f4
1 parent 9776b10
commit 312a9f4
1 file changed
+148 -0
Showing 1 changed file+148-0
▼docs_site/src/content/docs/tutorials/error-handling.md
+148-0
| @@ -0,0 +1,148 @@ | ||
1 | +--- | |
2 | +title: Error Handling | |
3 | +description: Handle errors gracefully with HTTPException, custom exception handlers, and validation error responses. | |
4 | +--- | |
5 | + | |
6 | +JustAPI provides a structured error-handling system that lets you return consistent error responses across your API. | |
7 | + | |
8 | +## Raising HTTP Errors | |
9 | + | |
10 | +Use `HTTPException` to return error responses from any handler or dependency: | |
11 | + | |
12 | +```python | |
13 | +from justapi import JustAPIApp, HTTPException | |
14 | + | |
15 | +app = JustAPIApp() | |
16 | + | |
17 | + | |
18 | +@app.get("/items/{item_id}") | |
19 | +def read_item(request, item_id: int): | |
20 | + if item_id == 0: | |
21 | + raise HTTPException(status_code=400, detail="Item ID must be positive") | |
22 | + if item_id == 42: | |
23 | + raise HTTPException( | |
24 | + status_code=418, | |
25 | + detail="I'm a teapot", | |
26 | + headers={"X-Error": "teapot"}, | |
27 | + ) | |
28 | + return {"item_id": item_id} | |
29 | +``` | |
30 | + | |
31 | +```bash | |
32 | +curl http://127.0.0.1:8000/items/0 | |
33 | +# Output: {"detail":"Item ID must be positive"} | |
34 | + | |
35 | +curl http://127.0.0.1:8000/items/42 | |
36 | +# Output: {"detail":"I'm a teapot"} | |
37 | +``` | |
38 | + | |
39 | +### `HTTPException` Parameters | |
40 | + | |
41 | +| Parameter | Type | Default | Description | | |
42 | +|---|---|---|---| | |
43 | +| `status_code` | int | required | HTTP status code | | |
44 | +| `detail` | str | required | Error message | | |
45 | +| `headers` | dict | `None` | Additional response headers | | |
46 | + | |
47 | +## Custom Exception Handlers | |
48 | + | |
49 | +Override the default error format for specific exception types: | |
50 | + | |
51 | +```python | |
52 | +from justapi import RequestValidationError | |
53 | + | |
54 | + | |
55 | +@app.exception_handler(HTTPException) | |
56 | +def http_exception_handler(request, exc: HTTPException): | |
57 | + return { | |
58 | + "status": exc.status_code, | |
59 | + "body": { | |
60 | + "error": "Request Failed", | |
61 | + "code": exc.status_code, | |
62 | + "message": exc.detail, | |
63 | + }, | |
64 | + } | |
65 | + | |
66 | + | |
67 | +@app.exception_handler(RequestValidationError) | |
68 | +def validation_handler(request, exc: RequestValidationError): | |
69 | + return { | |
70 | + "status": 422, | |
71 | + "body": { | |
72 | + "error": "Validation Failed", | |
73 | + "issues": exc.errors(), | |
74 | + }, | |
75 | + } | |
76 | +``` | |
77 | + | |
78 | +## Validation Errors | |
79 | + | |
80 | +When a request fails type validation (e.g., invalid path parameter type), JustAPI returns a structured 422 response: | |
81 | + | |
82 | +```bash | |
83 | +curl http://127.0.0.1:8000/items/abc | |
84 | +# Output: {"detail":"validation error"} | |
85 | +``` | |
86 | + | |
87 | +With a custom handler, you can expand this to include field-level details. | |
88 | + | |
89 | +## Error Response Format | |
90 | + | |
91 | +By default, all error responses follow the format: | |
92 | + | |
93 | +```json | |
94 | +{ | |
95 | + "detail": "<error message>" | |
96 | +} | |
97 | +``` | |
98 | + | |
99 | +This applies to: | |
100 | +- **400** — Bad request | |
101 | +- **401** — Unauthorized | |
102 | +- **403** — Forbidden | |
103 | +- **404** — Not found (unmatched route) | |
104 | +- **413** — Payload too large | |
105 | +- **422** — Validation error | |
106 | +- **429** — Rate limit exceeded | |
107 | +- **500** — Internal server error | |
108 | +- **503** — Service unavailable (pool saturation) | |
109 | +- **504** — Gateway timeout | |
110 | + | |
111 | +## Handling Errors in Dependencies | |
112 | + | |
113 | +Dependencies can raise `HTTPException` to reject requests: | |
114 | + | |
115 | +```python | |
116 | +from justapi import Depends, HTTPException, Header | |
117 | + | |
118 | + | |
119 | +def require_admin(authorization: str = Header(...)): | |
120 | + if authorization != "Bearer admin-token": | |
121 | + raise HTTPException(status_code=403, detail="Admin access required") | |
122 | + | |
123 | + | |
124 | +@app.get("/admin") | |
125 | +def admin_panel(request, admin: None = Depends(require_admin)): | |
126 | + return {"secret": "classified"} | |
127 | +``` | |
128 | + | |
129 | +## Global Error Catchers | |
130 | + | |
131 | +You can register a catch-all exception handler: | |
132 | + | |
133 | +```python | |
134 | +@app.exception_handler(Exception) | |
135 | +def global_handler(request, exc: Exception): | |
136 | + return { | |
137 | + "status": 500, | |
138 | + "body": {"detail": "An unexpected error occurred"}, | |
139 | + } | |
140 | +``` | |
141 | + | |
142 | +In production, JustAPI's `panic = "abort"` mode ensures that unexpected Rust panics abort the process cleanly, and the supervisor restarts it. See [Production Checklist](/deployment/production-checklist/). | |
143 | + | |
144 | +## Next Steps | |
145 | + | |
146 | +- [Middleware](/tutorials/middleware/) — Request/response interception | |
147 | +- [File Uploads](/tutorials/file-uploads/) — Handle multipart form data | |
148 | +- [Production Checklist](/deployment/production-checklist/) — Error handling in production | |
0 | 149 | |