Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 62 additions & 1 deletion docs/cookbook/src/crates/rustapi_extras.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ This crate is a collection of production-ready middleware. Everything is behind
| `audit` | `AuditStore`, `AuditLogger` |
| `insight` | `InsightLayer`, `InsightStore` |
| `rate-limit` | `RateLimitLayer` |
| `replay` | `ReplayLayer` (Time-Travel Debugging) |
| `timeout` | `TimeoutLayer` |
| `guard` | `PermissionGuard` |
| `sanitization` | Input sanitization utilities |

## Middleware Usage

Expand Down Expand Up @@ -135,7 +139,7 @@ let app = RustApi::new()

### Structured Logging

Emit logs as JSON for aggregators like Datadog or Splunk.
Emit logs as JSON for aggregators like Datadog or Splunk. This is different from request logging; it formats your application logs.

```rust
use rustapi_extras::structured_logging::{StructuredLoggingLayer, JsonFormatter};
Expand Down Expand Up @@ -184,6 +188,33 @@ let app = RustApi::new()
.layer(ApiKeyLayer::new("my-secret-key"));
```

### Permission Guards

The `guard` feature provides role-based access control (RBAC) helpers.

```rust
use rustapi_extras::guard::PermissionGuard;

// Only allows users with "admin" role
#[rustapi_rs::get("/admin")]
async fn admin_panel(
_guard: PermissionGuard
) -> &'static str {
"Welcome Admin"
}
```

### Input Sanitization

The `sanitization` feature helps prevent XSS by cleaning user input.

```rust
use rustapi_extras::sanitization::sanitize_html;

let safe_html = sanitize_html("<script>alert(1)</script>Hello");
// Result: "&lt;script&gt;alert(1)&lt;/script&gt;Hello"
```

## Resilience

### Circuit Breaker
Expand All @@ -208,6 +239,18 @@ let app = RustApi::new()
.layer(RetryLayer::default());
```

### Timeout

Ensure requests don't hang indefinitely.

```rust
use rustapi_extras::timeout::TimeoutLayer;
use std::time::Duration;

let app = RustApi::new()
.layer(TimeoutLayer::new(Duration::from_secs(30)));
```

## Optimization

### Caching
Expand All @@ -231,3 +274,21 @@ use rustapi_extras::dedup::DedupLayer;
let app = RustApi::new()
.layer(DedupLayer::new());
```

## Debugging

### Time-Travel Debugging (Replay)

The `replay` feature allows you to record production traffic and replay it locally for debugging.

See the [Time-Travel Debugging Recipe](../recipes/replay.md) for full details.

```rust
use rustapi_extras::replay::{ReplayLayer, ReplayConfig, InMemoryReplayStore};

let replay_config = ReplayConfig::default();
let store = InMemoryReplayStore::new(1_000);

let app = RustApi::new()
.layer(ReplayLayer::new(replay_config).with_store(store));
```
37 changes: 23 additions & 14 deletions docs/cookbook/src/crates/rustapi_jobs.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,36 @@ Long-running tasks shouldn't block HTTP requests. `rustapi-jobs` provides a robu

Here is how to set up a simple background job queue using the in-memory backend.

### 1. Define the Job
### 1. Define the Job and Data

Jobs are simple structs that implement `Serialize` and `Deserialize`.
Jobs are separated into two parts:
1. The **Data** struct (the payload), which must be serializable.
2. The **Job** struct (the handler), which contains the logic.

```rust
use serde::{Deserialize, Serialize};
use rustapi_jobs::{Job, JobContext, Result};
use std::sync::Arc;
use async_trait::async_trait;

// 1. The payload data
#[derive(Serialize, Deserialize, Debug, Clone)]
struct EmailJob {
struct EmailJobData {
to: String,
subject: String,
body: String,
}

// Implement the Job trait to define how to process it
#[async_trait::async_trait]
// 2. The handler struct (usually stateless)
#[derive(Clone)]
struct EmailJob;

#[async_trait]
impl Job for EmailJob {
const NAME: &'static str = "email_job";
type Data = EmailJobData;

async fn run(&self, _ctx: JobContext) -> Result<()> {
println!("Sending email to {} with subject: {}", self.to, self.subject);
async fn execute(&self, _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(())
Expand All @@ -46,7 +53,7 @@ impl Job for EmailJob {
In your `main` function, initialize the queue and start the worker.

```rust
use rustapi_jobs::{JobQueue, InMemoryBackend, EnqueueOptions};
use rustapi_jobs::{JobQueue, InMemoryBackend};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -56,17 +63,19 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 2. Create the queue
let queue = JobQueue::new(backend);

// 3. Register the job type
queue.register_job::<EmailJob>();
// 3. Register the job handler
queue.register_job(EmailJob).await;

// 4. Start the worker in the background
let worker_queue = queue.clone();
tokio::spawn(async move {
worker_queue.start_workers().await;
if let Err(e) = worker_queue.start_worker().await {
eprintln!("Worker failed: {:?}", e);
}
});

// 5. Enqueue a job
queue.enqueue(EmailJob {
// 5. Enqueue a job (pass the DATA, not the handler)
queue.enqueue::<EmailJob>(EmailJobData {
to: "user@example.com".into(),
subject: "Welcome!".into(),
body: "Thanks for joining.".into(),
Expand Down
9 changes: 9 additions & 0 deletions docs/cookbook/src/crates/rustapi_testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
1. **In-process API testing**: Testing your endpoints without binding to a real TCP port.
2. **External service mocking**: Mocking downstream services (like payment gateways or auth providers) that your API calls.

## Installation

Add the crate to your `dev-dependencies`:

```toml
[dev-dependencies]
rustapi-testing = { version = "0.1.300" }
```

## The `TestClient`

Integration testing is often slow and painful because it involves spinning up a server, waiting for ports, and managing child processes. `TestClient` solves this by wrapping your `RustApi` application and executing requests directly against the service layer.
Expand Down
Loading