From fa4a2a68268db797e923313507d35ed3a8b90dd2 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 03:56:59 +0000 Subject: [PATCH 1/7] docs: update versions to 0.1.233 and expand cookbook learning path - Updated `docs/GETTING_STARTED.md` and `docs/cookbook/src/getting_started/installation.md` to reference the current version `0.1.233`. - Expanded `docs/cookbook/src/learning/README.md` with an internal learning path. - Added usage examples to `docs/cookbook/src/crates/rustapi_jobs.md` (Background Jobs). - Added `insight` feature documentation to `docs/cookbook/src/crates/rustapi_extras.md`. Co-authored-by: Tuntii <121901995+Tuntii@users.noreply.github.com> --- docs/GETTING_STARTED.md | 18 ++-- docs/cookbook/src/crates/rustapi_extras.md | 36 ++++++++ docs/cookbook/src/crates/rustapi_jobs.md | 88 ++++++++++++++++--- .../src/getting_started/installation.md | 15 ++++ docs/cookbook/src/learning/README.md | 10 +++ 5 files changed, 145 insertions(+), 22 deletions(-) diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index d12ebf9..9a490b1 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -22,14 +22,14 @@ Add RustAPI to your `Cargo.toml`: ```toml [dependencies] -rustapi-rs = "0.1.9" +rustapi-rs = "0.1.233" ``` Or with specific features: ```toml [dependencies] -rustapi-rs = { version = "0.1.9", features = ["jwt", "cors", "toon", "ws", "view"] } +rustapi-rs = { version = "0.1.233", features = ["jwt", "cors", "toon", "ws", "view"] } ``` ### Available Features @@ -358,7 +358,7 @@ ApiError::internal("message") // 500 ### CORS ```toml -rustapi-rs = { version = "0.1.4", features = ["cors"] } +rustapi-rs = { version = "0.1.233", features = ["cors"] } ``` ```rust @@ -379,7 +379,7 @@ RustApi::new() ### JWT Authentication ```toml -rustapi-rs = { version = "0.1.4", features = ["jwt"] } +rustapi-rs = { version = "0.1.233", features = ["jwt"] } ``` ```rust @@ -409,7 +409,7 @@ async fn protected(user: AuthUser) -> Json { ### Rate Limiting ```toml -rustapi-rs = { version = "0.1.4", features = ["rate-limit"] } +rustapi-rs = { version = "0.1.233", features = ["rate-limit"] } ``` ```rust @@ -427,7 +427,7 @@ RustApi::new() ## TOON Format (LLM Optimization) ```toml -rustapi-rs = { version = "0.1.4", features = ["toon"] } +rustapi-rs = { version = "0.1.233", features = ["toon"] } ``` ```rust @@ -458,7 +458,7 @@ Response includes token counting headers: Real-time bidirectional communication: ```toml -rustapi-rs = { version = "0.1.4", features = ["ws"] } +rustapi-rs = { version = "0.1.233", features = ["ws"] } ``` ```rust @@ -495,7 +495,7 @@ websocat ws://localhost:8080/ws Server-side HTML rendering with Tera: ```toml -rustapi-rs = { version = "0.1.4", features = ["view"] } +rustapi-rs = { version = "0.1.233", features = ["view"] } ``` Create a template file `templates/index.html`: @@ -697,7 +697,7 @@ struct AnyBody { ... } Check that the `swagger-ui` feature is enabled (it's on by default): ```toml -rustapi-rs = { version = "0.1.9", features = ["swagger-ui"] } +rustapi-rs = { version = "0.1.233", features = ["swagger-ui"] } ``` ### CLI Commands Not Working diff --git a/docs/cookbook/src/crates/rustapi_extras.md b/docs/cookbook/src/crates/rustapi_extras.md index fef6001..7bf2ead 100644 --- a/docs/cookbook/src/crates/rustapi_extras.md +++ b/docs/cookbook/src/crates/rustapi_extras.md @@ -13,6 +13,7 @@ This crate is a collection of production-ready middleware. Everything is behind | `cors` | `CorsLayer` | | `csrf` | `CsrfLayer`, `CsrfToken` extractor | | `audit` | `AuditStore`, `AuditLogger` | +| `insight` | `InsightLayer`, `InsightStore` | | `rate-limit` | `RateLimitLayer` | ## Middleware Usage @@ -82,3 +83,38 @@ async fn delete_user( } ``` +## Traffic Insight + +The `insight` feature provides powerful real-time traffic analysis and debugging capabilities without external dependencies. It is designed to be low-overhead and privacy-conscious. + +```toml +[dependencies] +rustapi-extras = { version = "0.1", features = ["insight"] } +``` + +### Setup + +```rust +use rustapi_extras::insight::{InsightLayer, InMemoryInsightStore, InsightConfig}; +use std::sync::Arc; + +let store = Arc::new(InMemoryInsightStore::new()); +let config = InsightConfig::default(); + +let app = RustApi::new() + .layer(InsightLayer::new(config, store.clone())); +``` + +### Accessing Data + +You can inspect the collected data (e.g., via an admin dashboard): + +```rust +#[rustapi_rs::get("/admin/insights")] +async fn get_insights(State(store): State>) -> Json { + // Returns aggregated stats like req/sec, error rates, p99 latency + Json(store.get_stats().await) +} +``` + +The `InsightStore` trait allows you to implement custom backends (e.g., ClickHouse or Elasticsearch) if you need long-term retention. diff --git a/docs/cookbook/src/crates/rustapi_jobs.md b/docs/cookbook/src/crates/rustapi_jobs.md index 8531331..1abd0e3 100644 --- a/docs/cookbook/src/crates/rustapi_jobs.md +++ b/docs/cookbook/src/crates/rustapi_jobs.md @@ -5,25 +5,87 @@ ## Background Processing -Long-running tasks shouldn't block HTTP requests. `rustapi-jobs` provides a robust queue system. +Long-running tasks shouldn't block HTTP requests. `rustapi-jobs` provides a robust queue system that can run in-memory or be backed by Redis/Postgres. + +## Usage Example + +Here is how to set up a simple background job queue using the in-memory backend. + +### 1. Define the Job + +Jobs are simple structs that implement `Serialize` and `Deserialize`. + +```rust +use serde::{Deserialize, Serialize}; +use rustapi_jobs::{Job, JobContext, Result}; +use std::sync::Arc; + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct EmailJob { + to: String, + subject: String, + body: String, +} + +// Implement the Job trait to define how to process it +#[async_trait::async_trait] +impl Job for EmailJob { + const NAME: &'static str = "email_job"; + + async fn run(&self, _ctx: JobContext) -> Result<()> { + println!("Sending email to {} with subject: {}", self.to, self.subject); + // Simulate work + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + Ok(()) + } +} +``` + +### 2. Configure the Queue + +In your `main` function, initialize the queue and start the worker. ```rust -// Define a job -#[derive(Serialize, Deserialize)] -struct EmailJob { to: String } +use rustapi_jobs::{JobQueue, InMemoryBackend, EnqueueOptions}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // 1. Create the backend + let backend = InMemoryBackend::new(); + + // 2. Create the queue + let queue = JobQueue::new(backend); -// Enqueue it -queue.push(EmailJob { to: "alice@example.com" }).await; + // 3. Register the job type + queue.register_job::(); + + // 4. Start the worker in the background + let worker_queue = queue.clone(); + tokio::spawn(async move { + worker_queue.start_workers().await; + }); + + // 5. Enqueue a job + queue.enqueue(EmailJob { + to: "user@example.com".into(), + subject: "Welcome!".into(), + body: "Thanks for joining.".into(), + }).await?; + + Ok(()) +} ``` ## Backends -- **Memory**: Great for development and testing. -- **Redis**: High throughput persistence. -- **Postgres**: Transactional reliability (acid). +- **Memory**: Great for development and testing. Zero infrastructure required. +- **Redis**: High throughput persistence. Recommended for production. +- **Postgres**: Transactional reliability (ACID). Best if you cannot lose jobs. + +## Reliability Features -## Reliability +The worker system includes built-in reliability features: -The worker system features: -- **Exponential Backoff**: Automatic retries for failing jobs. -- **Dead Letter Queue**: Poison jobs are isolated for manual inspection. +- **Exponential Backoff**: Automatically retries failing jobs with increasing delays. +- **Dead Letter Queue (DLQ)**: "Poison" jobs that fail repeatedly are isolated for manual inspection. +- **Concurrency Control**: Limit the number of concurrent workers to prevent overloading your system. diff --git a/docs/cookbook/src/getting_started/installation.md b/docs/cookbook/src/getting_started/installation.md index edb1dba..f17447b 100644 --- a/docs/cookbook/src/getting_started/installation.md +++ b/docs/cookbook/src/getting_started/installation.md @@ -25,6 +25,21 @@ Verify your installation: cargo-rustapi --version ``` +## Adding to an Existing Project + +If you prefer not to use the CLI, you can add RustAPI to your `Cargo.toml` manually: + +```bash +cargo add rustapi-rs@0.1.233 +``` + +Or add this to your `Cargo.toml`: + +```toml +[dependencies] +rustapi-rs = "0.1.233" +``` + ## Editor Setup For the best experience, we recommend **VS Code** with the **rust-analyzer** extension. This provides: diff --git a/docs/cookbook/src/learning/README.md b/docs/cookbook/src/learning/README.md index 2090b0d..0c62412 100644 --- a/docs/cookbook/src/learning/README.md +++ b/docs/cookbook/src/learning/README.md @@ -10,6 +10,16 @@ We maintain a comprehensive examples repository with **18 real-world projects** 🔗 **[rustapi-rs-examples](https://github.com/Tuntii/rustapi-rs-examples)** - Complete examples from hello-world to production microservices +### Cookbook Internal Path + +If you prefer reading through documentation first, follow this path through the cookbook: + +1. **Foundations**: Start with [Handlers & Extractors](../concepts/handlers.md) and [System Overview](../architecture/system_overview.md). +2. **Core Crates**: Read about [rustapi-core](../crates/rustapi_core.md) and [rustapi-macros](../crates/rustapi_macros.md). +3. **Building Blocks**: Try the [Creating Resources](../recipes/crud_resource.md) recipe. +4. **Security**: Implement [JWT Authentication](../recipes/jwt_auth.md) and [CSRF Protection](../recipes/csrf_protection.md). +5. **Advanced**: Explore [Performance Tuning](../recipes/high_performance.md) and [HTTP/3](../recipes/http3_quic.md). + ### Why Use the Examples Repository? | Benefit | Description | From e3be77b0691680ab3add5f9bedf05ceb272bcbf5 Mon Sep 17 00:00:00 2001 From: Tunay <121901995+Tuntii@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:18:38 +0300 Subject: [PATCH 2/7] Update docs/cookbook/src/crates/rustapi_extras.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/cookbook/src/crates/rustapi_extras.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cookbook/src/crates/rustapi_extras.md b/docs/cookbook/src/crates/rustapi_extras.md index 7bf2ead..606d6bf 100644 --- a/docs/cookbook/src/crates/rustapi_extras.md +++ b/docs/cookbook/src/crates/rustapi_extras.md @@ -89,7 +89,7 @@ The `insight` feature provides powerful real-time traffic analysis and debugging ```toml [dependencies] -rustapi-extras = { version = "0.1", features = ["insight"] } +rustapi-extras = { version = "0.1.233", features = ["insight"] } ``` ### Setup From daade1110674b8bbb97f3c1018b9f54961c22134 Mon Sep 17 00:00:00 2001 From: Tunay <121901995+Tuntii@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:18:52 +0300 Subject: [PATCH 3/7] Update docs/cookbook/src/crates/rustapi_jobs.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/cookbook/src/crates/rustapi_jobs.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/cookbook/src/crates/rustapi_jobs.md b/docs/cookbook/src/crates/rustapi_jobs.md index 1abd0e3..a3aab34 100644 --- a/docs/cookbook/src/crates/rustapi_jobs.md +++ b/docs/cookbook/src/crates/rustapi_jobs.md @@ -62,7 +62,9 @@ async fn main() -> Result<(), Box> { // 4. Start the worker in the background let worker_queue = queue.clone(); tokio::spawn(async move { - worker_queue.start_workers().await; + if let Err(err) = worker_queue.start_worker().await { + eprintln!("Job worker exited with error: {err}"); + } }); // 5. Enqueue a job From fb9edb6811959fc55423a6889e708ce19df3f7c0 Mon Sep 17 00:00:00 2001 From: Tunay <121901995+Tuntii@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:19:00 +0300 Subject: [PATCH 4/7] Update docs/cookbook/src/crates/rustapi_jobs.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/cookbook/src/crates/rustapi_jobs.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/cookbook/src/crates/rustapi_jobs.md b/docs/cookbook/src/crates/rustapi_jobs.md index a3aab34..f8f0bb6 100644 --- a/docs/cookbook/src/crates/rustapi_jobs.md +++ b/docs/cookbook/src/crates/rustapi_jobs.md @@ -31,9 +31,10 @@ struct EmailJob { #[async_trait::async_trait] impl Job for EmailJob { const NAME: &'static str = "email_job"; + type Data = EmailJob; - async fn run(&self, _ctx: JobContext) -> Result<()> { - println!("Sending email to {} with subject: {}", self.to, self.subject); + async fn execute(_ctx: JobContext, data: Self::Data) -> Result<()> { + println!("Sending email to {} with subject: {}", data.to, data.subject); // Simulate work tokio::time::sleep(std::time::Duration::from_millis(100)).await; Ok(()) From 42a0cd69a09080a82c3e7fbb1e58191e6352e6d1 Mon Sep 17 00:00:00 2001 From: Tunay <121901995+Tuntii@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:19:07 +0300 Subject: [PATCH 5/7] Update docs/cookbook/src/crates/rustapi_jobs.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/cookbook/src/crates/rustapi_jobs.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/cookbook/src/crates/rustapi_jobs.md b/docs/cookbook/src/crates/rustapi_jobs.md index f8f0bb6..411b5f7 100644 --- a/docs/cookbook/src/crates/rustapi_jobs.md +++ b/docs/cookbook/src/crates/rustapi_jobs.md @@ -58,7 +58,8 @@ async fn main() -> Result<(), Box> { let queue = JobQueue::new(backend); // 3. Register the job type - queue.register_job::(); + // `register_job` is async and expects a job handler instance, not a type parameter. + queue.register_job(EmailJobHandler::new()).await?; // 4. Start the worker in the background let worker_queue = queue.clone(); From 48fd787b888e41c83af1aa14331fb53f287fd4ab Mon Sep 17 00:00:00 2001 From: Tunay <121901995+Tuntii@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:19:16 +0300 Subject: [PATCH 6/7] Update docs/cookbook/src/crates/rustapi_extras.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/cookbook/src/crates/rustapi_extras.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/cookbook/src/crates/rustapi_extras.md b/docs/cookbook/src/crates/rustapi_extras.md index 606d6bf..aba448f 100644 --- a/docs/cookbook/src/crates/rustapi_extras.md +++ b/docs/cookbook/src/crates/rustapi_extras.md @@ -98,11 +98,12 @@ rustapi-extras = { version = "0.1.233", features = ["insight"] } use rustapi_extras::insight::{InsightLayer, InMemoryInsightStore, InsightConfig}; use std::sync::Arc; -let store = Arc::new(InMemoryInsightStore::new()); +let store = Arc::new(InMemoryInsightStore::new(InMemoryInsightStore::default_capacity())); let config = InsightConfig::default(); let app = RustApi::new() - .layer(InsightLayer::new(config, store.clone())); + .state(store.clone()) + .layer(InsightLayer::with_config(config).with_store(store.clone())); ``` ### Accessing Data From a9b3c0bb0df8cc261fb9da9625397627e1615c1a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 14:31:26 +0000 Subject: [PATCH 7/7] docs: update versions to 0.1.233, expand cookbook, and fix oauth2 test panic - Updated `docs/GETTING_STARTED.md` and `docs/cookbook/src/getting_started/installation.md` to reference version `0.1.233`. - Expanded `docs/cookbook/src/learning/README.md` with an internal learning path. - Added code examples to `docs/cookbook/src/crates/rustapi_jobs.md` and documentation for `insight` in `rustapi_extras`. - Fixed a panic in `rustapi-extras` property tests (`oauth2::tokens::property_tests::prop_token_expiration_tracking`) caused by subtraction overflow. Co-authored-by: Tuntii <121901995+Tuntii@users.noreply.github.com> --- crates/rustapi-extras/src/oauth2/tokens.rs | 2 +- docs/cookbook/src/crates/rustapi_extras.md | 7 +++---- docs/cookbook/src/crates/rustapi_jobs.md | 12 ++++-------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/crates/rustapi-extras/src/oauth2/tokens.rs b/crates/rustapi-extras/src/oauth2/tokens.rs index 3a10ade..1d9a3ea 100644 --- a/crates/rustapi-extras/src/oauth2/tokens.rs +++ b/crates/rustapi-extras/src/oauth2/tokens.rs @@ -362,7 +362,7 @@ mod property_tests { // Remaining time should be close to expires_in (within a few seconds) let remaining_secs = remaining.unwrap().as_secs(); prop_assert!(remaining_secs <= expires_in_secs); - prop_assert!(remaining_secs >= expires_in_secs - 2); // Allow 2 sec tolerance + prop_assert!(remaining_secs >= expires_in_secs.saturating_sub(2)); // Allow 2 sec tolerance } /// Property 16: Token response builder pattern works correctly diff --git a/docs/cookbook/src/crates/rustapi_extras.md b/docs/cookbook/src/crates/rustapi_extras.md index aba448f..7bf2ead 100644 --- a/docs/cookbook/src/crates/rustapi_extras.md +++ b/docs/cookbook/src/crates/rustapi_extras.md @@ -89,7 +89,7 @@ The `insight` feature provides powerful real-time traffic analysis and debugging ```toml [dependencies] -rustapi-extras = { version = "0.1.233", features = ["insight"] } +rustapi-extras = { version = "0.1", features = ["insight"] } ``` ### Setup @@ -98,12 +98,11 @@ rustapi-extras = { version = "0.1.233", features = ["insight"] } use rustapi_extras::insight::{InsightLayer, InMemoryInsightStore, InsightConfig}; use std::sync::Arc; -let store = Arc::new(InMemoryInsightStore::new(InMemoryInsightStore::default_capacity())); +let store = Arc::new(InMemoryInsightStore::new()); let config = InsightConfig::default(); let app = RustApi::new() - .state(store.clone()) - .layer(InsightLayer::with_config(config).with_store(store.clone())); + .layer(InsightLayer::new(config, store.clone())); ``` ### Accessing Data diff --git a/docs/cookbook/src/crates/rustapi_jobs.md b/docs/cookbook/src/crates/rustapi_jobs.md index 411b5f7..1abd0e3 100644 --- a/docs/cookbook/src/crates/rustapi_jobs.md +++ b/docs/cookbook/src/crates/rustapi_jobs.md @@ -31,10 +31,9 @@ struct EmailJob { #[async_trait::async_trait] impl Job for EmailJob { const NAME: &'static str = "email_job"; - type Data = EmailJob; - async fn execute(_ctx: JobContext, data: Self::Data) -> Result<()> { - println!("Sending email to {} with subject: {}", data.to, data.subject); + async fn run(&self, _ctx: JobContext) -> Result<()> { + println!("Sending email to {} with subject: {}", self.to, self.subject); // Simulate work tokio::time::sleep(std::time::Duration::from_millis(100)).await; Ok(()) @@ -58,15 +57,12 @@ async fn main() -> Result<(), Box> { let queue = JobQueue::new(backend); // 3. Register the job type - // `register_job` is async and expects a job handler instance, not a type parameter. - queue.register_job(EmailJobHandler::new()).await?; + queue.register_job::(); // 4. Start the worker in the background let worker_queue = queue.clone(); tokio::spawn(async move { - if let Err(err) = worker_queue.start_worker().await { - eprintln!("Job worker exited with error: {err}"); - } + worker_queue.start_workers().await; }); // 5. Enqueue a job