9a3684a
commit 9a3684a
5 files changed
+338 -34
Showing 5 changed files+338-34
▼docs_site/src/content/docs/advanced/batching.md
+56-0
| @@ -0,0 +1,56 @@ | ||
1 | +--- | |
2 | +title: Adaptive Batching | |
3 | +description: Automatically batch multiple requests into a single handler call in JustAPI. | |
4 | +keywords: [JustAPI, batching, adaptive batch, performance, request coalescing] | |
5 | +--- | |
6 | + | |
7 | +## Basic Usage | |
8 | + | |
9 | +Use `@adaptive_batch` to automatically collect and batch incoming requests: | |
10 | + | |
11 | +```python | |
12 | +from justapi import JustAPIApp, adaptive_batch | |
13 | + | |
14 | +app = JustAPIApp() | |
15 | + | |
16 | +@app.post("/predict") | |
17 | +@adaptive_batch(max_size=32, window_ms=10) | |
18 | +def predict_batch(items: list[dict]): | |
19 | + # Called with up to 32 items collected within 10ms | |
20 | + results = [process(item) for item in items] | |
21 | + return results | |
22 | +``` | |
23 | + | |
24 | +When multiple requests arrive within the `window_ms` window, JustAPI collects them and calls your handler once with all items combined. | |
25 | + | |
26 | +## Parameters | |
27 | + | |
28 | +| Parameter | Type | Default | Description | | |
29 | +|-----------|------|---------|-------------| | |
30 | +| `max_size` | `int` | `32` | Maximum items per batch | | |
31 | +| `window_ms` | `int` | `10` | Collection window in milliseconds | | |
32 | + | |
33 | +## How It Works | |
34 | + | |
35 | +1. Request arrives → added to batch queue | |
36 | +2. If queue reaches `max_size` → handler called immediately | |
37 | +3. If `window_ms` elapses → handler called with whatever is queued | |
38 | +4. Response is sent back to each original request | |
39 | + | |
40 | +## With Dependencies | |
41 | + | |
42 | +```python | |
43 | +@app.post("/predict") | |
44 | +@adaptive_batch(max_size=64, window_ms=5) | |
45 | +def predict_batch( | |
46 | + items: list[dict], | |
47 | + user: dict = Security(get_current_user), | |
48 | +): | |
49 | + # Process batch for authenticated user | |
50 | + return process_batch(items, user["id"]) | |
51 | +``` | |
52 | + | |
53 | +## See Also | |
54 | + | |
55 | +- [Resilience Patterns](/advanced/resilience-patterns/) — rate limiting, circuit breakers | |
56 | +- [Performance Tuning](/advanced/performance-tuning/) — optimization | |
0 | 57 | |
▼docs_site/src/content/docs/api-reference/mcp-server.md
+74-0
| @@ -0,0 +1,74 @@ | ||
1 | +--- | |
2 | +title: MCP Server | |
3 | +description: Built-in MCP (Model Context Protocol) server for AI agent integration in JustAPI. | |
4 | +keywords: [JustAPI, MCP, agent, AI, tools, Model Context Protocol] | |
5 | +--- | |
6 | + | |
7 | +## Register Tools | |
8 | + | |
9 | +Expose your routes as MCP tools: | |
10 | + | |
11 | +```python | |
12 | +from justapi import JustAPIApp | |
13 | + | |
14 | +app = JustAPIApp() | |
15 | + | |
16 | +@app.tool | |
17 | +def get_weather(city: str) -> dict: | |
18 | + """Get current weather for a city.""" | |
19 | + return {"city": city, "temp": 72, "condition": "sunny"} | |
20 | + | |
21 | +@app.tool(name="search_db", description="Search the database") | |
22 | +def search(query: str) -> list: | |
23 | + return db.search(query) | |
24 | +``` | |
25 | + | |
26 | +## List Tools | |
27 | + | |
28 | +```python | |
29 | +tools = app.list_tools() | |
30 | +# Returns list of MCP tool descriptors | |
31 | +``` | |
32 | + | |
33 | +## Call Tools | |
34 | + | |
35 | +```python | |
36 | +result = app.call_tool("get_weather", {"city": "London"}) | |
37 | +# Returns: {"city": "London", "temp": 72, "condition": "sunny"} | |
38 | +``` | |
39 | + | |
40 | +## Built-in MCP Tools | |
41 | + | |
42 | +JustAPI registers these introspection tools automatically: | |
43 | + | |
44 | +| Tool | Description | | |
45 | +|------|-------------| | |
46 | +| `list_routes` | List all registered API routes | | |
47 | +| `get_signature` | Full signature for one route | | |
48 | +| `explain_endpoint` | Natural-language explanation of an endpoint | | |
49 | +| `generate_snippet` | Client code snippet for an endpoint | | |
50 | + | |
51 | +## MCP Server CLI | |
52 | + | |
53 | +Run the MCP stdio server for agent integration: | |
54 | + | |
55 | +```bash | |
56 | +python -m justapi.mcp_server --base-url http://localhost:8000 | |
57 | +``` | |
58 | + | |
59 | +## System Routes | |
60 | + | |
61 | +With `app.enable_system_routes()`, these routes expose tool data: | |
62 | + | |
63 | +| Route | Description | | |
64 | +|-------|-------------| | |
65 | +| `/_system/tools` | List registered tools | | |
66 | +| `/_system/tools/call` | Invoke a tool by name | | |
67 | +| `/_system/help` | Rich route descriptors | | |
68 | +| `/_system/help/{name}` | Detailed help for one route | | |
69 | + | |
70 | +## See Also | |
71 | + | |
72 | +- [Agent System](/advanced/agent-system/) — full agent guide | |
73 | +- [System Routes](/api-reference/system-routes/) — introspection routes | |
74 | +- [Session](/api-reference/session/) — agent session state | |
0 | 75 | |
▼docs_site/src/content/docs/api-reference/status-codes.md
+84-0
| @@ -0,0 +1,84 @@ | ||
1 | +--- | |
2 | +title: Status Codes | |
3 | +description: HTTP and WebSocket status code constants in JustAPI. | |
4 | +keywords: [JustAPI, status codes, HTTP, WebSocket, constants] | |
5 | +--- | |
6 | + | |
7 | +## HTTP Status Codes | |
8 | + | |
9 | +Import from `justapi.status`: | |
10 | + | |
11 | +```python | |
12 | +from justapi import status | |
13 | + | |
14 | +@app.get("/items") | |
15 | +def list_items(): | |
16 | + if not items: | |
17 | + return JSONResponse( | |
18 | + content={"error": "no items"}, | |
19 | + status_code=status.HTTP_204_NO_CONTENT, | |
20 | + ) | |
21 | + return {"items": items} | |
22 | +``` | |
23 | + | |
24 | +### 1xx Informational | |
25 | + | |
26 | +| Constant | Code | Description | | |
27 | +|----------|------|-------------| | |
28 | +| `HTTP_100_CONTINUE` | 100 | Continue | | |
29 | +| `HTTP_101_SWITCHING_PROTOCOLS` | 101 | Switching Protocols | | |
30 | + | |
31 | +### 2xx Success | |
32 | + | |
33 | +| Constant | Code | Description | | |
34 | +|----------|------|-------------| | |
35 | +| `HTTP_200_OK` | 200 | OK | | |
36 | +| `HTTP_201_CREATED` | 201 | Created | | |
37 | +| `HTTP_202_ACCEPTED` | 202 | Accepted | | |
38 | +| `HTTP_204_NO_CONTENT` | 204 | No Content | | |
39 | + | |
40 | +### 3xx Redirection | |
41 | + | |
42 | +| Constant | Code | Description | | |
43 | +|----------|------|-------------| | |
44 | +| `HTTP_301_MOVED_PERMANENTLY` | 301 | Moved Permanently | | |
45 | +| `HTTP_302_FOUND` | 302 | Found | | |
46 | +| `HTTP_304_NOT_MODIFIED` | 304 | Not Modified | | |
47 | + | |
48 | +### 4xx Client Errors | |
49 | + | |
50 | +| Constant | Code | Description | | |
51 | +|----------|------|-------------| | |
52 | +| `HTTP_400_BAD_REQUEST` | 400 | Bad Request | | |
53 | +| `HTTP_401_UNAUTHORIZED` | 401 | Unauthorized | | |
54 | +| `HTTP_403_FORBIDDEN` | 403 | Forbidden | | |
55 | +| `HTTP_404_NOT_FOUND` | 404 | Not Found | | |
56 | +| `HTTP_405_METHOD_NOT_ALLOWED` | 405 | Method Not Allowed | | |
57 | +| `HTTP_409_CONFLICT` | 409 | Conflict | | |
58 | +| `HTTP_422_UNPROCESSABLE_ENTITY` | 422 | Validation Error | | |
59 | +| `HTTP_429_TOO_MANY_REQUESTS` | 429 | Rate Limited | | |
60 | + | |
61 | +### 5xx Server Errors | |
62 | + | |
63 | +| Constant | Code | Description | | |
64 | +|----------|------|-------------| | |
65 | +| `HTTP_500_INTERNAL_SERVER_ERROR` | 500 | Internal Server Error | | |
66 | +| `HTTP_502_BAD_GATEWAY` | 502 | Bad Gateway | | |
67 | +| `HTTP_503_SERVICE_UNAVAILABLE` | 503 | Service Unavailable | | |
68 | + | |
69 | +## WebSocket Close Codes | |
70 | + | |
71 | +| Constant | Code | Description | | |
72 | +|----------|------|-------------| | |
73 | +| `WS_1000_NORMAL_CLOSURE` | 1000 | Normal closure | | |
74 | +| `WS_1001_GOING_AWAY` | 1001 | Endpoint going away | | |
75 | +| `WS_1002_PROTOCOL_ERROR` | 1002 | Protocol error | | |
76 | +| `WS_1003_UNSUPPORTED_DATA` | 1003 | Unsupported data | | |
77 | +| `WS_1008_POLICY_VIOLATION` | 1008 | Policy violation | | |
78 | +| `WS_1011_INTERNAL_ERROR` | 1011 | Internal error | | |
79 | + | |
80 | +## See Also | |
81 | + | |
82 | +- [Response Status Code](/tutorials/response-status-code/) — setting status codes | |
83 | +- [Handling Errors](/tutorials/error-handling/) — error responses | |
84 | +- [Response Classes](/api-reference/responses/) — response types | |
0 | 85 | |
▼docs_site/src/content/docs/api-reference/testing-client.md
+50-34
| @@ -1,55 +1,71 @@ | ||
1 | 1 | --- |
2 | -title: Testing Client API | |
3 | -description: "API reference for the test client in JustAPI, the FastAPI alternative — in-process HTTP test client for testing routes without a server." | |
4 | -keywords: [testing client, fastapi alternative, justapi, test client, justapitestclient, integration testing] | |
2 | +title: Testing Client | |
3 | +description: Test JustAPI applications with JustAPITestClient — in-process HTTP testing. | |
4 | +keywords: [JustAPI, test client, JustAPITestClient, testing, HTTP testing] | |
5 | 5 | --- |
6 | 6 | |
7 | -## `JustAPITestClient` | |
8 | - | |
9 | -The test client sends HTTP requests to your application without starting a real server, using `tokio` duplex streams. | |
7 | +## Basic Usage | |
10 | 8 | |
11 | 9 | ```python |
12 | -from justapi import JustAPITestClient | |
10 | +from justapi import JustAPIApp, JustAPITestClient | |
13 | 11 | |
14 | -client = JustAPITestClient(app) | |
15 | -``` | |
12 | +app = JustAPIApp() | |
16 | 13 | |
17 | -### Methods | |
14 | +@app.get("/hello") | |
15 | +def hello(): | |
16 | + return {"message": "Hello!"} | |
18 | 17 | |
19 | -```python | |
20 | -response = client.get("/items/42") | |
21 | -response = client.post("/items/", body={"name": "Item"}) | |
22 | -response = client.put("/items/42", body={"name": "Updated"}) | |
23 | -response = client.patch("/items/42", body={"name": "Patched"}) | |
24 | -response = client.delete("/items/42") | |
18 | +client = JustAPITestClient(app) | |
19 | + | |
20 | +def test_hello(): | |
21 | + response = client.get("/hello") | |
22 | + assert response["status"] == 200 | |
23 | + assert response["body"] == b'{"message":"Hello!"}' | |
25 | 24 | ``` |
26 | 25 | |
27 | -### Response Object | |
26 | +## Methods | |
27 | + | |
28 | +| Method | Signature | Description | | |
29 | +|--------|-----------|-------------| | |
30 | +| `get(path)` | `GET path` | Send GET request | | |
31 | +| `post(path, body)` | `POST path` with JSON body | Send POST request | | |
32 | +| `post_with(path, body, headers)` | `POST path` with headers | POST with custom headers | | |
33 | +| `put(path, body)` | `PUT path` with JSON body | Send PUT request | | |
34 | +| `patch(path, body)` | `PATCH path` with JSON body | Send PATCH request | | |
35 | +| `delete(path)` | `DELETE path` | Send DELETE request | | |
28 | 36 | |
29 | -| Attribute | Type | Description | | |
30 | -|---|---|---| | |
31 | -| `status` | `int` | HTTP status code | | |
32 | -| `body` | `bytes` | Raw response body | | |
33 | -| `json()` | `dict` | Parsed JSON body | | |
37 | +## Response Format | |
34 | 38 | |
35 | -### Example Test | |
39 | +All methods return a dict: | |
36 | 40 | |
37 | 41 | ```python |
38 | -from justapi import JustAPIApp, JustAPITestClient | |
42 | +{ | |
43 | + "status": 200, # HTTP status code | |
44 | + "headers": {...}, # Response headers | |
45 | + "body": b"..." # Raw body bytes | |
46 | +} | |
47 | +``` | |
39 | 48 | |
40 | -app = JustAPIApp() | |
49 | +## POST with Headers | |
50 | + | |
51 | +```python | |
52 | +def test_with_auth(): | |
53 | + response = client.post_with( | |
54 | + "/items", | |
55 | + {"name": "Widget"}, | |
56 | + headers={"Authorization": "Bearer my-token"}, | |
57 | + ) | |
58 | + assert response["status"] == 201 | |
59 | +``` | |
41 | 60 | |
42 | -@app.get("/ping") | |
43 | -def ping(request): | |
44 | - return {"status": "ok"} | |
61 | +## With Database | |
45 | 62 | |
46 | -client = JustAPITestClient(app) | |
47 | -response = client.get("/ping") | |
48 | -assert response.status == 200 | |
49 | -assert response.json() == {"status": "ok"} | |
63 | +```python | |
64 | +client = JustAPITestClient(app, database="sqlite:///test.db") | |
50 | 65 | ``` |
51 | 66 | |
52 | 67 | ## See Also |
53 | 68 | |
54 | -- [Testing Guide](/contributing/testing-guide/) — Writing tests for JustAPI | |
55 | -- [JustAPIApp](/api-reference/justapiapp/) — App configuration | |
69 | +- [Testing](/tutorials/testing/) — basic testing guide | |
70 | +- [Testing Utilities](/tutorials/testing-utilities/) — AsyncTestClient, Snapshot, assertions | |
71 | +- [Testing a Database](/how-to/testing-database/) — database test patterns | |
56 | 72 | |
▼docs_site/src/content/docs/tutorials/logging-setup.md
+74-0
| @@ -0,0 +1,74 @@ | ||
1 | +--- | |
2 | +title: Logging Setup | |
3 | +description: Configure logging and tracing in JustAPI — text, JSON, file, and OpenTelemetry. | |
4 | +keywords: [JustAPI, logging, tracing, OpenTelemetry, JSON logging, file logging] | |
5 | +--- | |
6 | + | |
7 | +## Text Logging | |
8 | + | |
9 | +```python | |
10 | +from justapi import init_logging | |
11 | + | |
12 | +init_logging(level="info", format="text") | |
13 | +``` | |
14 | + | |
15 | +## JSON Logging | |
16 | + | |
17 | +```python | |
18 | +from justapi import init_json_logging | |
19 | + | |
20 | +init_json_logging() | |
21 | +``` | |
22 | + | |
23 | +Outputs structured JSON to stdout — ideal for production log aggregation. | |
24 | + | |
25 | +## File Logging | |
26 | + | |
27 | +```python | |
28 | +from justapi import init_file_logging | |
29 | + | |
30 | +init_file_logging(path="logs/app.log") | |
31 | +``` | |
32 | + | |
33 | +JSON logging to a rolling file. | |
34 | + | |
35 | +## OpenTelemetry Tracing | |
36 | + | |
37 | +```python | |
38 | +from justapi import init_otlp_tracing | |
39 | + | |
40 | +init_otlp_tracing( | |
41 | + endpoint="http://localhost:4317", | |
42 | + service_name="my-api", | |
43 | +) | |
44 | +``` | |
45 | + | |
46 | +## Shutdown Tracing | |
47 | + | |
48 | +```python | |
49 | +from justapi import shutdown_tracing | |
50 | + | |
51 | +# On app shutdown | |
52 | +shutdown_tracing() | |
53 | +``` | |
54 | + | |
55 | +Flushes all pending spans and shuts down the subscriber. | |
56 | + | |
57 | +## Read Trace Context | |
58 | + | |
59 | +```python | |
60 | +from justapi.tracing import get_current_trace_id, get_current_span_id | |
61 | + | |
62 | +@app.get("/debug") | |
63 | +def debug(): | |
64 | + return { | |
65 | + "trace_id": get_current_trace_id(), | |
66 | + "span_id": get_current_span_id(), | |
67 | + } | |
68 | +``` | |
69 | + | |
70 | +## See Also | |
71 | + | |
72 | +- [Structured Logging](/observability/structured-logging/) — logging overview | |
73 | +- [OpenTelemetry](/observability/opentelemetry/) — tracing overview | |
74 | +- [Environment Variables](/reference/environment-variables/) — JUSTAPI_ENABLE_TRACE | |
0 | 75 | |