p3g-i: add Dependency Injection, Exceptions, Schema & Validation API reference pages
swadhinbiswas committed 3 months ago
78f82c4
1 parent 29908fb
commit 78f82c4
3 files changed
+377 -0
Showing 3 changed files+377-0
▼docs_site/src/content/docs/api-reference/dependency-injection.md
+147-0
| @@ -0,0 +1,147 @@ | ||
1 | +--- | |
2 | +title: Dependency Injection API | |
3 | +description: Reference for Depends and parameter extraction functions. | |
4 | +--- | |
5 | + | |
6 | +## `Depends()` | |
7 | + | |
8 | +Declare a dependency that is resolved before the handler executes: | |
9 | + | |
10 | +```python | |
11 | +from justapi import Depends | |
12 | + | |
13 | +def common_params(q: str | None = None, skip: int = 0): | |
14 | + return {"q": q, "skip": skip} | |
15 | + | |
16 | +@app.get("/items") | |
17 | +def list_items(request, params: dict = Depends(common_params)): | |
18 | + ... | |
19 | +``` | |
20 | + | |
21 | +| Parameter | Type | Default | Description | | |
22 | +|---|---|---|---| | |
23 | +| `dependency` | callable | required | The dependency function or class | | |
24 | +| `use_cache` | `bool` | `True` | Cache result for reuse across nested dependencies | | |
25 | + | |
26 | +## Parameter Extractors | |
27 | + | |
28 | +These functions extract specific parts of the request: | |
29 | + | |
30 | +### `Path()` | |
31 | + | |
32 | +```python | |
33 | +from justapi import Path | |
34 | + | |
35 | +@app.get("/items/{item_id}") | |
36 | +def read_item( | |
37 | + request, | |
38 | + item_id: int = Path(..., description="The item ID"), | |
39 | +): | |
40 | +``` | |
41 | + | |
42 | +| Parameter | Type | Default | Description | | |
43 | +|---|---|---|---| | |
44 | +| `default` | any | `...` | `...` means required | | |
45 | +| `description` | `str` | `None` | OpenAPI description | | |
46 | + | |
47 | +### `Query()` | |
48 | + | |
49 | +```python | |
50 | +from justapi import Query | |
51 | + | |
52 | +@app.get("/items") | |
53 | +def list_items( | |
54 | + request, | |
55 | + q: str = Query(None, max_length=50, description="Search query"), | |
56 | +): | |
57 | +``` | |
58 | + | |
59 | +| Parameter | Type | Default | Description | | |
60 | +|---|---|---|---| | |
61 | +| `default` | any | `...` | Default value (`...` = required) | | |
62 | +| `description` | `str` | `None` | OpenAPI description | | |
63 | +| `max_length` | `int` | `None` | Max string length | | |
64 | +| `min_length` | `int` | `None` | Min string length | | |
65 | +| `regex` | `str` | `None` | Regex pattern | | |
66 | + | |
67 | +### `Header()` | |
68 | + | |
69 | +```python | |
70 | +from justapi import Header | |
71 | + | |
72 | +@app.get("/protected") | |
73 | +def protected( | |
74 | + request, | |
75 | + authorization: str = Header(..., description="Bearer token"), | |
76 | +): | |
77 | +``` | |
78 | + | |
79 | +### `Cookie()` | |
80 | + | |
81 | +```python | |
82 | +from justapi import Cookie | |
83 | + | |
84 | +@app.get("/") | |
85 | +def home( | |
86 | + request, | |
87 | + session_id: str = Cookie(None, description="Session cookie"), | |
88 | +): | |
89 | +``` | |
90 | + | |
91 | +### `Body()` | |
92 | + | |
93 | +```python | |
94 | +from justapi import Body | |
95 | + | |
96 | +@app.post("/items") | |
97 | +def create_item( | |
98 | + request, | |
99 | + data: dict = Body(..., description="Request body"), | |
100 | +): | |
101 | +``` | |
102 | + | |
103 | +### `File()` | |
104 | + | |
105 | +```python | |
106 | +from justapi import File, UploadFile | |
107 | + | |
108 | +@app.post("/upload") | |
109 | +def upload( | |
110 | + request, | |
111 | + file: UploadFile = File(..., description="File to upload"), | |
112 | +): | |
113 | +``` | |
114 | + | |
115 | +### `Form()` | |
116 | + | |
117 | +```python | |
118 | +from justapi import Form | |
119 | + | |
120 | +@app.post("/submit") | |
121 | +def submit( | |
122 | + request, | |
123 | + name: str = Form(..., description="User name"), | |
124 | +): | |
125 | +``` | |
126 | + | |
127 | +## `Session` (Agent) | |
128 | + | |
129 | +```python | |
130 | +from justapi import Session | |
131 | + | |
132 | +@app.get("/agent/state") | |
133 | +def agent_state(request, session: Session): | |
134 | + return {"session_id": session.id, **session.get()} | |
135 | +``` | |
136 | + | |
137 | +| Attribute/Method | Description | | |
138 | +|---|---| | |
139 | +| `session.id` | Unique session identifier | | |
140 | +| `session.get()` | Get all session data as dict | | |
141 | +| `session.update(**kwargs)` | Update session data | | |
142 | + | |
143 | +## See Also | |
144 | + | |
145 | +- [Dependency Injection Tutorial](/tutorials/dependency-injection/) — Usage guide | |
146 | +- [Session API](/api-reference/session/) — Agent session reference | |
147 | +- [JustAPIApp](/api-reference/justapiapp/) — App configuration | |
0 | 148 | |
▼docs_site/src/content/docs/api-reference/exceptions.md
+94-0
| @@ -0,0 +1,94 @@ | ||
1 | +--- | |
2 | +title: Exceptions & Error Handling API | |
3 | +description: Reference for HTTPException, validation errors, and exception handlers. | |
4 | +--- | |
5 | + | |
6 | +## `HTTPException` | |
7 | + | |
8 | +Raise to return an HTTP error response: | |
9 | + | |
10 | +```python | |
11 | +from justapi import HTTPException | |
12 | + | |
13 | +raise HTTPException( | |
14 | + status_code=404, | |
15 | + detail="Item not found", | |
16 | + headers={"X-Error": "not_found"}, | |
17 | +) | |
18 | +``` | |
19 | + | |
20 | +| Parameter | Type | Default | Description | | |
21 | +|---|---|---|---| | |
22 | +| `status_code` | `int` | required | HTTP status code | | |
23 | +| `detail` | `str` | `""` | Error message | | |
24 | +| `headers` | `dict` | `None` | Additional response headers | | |
25 | + | |
26 | +## `RequestValidationError` | |
27 | + | |
28 | +Raised automatically when Pydantic validation fails: | |
29 | + | |
30 | +```python | |
31 | +from justapi import RequestValidationError | |
32 | + | |
33 | +@app.exception_handler(RequestValidationError) | |
34 | +def handler(request, exc: RequestValidationError): | |
35 | + return { | |
36 | + "status": 422, | |
37 | + "body": {"error": "Validation Failed", "issues": exc.errors()}, | |
38 | + } | |
39 | +``` | |
40 | + | |
41 | +| Attribute | Type | Description | | |
42 | +|---|---|---| | |
43 | +| `.errors()` | `list` | List of validation error details | | |
44 | + | |
45 | +## Exception Handlers | |
46 | + | |
47 | +Register custom handlers with `@app.exception_handler()`: | |
48 | + | |
49 | +```python | |
50 | +@app.exception_handler(HTTPException) | |
51 | +def custom_handler(request, exc: HTTPException): | |
52 | + return { | |
53 | + "status": exc.status_code, | |
54 | + "body": {"error": exc.detail, "code": exc.status_code}, | |
55 | + } | |
56 | +``` | |
57 | + | |
58 | +### Precedence | |
59 | + | |
60 | +Exception handlers are matched in order: | |
61 | +1. Exact exception class handler | |
62 | +2. Parent class handler | |
63 | +3. `Exception` catch-all handler | |
64 | + | |
65 | +## Default Error Codes | |
66 | + | |
67 | +| Status | Error | When | | |
68 | +|---|---|---| | |
69 | +| 400 | Bad Request | Malformed request | | |
70 | +| 401 | Unauthorized | Missing/invalid auth | | |
71 | +| 403 | Forbidden | Insufficient permissions | | |
72 | +| 404 | Not Found | Unmatched route | | |
73 | +| 413 | Payload Too Large | Body exceeds `max_body_size` | | |
74 | +| 422 | Unprocessable Entity | Validation failure | | |
75 | +| 429 | Too Many Requests | Rate limit exceeded | | |
76 | +| 500 | Internal Server Error | Unhandled exception | | |
77 | +| 503 | Service Unavailable | Pool saturation | | |
78 | +| 504 | Gateway Timeout | Request timeout | | |
79 | + | |
80 | +## Error Response Format | |
81 | + | |
82 | +Default error body: | |
83 | + | |
84 | +```json | |
85 | +{"detail": "<error message>"} | |
86 | +``` | |
87 | + | |
88 | +Custom handlers can return any format. | |
89 | + | |
90 | +## See Also | |
91 | + | |
92 | +- [Error Handling Tutorial](/tutorials/error-handling/) — Usage guide | |
93 | +- [Resilience Patterns](/advanced/resilience-patterns/) — Circuit breakers, rate limiting | |
94 | +- [Production Checklist](/deployment/production-checklist/) — Error handling in production | |
0 | 95 | |
▼docs_site/src/content/docs/api-reference/schema-validation.md
+136-0
| @@ -0,0 +1,136 @@ | ||
1 | +--- | |
2 | +title: Schema & Validation API | |
3 | +description: Reference for Schema, Field, and validation functions. | |
4 | +--- | |
5 | + | |
6 | +JustAPI provides two validation paths: the built-in `Schema` class (validated in Rust) and Pydantic models (validated in Python). | |
7 | + | |
8 | +## `Schema` Class | |
9 | + | |
10 | +Define a validation schema using the built-in `Schema` class: | |
11 | + | |
12 | +```python | |
13 | +from justapi import JustAPIApp, Schema | |
14 | +from pydantic import Field | |
15 | + | |
16 | +class ProductCreate(Schema): | |
17 | + name: str = Field(..., min_length=1, max_length=100) | |
18 | + price: float = Field(..., gt=0, le=100000) | |
19 | + quantity: int = Field(0, ge=0) | |
20 | +``` | |
21 | + | |
22 | +Schema subclasses can be used with `body_schema` to validate in Rust. | |
23 | + | |
24 | +## `Field()` Constraints | |
25 | + | |
26 | +```python | |
27 | +from pydantic import Field | |
28 | + | |
29 | +Field( | |
30 | + default=..., | |
31 | + gt=None, # Greater than | |
32 | + ge=None, # Greater than or equal | |
33 | + lt=None, # Less than | |
34 | + le=None, # Less than or equal | |
35 | + min_length=None, # Min string length | |
36 | + max_length=None, # Max string length | |
37 | + regex=None, # Regular expression pattern | |
38 | + format=None, # Named format (email, uri, uuid, date-time, date, hostname, ipv4) | |
39 | + enum=None, # List of allowed values | |
40 | + description=None, # Field description for OpenAPI docs | |
41 | +) | |
42 | +``` | |
43 | + | |
44 | +### Supported Formats | |
45 | + | |
46 | +| Format | Valid Example | Invalid Example | | |
47 | +|---|---|---| | |
48 | +| `email` | `[email protected]` | `not-an-email` | | |
49 | +| `uri` | `https://example.com` | `not-a-uri` | | |
50 | +| `uuid` | `550e8400-...` | `not-a-uuid` | | |
51 | +| `date` | `2026-07-24` | `07/24/2026` | | |
52 | +| `date-time` | `2026-07-24T12:00:00Z` | `tomorrow` | | |
53 | +| `hostname` | `example.com` | `not a hostname` | | |
54 | +| `ipv4` | `192.168.1.1` | `256.0.0.1` | | |
55 | + | |
56 | +## Using with `body_schema` | |
57 | + | |
58 | +```python | |
59 | +@app.post("/products", body_schema=ProductCreate) | |
60 | +def create_product(request): | |
61 | + data = request.json() | |
62 | + return {"status": "created", "product": data} | |
63 | +``` | |
64 | + | |
65 | +When `body_schema` is set: | |
66 | +1. The request body is parsed and validated in Rust against the schema | |
67 | +2. Invalid bodies receive 422 without Python involvement | |
68 | +3. Valid bodies are passed to the handler as parsed JSON | |
69 | + | |
70 | +## Native Fast Path | |
71 | + | |
72 | +With `native=True`, the entire handler executes in Rust: | |
73 | + | |
74 | +```python | |
75 | +@app.post("/fast-products", body_schema=ProductCreate, native=True) | |
76 | +def fast_create(request): | |
77 | + return {"status": "ok"} | |
78 | +``` | |
79 | + | |
80 | +## Nested Schemas | |
81 | + | |
82 | +```python | |
83 | +class Address(Schema): | |
84 | + street: str | |
85 | + city: str | |
86 | + zip_code: str = Field(..., regex=r"^\d{5}(-\d{4})?$") | |
87 | + | |
88 | +class Customer(Schema): | |
89 | + name: str | |
90 | + address: Address | |
91 | + tags: list[str] | |
92 | +``` | |
93 | + | |
94 | +Nested schemas are compiled into a single JSON Schema with `$defs` and `$ref` references. | |
95 | + | |
96 | +## `validate_value()` Function | |
97 | + | |
98 | +Validate arbitrary data against a schema at runtime: | |
99 | + | |
100 | +```python | |
101 | +from justapi._justapi import validate_value | |
102 | + | |
103 | +schema = { | |
104 | + "type": "object", | |
105 | + "properties": { | |
106 | + "name": {"type": "string"}, | |
107 | + "age": {"type": "integer", "minimum": 0}, | |
108 | + }, | |
109 | + "required": ["name", "age"], | |
110 | +} | |
111 | + | |
112 | +result = validate_value({"name": "Alice", "age": 30}, schema) | |
113 | +# Returns: {"valid": true, "errors": []} | |
114 | +``` | |
115 | + | |
116 | +## Pydantic Bridge | |
117 | + | |
118 | +Standard Pydantic models work with JustAPI routes without modification: | |
119 | + | |
120 | +```python | |
121 | +from pydantic import BaseModel | |
122 | + | |
123 | +class Item(BaseModel): | |
124 | + name: str | |
125 | + price: float | |
126 | + | |
127 | +@app.post("/items") | |
128 | +def create_item(request, item: Item): | |
129 | + return item.model_dump() | |
130 | +``` | |
131 | + | |
132 | +## See Also | |
133 | + | |
134 | +- [Request Body & Validation Tutorial](/tutorials/request-body/) — Usage guide | |
135 | +- [Native Fast Path](/advanced/native-fast-path/) — Rust-native execution | |
136 | +- [JustAPIApp](/api-reference/justapiapp/) — body_schema parameter on route decorators | |
0 | 137 | |