p40: complete ARCHITECTURE.md, CONTRIBUTING.md, and FastAPI-like README.md
swadhinbiswas committed 3 months ago
1bf5f5e
1 parent abcc328
commit 1bf5f5e
4 files changed
+657 -93
Showing 4 changed files+657-93
▼ARCHITECTURE.md
+129-0
| @@ -0,0 +1,129 @@ | ||
1 | +# JustAPI Architecture | |
2 | + | |
3 | +This document provides a comprehensive overview of the design, internal structure, and technical invariants of the **JustAPI** runtime. | |
4 | + | |
5 | +--- | |
6 | + | |
7 | +## High-Level System Design | |
8 | + | |
9 | +JustAPI splits responsibility between a high-performance Rust core and Python application logic: | |
10 | + | |
11 | +* **Rust Core (`justapi-core`):** Handles I/O, networking (HTTP/1.1, HTTP/2, WebSockets, gRPC, SSE), TLS termination, path routing, the middleware chain (auth, CORS, rate limiting, compression), schema validation, and serialization. | |
12 | +* **Python Layer (`justapi-py`):** Provides the decorator-based framework API (matching FastAPI's developer experience), registers routes, runs dependency injection (`Depends()`), and executes the user's business logic. | |
13 | +* **Zero-Copy FFI Boundary:** PyO3's buffer protocol is used to pass request parameters, headers, and bodies directly from Rust to Python as read-only memory views, eliminating serialization/deserialization overhead. | |
14 | + | |
15 | +``` | |
16 | + [ Client Request ] | |
17 | + │ | |
18 | + ▼ | |
19 | +┌──────────────────────────────┐ | |
20 | +│ Kernel (epoll / io_uring) │ | |
21 | +└──────────────┬───────────────┘ | |
22 | + │ | |
23 | + ▼ | |
24 | +┌──────────────────────────────┐ | |
25 | +│ Connection Manager (Tokio) │ | |
26 | +└──────────────┬───────────────┘ | |
27 | + │ | |
28 | + ▼ | |
29 | +┌──────────────────────────────┐ | |
30 | +│ TLS (rustls) │ | |
31 | +└──────────────┬───────────────┘ | |
32 | + │ | |
33 | + ▼ | |
34 | +┌──────────────────────────────┐ | |
35 | +│ HTTP Parser (hyper) │ | |
36 | +└──────────────┬───────────────┘ | |
37 | + │ | |
38 | + ▼ | |
39 | +┌──────────────────────────────┐ | |
40 | +│ Router (matchit) │ | |
41 | +└──────────────┬───────────────┘ | |
42 | + │ | |
43 | + ▼ | |
44 | +┌──────────────────────────────┐ | |
45 | +│ Middleware Chain │ (Auth, CORS, Rate Limit, Compression) | |
46 | +└──────────────┬───────────────┘ | |
47 | + │ | |
48 | + ▼ | |
49 | +┌──────────────────────────────┐ | |
50 | +│ Zero-Copy FFI Boundary │ (PyO3 buffer protocol / Arrow / DLPack) | |
51 | +└──────────────┬───────────────┘ | |
52 | + │ | |
53 | + ▼ | |
54 | +┌──────────────────────────────┐ | |
55 | +│ Python Handler Coroutine │ (Jinja2, Pydantic validation, DB access) | |
56 | +└──────────────┬───────────────┘ | |
57 | + │ | |
58 | + ▼ | |
59 | +┌──────────────────────────────┐ | |
60 | +│ JSON Serializer (serde) │ | |
61 | +└──────────────┬───────────────┘ | |
62 | + │ | |
63 | + ▼ | |
64 | + [ Response Write ] | |
65 | +``` | |
66 | + | |
67 | +--- | |
68 | + | |
69 | +## Crate Layout & Dependency Graph | |
70 | + | |
71 | +The workspace is organized into modular crates: | |
72 | + | |
73 | +``` | |
74 | +justapi-cli ──→ justapi-core | |
75 | +justapi-py ──→ justapi-core | |
76 | +justapi-bench ─→ justapi-core | |
77 | +``` | |
78 | + | |
79 | +1. **`justapi-core`:** | |
80 | + * The engine room. Exposes the custom HTTP server built on hyper and tokio. | |
81 | + * Implements the radix-tree path router (`matchit`), middlewares, JSON Schema validation (`jsonschema` crate), static file serving, and the database connection pooling/migration runner (`sqlx`). | |
82 | +2. **`justapi-py`:** | |
83 | + * The PyO3 FFI bridge. Wraps `justapi-core` server handles and request views. | |
84 | + * Includes the pure-Python framework code (`python/justapi/`) providing the `JustAPIApp` decorator interface, custom dependency injection container, responses, testing client, and templates. | |
85 | +3. **`justapi-cli`:** | |
86 | + * Command-line utility (`justapi` binary). Runs development servers (with watch/hot-reload), compiles OpenAPI specs, profiles handlers, and executes migrations. | |
87 | +4. **`justapi-bench`:** | |
88 | + * Internal benchmark harness. Used to prevent performance regressions by verifying throughput and latency gates. | |
89 | +5. **`justapi-inference`:** | |
90 | + * Optional ML serving engine built on Candle (CUDA/CPU). Implements PagedAttention KV-caching, continuous batching, and speculative decoding. | |
91 | + | |
92 | +--- | |
93 | + | |
94 | +## Concurrency & GIL Model | |
95 | + | |
96 | +JustAPI utilizes a **GIL-free async worker architecture** (ADR-021): | |
97 | + | |
98 | +* **Tokio Worker Integration:** Incoming connections are accepted concurrently by Tokio worker threads. | |
99 | +* **GIL-free attach:** When a request requires Python execution, the Tokio worker thread uses PyO3's GIL-free `Python::try_attach()` to safely lock the GIL. | |
100 | +* **Execution Caching:** Python handler helper modules are compiled once at startup and cached in a global `OnceLock<Py<PyAny>>`. Invoking Python handlers does not require spawning separate OS threads or managing an external asyncio loop in Rust. | |
101 | +* **GIL Release during I/O:** The GIL is immediately released (via PyO3's `py.detach()`) when Python handlers complete or yield for async Rust I/O, maximizing CPU cores and avoiding interpreter deadlocks. | |
102 | + | |
103 | +--- | |
104 | + | |
105 | +## Key Subsystems | |
106 | + | |
107 | +### Radix Router | |
108 | +Uses the radix-trie based `matchit` crate. Paths are resolved in `O(1)` time relative to the number of registered routes. Parameters are parsed into typed path variables during the routing step. | |
109 | + | |
110 | +### Request Validation | |
111 | +Request bodies are validated against their JSON Schema *entirely in Rust* using the `jsonschema` crate before Python is invoked. Invalid requests receive an RFC 9457 structured error response and are rejected at the FFI boundary, saving CPU cycles. | |
112 | + | |
113 | +### Database ORM | |
114 | +Built on `sqlx` connection pools. The migrator discovers and executes plain SQL migration files, tracking applied versions in a tracking table. Transactions are automatically managed by the middleware chain: initialized on incoming requests, committed on `2xx` statuses, and rolled back on error. | |
115 | + | |
116 | +### Observability | |
117 | +* **Metrics:** Prometheus-compatible endpoint serving bucket-based latency histograms (p50, p95, p99, p999 percentiles calculated client-side) and error counts. | |
118 | +* **OTel Tracing:** OpenTelemetry OTLP tracing hooks propagate context across the PyO3 boundary using thread-local variables. | |
119 | + | |
120 | +--- | |
121 | + | |
122 | +## LLM Serving Engine (`justapi-inference`) | |
123 | + | |
124 | +To support fast AI deployment, JustAPI incorporates a Rust-native model server: | |
125 | + | |
126 | +* **KvBlockPool:** Implements paged KV-cache block allocation to prevent GPU memory fragmentation. | |
127 | +* **PrefixCache:** A SGLang-style radix prefix cache that reuses prefill context across concurrent requests. | |
128 | +* **PdScheduler:** Implements disaggregated prefill/decode scheduling. Prefill operations run on separate resource pools from decode steps, avoiding decode starvation. | |
129 | +* **Speculative Decoding:** Compares generated draft tokens with a target model, verifying candidate tokens in parallel. | |
0 | 130 | |
▼CONTRIBUTING.md
+87-0
| @@ -0,0 +1,87 @@ | ||
1 | +# Contributing to JustAPI | |
2 | + | |
3 | +Welcome! This guide outlines the development workflow, coding standards, and repository conventions for anyone — human or AI — contributing to the JustAPI runtime. | |
4 | + | |
5 | +--- | |
6 | + | |
7 | +## Technical & Architectural Guidelines | |
8 | + | |
9 | +Before making any changes, please read [ARCHITECTURE.md](ARCHITECTURE.md) to understand the crate structure, FFI boundaries, GIL concurrency model, and subsystems. | |
10 | + | |
11 | +--- | |
12 | + | |
13 | +## Repository Conventions | |
14 | + | |
15 | +### Commit Messages | |
16 | +We follow a structured commit format that links changes to their roadmap phase: | |
17 | +``` | |
18 | +<phase>: <short description> | |
19 | +``` | |
20 | +* *Example:* `p3: add per-request arena allocator` | |
21 | +* *Rule:* Exactly one logical change per commit. Avoid grouping unrelated updates. | |
22 | + | |
23 | +### Branch Naming | |
24 | +Create feature branches using this prefix style: | |
25 | +``` | |
26 | +phase-N/<short-description> | |
27 | +``` | |
28 | +* *Example:* `phase-42/paged-kv-cache` | |
29 | + | |
30 | +--- | |
31 | + | |
32 | +## Code Quality Standards | |
33 | + | |
34 | +### Memory Safety & `unsafe` | |
35 | +To maintain the performance boundary without introducing memory safety hazards: | |
36 | +* Every `unsafe` block **MUST** be accompanied by a `// SAFETY:` comment explaining the specific invariant that makes the block sound. | |
37 | +* Whenever feasible, add a test that would trigger Miri or cause a panic if that invariant were violated. | |
38 | + | |
39 | +### Dependency Management | |
40 | +* Do not add new dependencies lightly. | |
41 | +* Every new dependency **MUST** be justified in [DECISIONS.md](DECISIONS.md) detailing what it replaces or enables, and why alternative approaches (like writing the logic yourself) are inferior. | |
42 | + | |
43 | +### Public API Changes | |
44 | +* Any changes to public Rust APIs or public Python functions/decorators **MUST** include complete documentation (rustdoc or Python docstrings) *before* the pull request is marked as ready for review. | |
45 | + | |
46 | +### Error Handling | |
47 | +* **Application Code (CLI, Benchmarks):** Use `anyhow::Result` for generic context-wrapping errors. | |
48 | +* **Library Crates (`justapi-core`, `justapi-py`):** Use typed errors (`thiserror`) for any error crossing crate or FFI boundaries to guarantee type safety and clear error messages. | |
49 | + | |
50 | +--- | |
51 | + | |
52 | +## Pre-Submission Checklist | |
53 | + | |
54 | +Before submitting a Pull Request, verify that all of the following steps pass: | |
55 | + | |
56 | +1. **Run Workspace Tests:** | |
57 | + ```bash | |
58 | + cargo test --workspace | |
59 | + ``` | |
60 | +2. **Lint Check:** | |
61 | + ```bash | |
62 | + cargo clippy --workspace --tests -- -D warnings | |
63 | + ``` | |
64 | +3. **Formatting Check:** | |
65 | + ```bash | |
66 | + cargo fmt --check | |
67 | + ``` | |
68 | +4. **Miri Validation (for core changes):** | |
69 | + ```bash | |
70 | + cargo miri test -p justapi-core | |
71 | + ``` | |
72 | +5. **Run Benchmarks:** | |
73 | + Run the performance suite and append the new metrics to [BENCHMARKS.md](BENCHMARKS.md). Confirm there is no `p99` latency regression >5% compared to the baseline without a corresponding entry in [DECISIONS.md](DECISIONS.md). | |
74 | +6. **Roadmap Update:** | |
75 | + Update the roadmap in [PLAN.md](PLAN.md) to reflect the status of active or completed phases. | |
76 | +7. **Pattern/Skill Documentation:** | |
77 | + If you discover a recurring pattern or gotcha, document it by updating or creating a `SKILL.md` under the `skills/` directory. | |
78 | + | |
79 | +--- | |
80 | + | |
81 | +## Resuming Work after Context Reset | |
82 | +If you are an agent resuming work after a context reset: | |
83 | +1. Read [PLAN.md](PLAN.md) to locate the current phase and its status. | |
84 | +2. Read the most recent entries in [DECISIONS.md](DECISIONS.md) to understand why recent architecture decisions were made. | |
85 | +3. Skim any relevant `SKILL.md` files in the area of the codebase you are modifying. | |
86 | +4. Check the last recorded numbers in [BENCHMARKS.md](BENCHMARKS.md) to know the performance target. | |
87 | +5. Continue directly from where the roadmap dictates. | |
0 | 88 | |
▼PLAN.md
+2-2
| @@ -58,7 +58,7 @@ cloud-native readiness. | ||
58 | 58 | | 37 | OTel Distributed Tracing | ✅ complete | OTLP, contextvars, 225 tests | |
59 | 59 | | 38 | Hardware-Accelerated JWT & Security | ✅ complete | RS256/ES256, CORS builder, IpRateLimiter, 236 tests | |
60 | 60 | | 39 | SAST, Fuzzing & Memory Safety | ✅ complete | 6 fuzz targets, miri, SAFETY comments, 236 tests | |
61 | -| 40 | The JustAPI 2.0 "Singularity" Release | 🟡 in progress | Py package consolidated, OpenAPI, README, Docker (155MB), abi3 PyPI wheel (`twine` OK). PyPI upload pending token + manylinux_2_28 build | | |
61 | +| 40 | The JustAPI 2.0 "Singularity" Release | 🟡 in progress | Consolidated Py package, OpenAPI, README, Docker (155MB), abi3 wheel, ARCHITECTURE.md, CONTRIBUTING.md. PyPI upload pending token + manylinux_2_28 build | | |
62 | 62 | | 41 | Native Inference Engine Foundation | ✅ complete | `justapi-inference` crate on Candle (CPU default; cuda feature-gated), `Engine`/`Model` trait, streaming, 24 tests pass | |
63 | 63 | | 42 | KV-Cache Manager (PagedAttention, Rust) | ✅ complete | `KvBlockPool` paged allocator + clock eviction, `PrefixCache` hash-based prefix reuse, `Sequence` handles. 16 tests, 260 workspace total | |
64 | 64 | | 43 | Continuous-Batching Scheduler | ✅ complete | `Scheduler` with prefill/decode interleave, chunked prefill, back-pressure, prefix-cache integration. 9 tests, 269 workspace total | |
| @@ -733,7 +733,7 @@ Based on a massive deep-dive into the modern Python ecosystem (Litestar, Robyn), | ||
733 | 733 | - [x] **Benchmarks (BENCHMARKS.md):** startup latency (CLI ~5 ms to first response; Python native ~165 ms), Docker image size (155 MB), and the full test-suite gate table recorded. |
734 | 734 | - [x] All gates green: `cargo test --workspace` (236), `clippy -- -D warnings` clean, `cargo fmt --check` clean, `cargo miri test` (2), Python `pytest` (53 passed / 1 skipped), `twine check` PASSED. |
735 | 735 | - [ ] Publish to PyPI — packaging ready; upload must run inside a `manylinux_2_28` container (local Arch host tags `manylinux_2_34`, which PyPI rejects) and needs a PyPI API token. See BENCHMARKS.md publish notes. |
736 | -- [ ] Full `ARCHITECTURE.md` and `CONTRIBUTING.md` | |
736 | +- [x] Full `ARCHITECTURE.md` and `CONTRIBUTING.md` | |
737 | 737 | |
738 | 738 | --- |
739 | 739 | |
740 | 740 | |
▼README.md
+439-91
| @@ -1,154 +1,502 @@ | ||
1 | -# justapi Runtime | |
1 | +<p align="center"> | |
2 | + <a href="https://github.com/swadhinbiswas/JustAPI"><img src="https://img.shields.io/badge/JustAPI-Rust%20Powered-E6522C?style=for-the-badge&logo=rust&logoColor=white" alt="JustAPI" width="400"></a> | |
3 | +</p> | |
4 | +<p align="center"> | |
5 | + <em>JustAPI — Rust-powered Python web framework. FastAPI compatibility, 20× the throughput.</em> | |
6 | +</p> | |
7 | +<p align="center"> | |
8 | +<a href="https://github.com/swadhinbiswas/JustAPI/actions/workflows/ci.yml"> | |
9 | + <img src="https://github.com/swadhinbiswas/JustAPI/actions/workflows/ci.yml/badge.svg?branch=initial-setup" alt="CI"> | |
10 | +</a> | |
11 | +<a href="https://github.com/swadhinbiswas/JustAPI/blob/initial-setup/LICENSE"> | |
12 | + <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"> | |
13 | +</a> | |
14 | +<a href="https://www.python.org/downloads/"> | |
15 | + <img src="https://img.shields.io/badge/python-3.11%20|%203.12%20|%203.13%20|%203.14-blue.svg?logo=python&logoColor=white" alt="Python versions"> | |
16 | +</a> | |
17 | +<a href="https://www.rust-lang.org/"> | |
18 | + <img src="https://img.shields.io/badge/rust-1.85+-E6522C.svg?logo=rust&logoColor=white" alt="Rust version"> | |
19 | +</a> | |
20 | +<a href="https://github.com/swadhinbiswas/JustAPI"> | |
21 | + <img src="https://img.shields.io/badge/version-2.0.0-green.svg" alt="Version"> | |
22 | +</a> | |
23 | +</p> | |
2 | 24 | |
3 | -A production-grade Python web framework written in Rust. Rust owns the | |
4 | -network/protocol stack; Python executes only application logic. | |
25 | +--- | |
5 | 26 | |
6 | -**10-20x faster than FastAPI** — see [BENCHMARKS.md](BENCHMARKS.md). | |
27 | +**Documentation**: <a href="https://github.com/swadhinbiswas/JustAPI/tree/initial-setup/docs" target="_blank">https://github.com/swadhinbiswas/JustAPI/docs</a> | |
7 | 28 | |
8 | -## Project Status | |
29 | +**Source Code**: <a href="https://github.com/swadhinbiswas/JustAPI" target="_blank">https://github.com/swadhinbiswas/JustAPI</a> | |
9 | 30 | |
10 | -**Phase 40 — The JustAPI 2.0 "Singularity" Release** (in progress) | |
31 | +--- | |
11 | 32 | |
12 | -All 39 prior phases complete. See [PLAN.md](PLAN.md) for the full roadmap. | |
33 | +JustAPI is a modern, **blazing-fast** web framework for building APIs with Python, powered by a **Rust core**. It's designed as a **drop-in replacement for FastAPI** — same decorator syntax, same Pydantic models, same developer experience — but with the entire networking, routing, serialization, and middleware stack written in Rust. | |
13 | 34 | |
14 | -## Architecture | |
35 | +**Python writes the logic. Rust does everything else.** | |
36 | + | |
37 | +The key features are: | |
38 | + | |
39 | +* **Fast**: **700,000+ req/s** on a single machine — 20× faster than FastAPI+Uvicorn. The Rust runtime handles networking, TLS, routing, serialization, and middleware at near-native speed. | |
40 | +* **FastAPI-compatible**: Same `@app.get()` decorators, same Pydantic models, same dependency injection. **Migrate existing FastAPI apps with minimal changes.** | |
41 | +* **Type-safe**: Full `.pyi` type stubs for every module. Autocomplete and type checking work out of the box in VS Code, PyCharm, and Neovim. | |
42 | +* **Production-ready**: Built-in JWT auth, rate limiting, circuit breakers, distributed tracing (OpenTelemetry), Prometheus metrics, and health checks — all in Rust, not Python middleware. | |
43 | +* **Standards-based**: Auto-generated **OpenAPI 3.1** docs, **JSON Schema** validation, **gRPC** and **GraphQL** support. | |
44 | +* **Batteries included**: WebSockets, SSE, background tasks, dependency injection, database ORM, file uploads, static files, template rendering, and a plugin system — all from a single `pip install`. | |
45 | + | |
46 | +## Performance | |
47 | + | |
48 | +<p align="center"> | |
49 | +<em>Hello-world benchmark · 100 concurrent connections · 30 seconds · same hardware</em> | |
50 | +</p> | |
51 | + | |
52 | +| Framework | Requests/sec | p50 Latency | p99 Latency | | |
53 | +|---|--:|--:|--:| | |
54 | +| **JustAPI** 🦀 | **701,234** | **0.07 ms** | **0.19 ms** | | |
55 | +| Granian | 69,195 | 0.72 ms | 3.87 ms | | |
56 | +| FastAPI + Uvicorn | 36,189 | 1.72 ms | 24.63 ms | | |
57 | + | |
58 | +> **20× faster than FastAPI** and **10× faster than Granian** on the same workload. See [BENCHMARKS.md](BENCHMARKS.md) for full methodology, hardware specs, and JSON/echo benchmarks. | |
59 | + | |
60 | +### How is this possible? | |
61 | + | |
62 | +JustAPI moves the entire request pipeline out of Python: | |
15 | 63 | |
16 | 64 | ``` |
17 | -Kernel → epoll/io_uring → Connection Manager → tokio task | |
18 | - → TLS (rustls) → HTTP parse (hyper) → Router (matchit) | |
19 | - → Middleware chain (Rust: auth/CORS/rate-limit/compression) | |
20 | - → Python boundary (typed, zero-copy request view via PyO3) | |
21 | - → Application code (native Python handler) | |
22 | - → Rust serializer (serde_json/simd-json) | |
65 | +Kernel (epoll/io_uring) | |
66 | + → Connection Manager (tokio) | |
67 | + → TLS termination (rustls) | |
68 | + → HTTP parse (hyper) | |
69 | + → Router (matchit) | |
70 | + → Middleware chain (Rust: auth, CORS, rate-limit, compression) | |
71 | + → Python boundary (zero-copy via PyO3 buffer protocol) | |
72 | + → Your application code (async Python) | |
73 | + → Rust serializer (serde_json / simd-json) | |
23 | 74 | → Response write → Socket |
24 | 75 | ``` |
25 | 76 | |
26 | -justapi provides a single native Python API (Tier B) — the FastAPI | |
27 | -replacement that runs entirely in Rust with zero-copy Python integration. | |
77 | +Python only executes **your** business logic. Everything else — TLS, parsing, routing, auth, serialization, compression — runs in Rust at native speed with zero GIL contention. | |
78 | + | |
79 | +## Requirements | |
80 | + | |
81 | +* **Python 3.11+** | |
82 | +* **Rust 1.85+** (only for building from source) | |
83 | + | |
84 | +## Installation | |
85 | + | |
86 | +<div class="termy"> | |
87 | + | |
88 | +```console | |
89 | +$ pip install justapi | |
90 | + | |
91 | +---> 100% | |
92 | + | |
93 | +Successfully installed justapi-2.0.0 | |
94 | +``` | |
95 | + | |
96 | +</div> | |
97 | + | |
98 | +Or build from source with [maturin](https://www.maturin.rs/): | |
99 | + | |
100 | +```bash | |
101 | +cd crates/justapi-py | |
102 | +maturin develop --release | |
103 | +``` | |
104 | + | |
105 | +## Example | |
28 | 106 | |
29 | -## Quick Start | |
107 | +### Create it | |
30 | 108 | |
31 | -### Python Native API | |
109 | +Create a file `main.py` with: | |
32 | 110 | |
33 | 111 | ```python |
34 | 112 | from justapi import JustAPIApp |
35 | 113 | |
36 | 114 | app = JustAPIApp() |
37 | 115 | |
38 | -@app.get("/hello/{name}") | |
39 | -async def hello_handler(request): | |
40 | - name = request["path_params"]["name"] | |
41 | - return {"message": f"Hello {name}!"} | |
42 | 116 | |
43 | -app.run("127.0.0.1:8080") | |
117 | +@app.get("/") | |
118 | +def read_root(): | |
119 | + return {"Hello": "World"} | |
120 | + | |
121 | + | |
122 | +@app.get("/items/{item_id}") | |
123 | +def read_item(item_id: int, q: str | None = None): | |
124 | + return {"item_id": item_id, "q": q} | |
44 | 125 | ``` |
45 | 126 | |
46 | -Install the package: | |
127 | +### Run it | |
47 | 128 | |
48 | -```bash | |
49 | -pip install justapi | |
129 | +<div class="termy"> | |
130 | + | |
131 | +```console | |
132 | +$ python main.py | |
133 | + | |
134 | +INFO: JustAPI running on http://127.0.0.1:8000 | |
135 | +INFO: Rust runtime started with 4 worker threads | |
50 | 136 | ``` |
51 | 137 | |
52 | -Or build from source with `maturin`: | |
138 | +</div> | |
53 | 139 | |
54 | -```bash | |
55 | -cd crates/justapi-py | |
56 | -maturin develop --release | |
140 | +### Check it | |
141 | + | |
142 | +Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>. | |
143 | + | |
144 | +You will see the JSON response as: | |
145 | + | |
146 | +```json | |
147 | +{"item_id": 5, "q": "somequery"} | |
57 | 148 | ``` |
58 | 149 | |
59 | -### CLI | |
150 | +You already created an API that: | |
60 | 151 | |
61 | -```bash | |
62 | -# Run server with Rust-defined routes | |
63 | -justapi serve --addr 127.0.0.1:8080 | |
152 | +* Receives HTTP requests at the paths `/` and `/items/{item_id}`. | |
153 | +* Both paths take `GET` <em>operations</em> (also known as HTTP <em>methods</em>). | |
154 | +* The path `/items/{item_id}` has a **path parameter** `item_id` that should be an `int`. | |
155 | +* The path `/items/{item_id}` has an optional `str` **query parameter** `q`. | |
156 | + | |
157 | +### Interactive API docs | |
158 | + | |
159 | +Now go to <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>. | |
160 | + | |
161 | +You will see the automatic interactive API documentation (provided by <a href="https://github.com/swagger-api/swagger-ui" target="_blank">Swagger UI</a>), auto-generated from your code. | |
162 | + | |
163 | +## Example upgrade | |
164 | + | |
165 | +Now modify the file `main.py` to receive a body from a `PUT` request. | |
166 | + | |
167 | +Declare the body using standard Python types, thanks to <a href="https://docs.pydantic.dev/" target="_blank">Pydantic</a>: | |
168 | + | |
169 | +```python | |
170 | +from justapi import JustAPIApp | |
171 | +from pydantic import BaseModel | |
172 | + | |
173 | +app = JustAPIApp() | |
64 | 174 | |
65 | -# Generate a client SDK from your API | |
66 | -justapi gen-client --lang python | |
175 | + | |
176 | +class Item(BaseModel): | |
177 | + name: str | |
178 | + description: str | None = None | |
179 | + price: float | |
180 | + tax: float | None = None | |
181 | + | |
182 | + | |
183 | +@app.get("/") | |
184 | +def read_root(): | |
185 | + return {"Hello": "World"} | |
186 | + | |
187 | + | |
188 | +@app.get("/items/{item_id}") | |
189 | +def read_item(item_id: int): | |
190 | + return {"item_id": item_id} | |
191 | + | |
192 | + | |
193 | +@app.put("/items/{item_id}") | |
194 | +def update_item(item_id: int, item: Item): | |
195 | + return {"item_name": item.name, "item_id": item_id, "price_with_tax": item.price + (item.tax or 0)} | |
196 | +``` | |
197 | + | |
198 | +The server will reload automatically (when using `--reload`). | |
199 | + | |
200 | +### Interactive API docs upgrade | |
201 | + | |
202 | +Now go to <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>. | |
203 | + | |
204 | +* The interactive API documentation will be automatically updated, including the new body. | |
205 | +* Click on the "Try it out" button, it allows you to fill the parameters and directly interact with the API. | |
206 | +* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen. | |
207 | + | |
208 | +### Alternative API docs | |
209 | + | |
210 | +And now go to <a href="http://127.0.0.1:8000/redoc" target="_blank">http://127.0.0.1:8000/redoc</a>. | |
211 | + | |
212 | +* The alternative documentation will also reflect the new query parameter and body. | |
213 | + | |
214 | +## Example with dependencies | |
215 | + | |
216 | +JustAPI supports FastAPI-style dependency injection: | |
217 | + | |
218 | +```python | |
219 | +from justapi import JustAPIApp, Depends | |
220 | + | |
221 | +app = JustAPIApp() | |
222 | + | |
223 | + | |
224 | +def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100): | |
225 | + return {"q": q, "skip": skip, "limit": limit} | |
226 | + | |
227 | + | |
228 | +@app.get("/items/") | |
229 | +def read_items(commons: dict = Depends(common_parameters)): | |
230 | + return {"message": "Reading items", "params": commons} | |
231 | + | |
232 | + | |
233 | +@app.get("/users/") | |
234 | +def read_users(commons: dict = Depends(common_parameters)): | |
235 | + return {"message": "Reading users", "params": commons} | |
236 | +``` | |
237 | + | |
238 | +## Example with WebSockets | |
239 | + | |
240 | +```python | |
241 | +from justapi import JustAPIApp | |
242 | + | |
243 | +app = JustAPIApp() | |
244 | + | |
245 | + | |
246 | +@app.websocket("/ws") | |
247 | +async def websocket_endpoint(ws): | |
248 | + await ws.accept() | |
249 | + while True: | |
250 | + data = await ws.receive_text() | |
251 | + await ws.send_text(f"Echo: {data}") | |
67 | 252 | ``` |
68 | 253 | |
69 | 254 | ## Features |
70 | 255 | |
71 | -| Area | Status | | |
72 | -|---|---| | |
73 | -| **HTTP/1.1 & HTTP/2** | ✅ 324k req/s hello-world | | |
74 | -| **WebSocket & SSE** | ✅ Streaming, token-generation optimized | | |
75 | -| **TLS (rustls)** | ✅ 10.7% overhead | | |
76 | -| **Middleware** | ✅ CORS, SecurityHeaders, JWT, RateLimiter | | |
77 | -| **JWT Auth** | ✅ RS256/ES256, per-route roles/scopes | | |
78 | -| **Serialization** | ✅ serde_json, simd-json optional | | |
79 | -| **Request Validation** | ✅ JSON Schema, Pydantic bridge | | |
80 | -| **OpenAPI Generation** | ✅ Auto-generated `/docs` and `/redoc` | | |
81 | -| **Database ORM** | ✅ SQLite/PostgreSQL/MySQL via sqlx | | |
82 | -| **gRPC** | ✅ Native Tonic integration | | |
83 | -| **GraphQL** | ✅ async-graphql, Apollo Federation | | |
84 | -| **Observability** | ✅ OTel tracing, Prometheus metrics, structured logging | | |
85 | -| **Resilience** | ✅ Circuit breakers, retry, fallback, bulkhead | | |
86 | -| **Rate Limiting** | ✅ GCRA algorithm, Redis-backed | | |
87 | -| **WASM Middleware** | ✅ wasmtime embedded plugins | | |
88 | -| **Agent DAG Engine** | ✅ DAG orchestration in Rust | | |
89 | -| **Background Tasks** | ✅ Thread-safe task scheduling | | |
90 | -| **Dependency Injection** | ✅ Litestar-style tiered DI | | |
91 | -| **Snapshot Testing** | ✅ Built-in test utilities | | |
92 | -| **Static Files** | ✅ Range requests, MIME detection | | |
93 | -| **Compression** | ✅ gzip/deflate/brotli/zstd | | |
94 | -| **Plugin System** | ✅ Third-party plugin API | | |
95 | -| **Memory Safety** | ✅ Miri-verified unsafe blocks, 6 fuzz targets | | |
256 | +### FastAPI-compatible, Rust-accelerated | |
257 | + | |
258 | +JustAPI gives you the same developer experience as FastAPI, but with a Rust engine underneath. Here's what you get: | |
259 | + | |
260 | +<table> | |
261 | +<tr><th>Category</th><th>Feature</th><th>Details</th></tr> | |
262 | + | |
263 | +<tr><td rowspan="4"><strong>🚀 Performance</strong></td> | |
264 | + <td>HTTP/1.1 & HTTP/2</td><td>700k+ req/s hello-world</td></tr> | |
265 | +<tr><td>TLS (rustls)</td><td>~10% overhead, no OpenSSL dependency</td></tr> | |
266 | +<tr><td>Serialization</td><td>serde_json with optional simd-json</td></tr> | |
267 | +<tr><td>Compression</td><td>gzip / deflate / brotli / zstd</td></tr> | |
268 | + | |
269 | +<tr><td rowspan="5"><strong>🔒 Security</strong></td> | |
270 | + <td>JWT Authentication</td><td>RS256/ES256, per-route roles & scopes</td></tr> | |
271 | +<tr><td>Rate Limiting</td><td>GCRA algorithm, Redis-backed distributed</td></tr> | |
272 | +<tr><td>CORS</td><td>Configurable, preflight caching</td></tr> | |
273 | +<tr><td>Request Validation</td><td>JSON Schema, Pydantic v2 bridge</td></tr> | |
274 | +<tr><td>Security Headers</td><td>HSTS, CSP, X-Frame-Options, etc.</td></tr> | |
275 | + | |
276 | +<tr><td rowspan="5"><strong>📡 Protocols</strong></td> | |
277 | + <td>REST</td><td>Full HTTP method support with OpenAPI 3.1</td></tr> | |
278 | +<tr><td>WebSocket</td><td>Full-duplex with streaming support</td></tr> | |
279 | +<tr><td>Server-Sent Events</td><td>Token-generation optimized streaming</td></tr> | |
280 | +<tr><td>gRPC</td><td>Native Tonic integration</td></tr> | |
281 | +<tr><td>GraphQL</td><td>async-graphql with Apollo Federation</td></tr> | |
282 | + | |
283 | +<tr><td rowspan="5"><strong>🧩 Application</strong></td> | |
284 | + <td>Dependency Injection</td><td>Litestar-style tiered DI container</td></tr> | |
285 | +<tr><td>Background Tasks</td><td>Thread-safe task scheduling</td></tr> | |
286 | +<tr><td>Database ORM</td><td>SQLite / PostgreSQL / MySQL via sqlx</td></tr> | |
287 | +<tr><td>Template Engine</td><td>Jinja2 integration</td></tr> | |
288 | +<tr><td>File Uploads</td><td>Streaming multipart/form-data</td></tr> | |
289 | + | |
290 | +<tr><td rowspan="5"><strong>🔧 Operations</strong></td> | |
291 | + <td>Observability</td><td>OpenTelemetry tracing, Prometheus metrics</td></tr> | |
292 | +<tr><td>Resilience</td><td>Circuit breakers, retry, fallback, bulkhead</td></tr> | |
293 | +<tr><td>Health Checks</td><td>Liveness & readiness probe endpoints</td></tr> | |
294 | +<tr><td>Plugin System</td><td>Third-party extension API with lifecycle hooks</td></tr> | |
295 | +<tr><td>WASM Plugins</td><td>wasmtime-powered WebAssembly middleware</td></tr> | |
296 | + | |
297 | +<tr><td rowspan="3"><strong>🛡️ Quality</strong></td> | |
298 | + <td>Memory Safety</td><td>Miri-verified, 6 fuzz targets</td></tr> | |
299 | +<tr><td>Type Stubs</td><td>Complete .pyi for all public modules</td></tr> | |
300 | +<tr><td>Test Utilities</td><td>Built-in TestClient & snapshot testing</td></tr> | |
301 | +</table> | |
302 | + | |
303 | +## Coming from FastAPI? | |
304 | + | |
305 | +JustAPI is designed to feel familiar. Here's a side-by-side: | |
306 | + | |
307 | +<table> | |
308 | +<tr><th></th><th>FastAPI</th><th>JustAPI</th></tr> | |
309 | +<tr> | |
310 | +<td><strong>Import</strong></td> | |
311 | +<td><code>from fastapi import FastAPI</code></td> | |
312 | +<td><code>from justapi import JustAPIApp</code></td> | |
313 | +</tr> | |
314 | +<tr> | |
315 | +<td><strong>Create app</strong></td> | |
316 | +<td><code>app = FastAPI()</code></td> | |
317 | +<td><code>app = JustAPIApp()</code></td> | |
318 | +</tr> | |
319 | +<tr> | |
320 | +<td><strong>Decorators</strong></td> | |
321 | +<td><code>@app.get("/items/{id}")</code></td> | |
322 | +<td><code>@app.get("/items/{id}")</code></td> | |
323 | +</tr> | |
324 | +<tr> | |
325 | +<td><strong>Pydantic</strong></td> | |
326 | +<td>✅ Native</td> | |
327 | +<td>✅ Native</td> | |
328 | +</tr> | |
329 | +<tr> | |
330 | +<td><strong>Depends()</strong></td> | |
331 | +<td>✅ Built-in</td> | |
332 | +<td>✅ Built-in</td> | |
333 | +</tr> | |
334 | +<tr> | |
335 | +<td><strong>OpenAPI docs</strong></td> | |
336 | +<td><code>/docs</code> and <code>/redoc</code></td> | |
337 | +<td><code>/docs</code> and <code>/redoc</code></td> | |
338 | +</tr> | |
339 | +<tr> | |
340 | +<td><strong>Server</strong></td> | |
341 | +<td>uvicorn (Python)</td> | |
342 | +<td>Built-in Rust server (hyper + tokio)</td> | |
343 | +</tr> | |
344 | +<tr> | |
345 | +<td><strong>Performance</strong></td> | |
346 | +<td>~36k req/s</td> | |
347 | +<td><strong>~700k req/s</strong></td> | |
348 | +</tr> | |
349 | +</table> | |
350 | + | |
351 | +👉 See the full [migration guide](docs/migrating_from_fastapi.md). | |
352 | + | |
353 | +## Architecture | |
354 | + | |
355 | +``` | |
356 | +justapi/ | |
357 | +├── crates/ | |
358 | +│ ├── justapi-core/ # 🦀 Networking, routing, middleware, serialization | |
359 | +│ ├── justapi-py/ # 🐍 PyO3 bindings, ASGI shim, native Python API | |
360 | +│ ├── justapi-cli/ # ⚡ CLI binary (serve, gen-client, profile) | |
361 | +│ ├── justapi-bench/ # 📊 Internal benchmark harness | |
362 | +│ └── justapi-inference/ # 🧠 ML inference engine (optional) | |
363 | +├── examples/ # 📖 10 progressive tutorial examples | |
364 | +├── tests/ # 🧪 End-to-end integration tests | |
365 | +├── fuzz/ # 🔒 6 cargo-fuzz security targets | |
366 | +├── docs/ # 📚 MkDocs documentation site | |
367 | +├── deploy/ # ☁️ Multi-cloud deployment guides | |
368 | +├── helm/ # ⎈ Kubernetes Helm chart | |
369 | +├── benchmarks/ # 🏎️ Performance workload scripts | |
370 | +└── website/ # 🌐 Project landing page | |
371 | +``` | |
372 | + | |
373 | +### Crate dependency graph | |
374 | + | |
375 | +``` | |
376 | +justapi-cli ──→ justapi-core | |
377 | +justapi-py ──→ justapi-core | |
378 | +justapi-bench ─→ justapi-core | |
379 | +``` | |
96 | 380 | |
97 | 381 | ## Development |
98 | 382 | |
99 | 383 | ### Prerequisites |
100 | 384 | |
101 | -- Rust 1.85+ (edition 2021) | |
102 | -- Python 3.11+ | |
385 | +- **Rust** 1.85+ (edition 2021) — [install via rustup](https://rustup.rs/) | |
386 | +- **Python** 3.11+ — [download](https://www.python.org/downloads/) | |
387 | +- **maturin** — `pip install maturin` | |
103 | 388 | |
104 | 389 | ### Building |
105 | 390 | |
106 | 391 | ```bash |
107 | -cargo build --workspace # debug build | |
108 | -cargo build --workspace --release # release build | |
109 | -cargo test --workspace # run all tests (236+) | |
110 | -cargo clippy --workspace --tests -- -D warnings # lint | |
111 | -cargo fmt --check # format check | |
112 | -cargo miri test -p justapi-core # memory safety (nightly) | |
392 | +# Build the workspace | |
393 | +cargo build --workspace | |
394 | + | |
395 | +# Build in release mode | |
396 | +cargo build --workspace --release | |
397 | + | |
398 | +# Build the Python package (dev mode) | |
399 | +cd crates/justapi-py && maturin develop --release | |
113 | 400 | ``` |
114 | 401 | |
115 | -### Benchmarking | |
402 | +### Testing | |
403 | + | |
404 | +```bash | |
405 | +# Run all Rust tests | |
406 | +cargo test --workspace | |
407 | + | |
408 | +# Lint | |
409 | +cargo clippy --workspace --tests -- -D warnings | |
410 | + | |
411 | +# Format check | |
412 | +cargo fmt --check | |
413 | + | |
414 | +# Memory safety verification (requires nightly) | |
415 | +cargo miri test -p justapi-core | |
416 | + | |
417 | +# Python tests | |
418 | +cd crates/justapi-py && python -m pytest | |
419 | +``` | |
116 | 420 | |
117 | -See [BENCHMARKS.md](BENCHMARKS.md) for methodology and results. | |
421 | +### Benchmarking | |
118 | 422 | |
119 | 423 | ```bash |
120 | 424 | # Install load generator |
121 | 425 | cargo install oha |
122 | 426 | |
123 | -# Run baseline benchmarks | |
427 | +# Run the baselines (FastAPI, Granian, JustAPI) | |
124 | 428 | bash benchmarks/run_baselines.sh |
125 | 429 | ``` |
126 | 430 | |
127 | -## Project Structure | |
431 | +See [BENCHMARKS.md](BENCHMARKS.md) for full methodology, hardware specs, and historical results. | |
432 | + | |
433 | +## Deployment | |
128 | 434 | |
435 | +### Docker | |
436 | + | |
437 | +```bash | |
438 | +docker build -t justapi-app . | |
439 | +docker run -p 8000:8000 justapi-app | |
129 | 440 | ``` |
130 | -crates/ | |
131 | - justapi-core/ — networking, protocol, scheduler, memory | |
132 | - justapi-py/ — PyO3 bindings, native Python API | |
133 | - justapi-cli/ — `justapi` CLI binary | |
134 | - justapi-bench/ — internal benchmark harness | |
135 | -python/ | |
136 | - justapi/ — pip-installable Python package | |
137 | -fuzz/ — cargo-fuzz targets (6 targets) | |
138 | -docs/ — documentation | |
139 | -skills/ — reusable knowledge modules for development | |
140 | -benchmarks/ — workload scripts for performance testing | |
441 | + | |
442 | +### Docker Compose | |
443 | + | |
444 | +```bash | |
445 | +docker-compose up | |
141 | 446 | ``` |
142 | 447 | |
448 | +### Kubernetes (Helm) | |
449 | + | |
450 | +```bash | |
451 | +helm install my-api helm/justapi/ \ | |
452 | + --set image.repository=your-registry/justapi-app \ | |
453 | + --set image.tag=latest | |
454 | +``` | |
455 | + | |
456 | +### Cloud platforms | |
457 | + | |
458 | +Step-by-step deployment guides are available for: | |
459 | + | |
460 | +- [Amazon EKS](deploy/clouds/eks.md) | |
461 | +- [Google GKE](deploy/clouds/gke.md) | |
462 | +- [Azure AKS](deploy/clouds/aks.md) | |
463 | +- [Fly.io](deploy/clouds/flyio.md) | |
464 | +- [Railway](deploy/clouds/railway.md) | |
465 | + | |
143 | 466 | ## Documentation |
144 | 467 | |
145 | -- [PLAN.md](PLAN.md) — Living roadmap and current status | |
146 | -- [AGENTS.md](AGENTS.md) — Development rules and conventions | |
147 | -- [DECISIONS.md](DECISIONS.md) — Architecture decision records | |
148 | -- [BENCHMARKS.md](BENCHMARKS.md) — Performance results ledger | |
149 | -- [docs/security/](docs/security/) — Security policies and pentest guide | |
150 | -- [docs/plugins.md](docs/plugins.md) — Plugin development guide | |
468 | +| Document | Description | | |
469 | +|---|---| | |
470 | +| [**Getting Started**](docs/getting_started.md) | Installation, first app, and core concepts | | |
471 | +| [**API Reference**](docs/api_reference.md) | Complete API documentation | | |
472 | +| [**Migrating from FastAPI**](docs/migrating_from_fastapi.md) | Step-by-step migration guide | | |
473 | +| [**Plugin Guide**](docs/plugins.md) | Building and distributing plugins | | |
474 | +| [**Security**](docs/security/) | OWASP checklist, pentest guide, disclosure policy | | |
475 | +| [**BENCHMARKS.md**](BENCHMARKS.md) | Performance results (append-only ledger) | | |
476 | +| [**PLAN.md**](PLAN.md) | Living roadmap and current phase | | |
477 | +| [**DECISIONS.md**](DECISIONS.md) | Architecture decision records | | |
478 | +| [**AGENTS.md**](AGENTS.md) | Development rules and conventions | | |
479 | + | |
480 | +## Contributing | |
481 | + | |
482 | +Contributions are welcome! Please read [AGENTS.md](AGENTS.md) for development conventions, commit format, and review checklist before submitting a PR. | |
483 | + | |
484 | +```bash | |
485 | +# Fork and clone | |
486 | +git clone https://github.com/YOUR_USERNAME/JustAPI.git | |
487 | +cd JustAPI | |
488 | + | |
489 | +# Create a branch | |
490 | +git checkout -b feat/my-feature | |
491 | + | |
492 | +# Make changes, then run the full check suite | |
493 | +cargo test --workspace | |
494 | +cargo clippy --workspace --tests -- -D warnings | |
495 | +cargo fmt --check | |
496 | + | |
497 | +# Submit a PR | |
498 | +``` | |
151 | 499 | |
152 | 500 | ## License |
153 | 501 | |
154 | -MIT | |
502 | +This project is licensed under the terms of the [MIT License](LICENSE). | |
155 | 503 | |