Skip to content

Commit 7e78dc4

Browse files
committed
Document and refactor optional features and middleware
Expanded documentation in README files to detail optional features such as JWT, CORS, rate limiting, and configuration management. Updated Cargo.toml feature flags for clarity and consistency. Refactored feature-gated re-exports in lib.rs for simpler usage and improved documentation. Improved header extraction test to handle duplicate headers correctly.
1 parent de030dd commit 7e78dc4

6 files changed

Lines changed: 170 additions & 179 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,22 @@ We believe that writing high-performance, type-safe web APIs in Rust shouldn't r
3030
- **📝 Automatic OpenAPI**: Your code *is* your documentation. Swagger UI is served at `/docs` out of the box.
3131
- **✅ Built-in Validation**: Add `#[validate(email)]` to your structs and get automatic 422 error handling.
3232
- **🧩 Intuitive Routing**: Radix-tree based routing with simple macros `#[rustapi::get]`, `#[rustapi::post]`.
33-
- **🔋 Batteries Included**: Tracing, graceful shutdown, state management, and error handling are pre-configured.
33+
- **🔋 Batteries Included**: Middleware, JWT auth, CORS, rate limiting, and configuration management.
34+
- **🔐 Security First**: JWT authentication, CORS middleware, and IP-based rate limiting out of the box.
35+
- **⚙️ Configuration**: Environment-based config with `.env` file support and typed config extraction.
3436

3537
## 📦 Quick Start
3638

3739
Add `rustapi-rs` to your `Cargo.toml`.
3840

41+
```toml
42+
[dependencies]
43+
rustapi-rs = "0.1"
44+
45+
# Optional features
46+
# rustapi-rs = { version = "0.1", features = ["jwt", "cors", "rate-limit"] }
47+
```
48+
3949
```rust
4050
use rustapi_rs::prelude::*;
4151

@@ -68,6 +78,84 @@ async fn main() -> Result<()> {
6878

6979
Visit `http://127.0.0.1:8080/docs` to see your interactive API documentation!
7080

81+
## 🔐 Optional Features
82+
83+
RustAPI provides optional features to keep your binary size minimal:
84+
85+
| Feature | Description |
86+
|---------|-------------|
87+
| `jwt` | JWT authentication middleware and `AuthUser<T>` extractor |
88+
| `cors` | CORS middleware with builder pattern configuration |
89+
| `rate-limit` | IP-based rate limiting middleware |
90+
| `config` | Configuration management with `.env` file support |
91+
| `cookies` | Cookie parsing extractor |
92+
| `sqlx` | SQLx database error conversion to ApiError |
93+
| `extras` | Meta feature enabling jwt, cors, and rate-limit |
94+
| `full` | All optional features enabled |
95+
96+
### JWT Authentication Example
97+
98+
```rust
99+
use rustapi_rs::prelude::*;
100+
101+
#[derive(Debug, Deserialize, Serialize)]
102+
struct Claims {
103+
sub: String,
104+
exp: u64,
105+
}
106+
107+
async fn protected(AuthUser(claims): AuthUser<Claims>) -> Json<String> {
108+
Json(format!("Hello, {}!", claims.sub))
109+
}
110+
111+
#[tokio::main]
112+
async fn main() -> Result<()> {
113+
RustApi::new()
114+
.with_middleware(JwtLayer::<Claims>::new("your-secret-key"))
115+
.route("/protected", get(protected))
116+
.run("127.0.0.1:8080")
117+
.await
118+
}
119+
```
120+
121+
### CORS Configuration Example
122+
123+
```rust
124+
use rustapi_rs::prelude::*;
125+
126+
#[tokio::main]
127+
async fn main() -> Result<()> {
128+
let cors = CorsLayer::new()
129+
.allow_origins(["https://example.com"])
130+
.allow_methods([Method::GET, Method::POST])
131+
.allow_credentials(true);
132+
133+
RustApi::new()
134+
.with_middleware(cors)
135+
.route("/api", get(handler))
136+
.run("127.0.0.1:8080")
137+
.await
138+
}
139+
```
140+
141+
### Rate Limiting Example
142+
143+
```rust
144+
use rustapi_rs::prelude::*;
145+
use std::time::Duration;
146+
147+
#[tokio::main]
148+
async fn main() -> Result<()> {
149+
let rate_limit = RateLimitLayer::new(100, Duration::from_secs(60)); // 100 req/min
150+
151+
RustApi::new()
152+
.with_middleware(rate_limit)
153+
.route("/api", get(handler))
154+
.run("127.0.0.1:8080")
155+
.await
156+
}
157+
```
158+
71159
## 🏗️ Architecture
72160

73161
RustAPI follows a **Facade Architecture** to ensure long-term stability:
@@ -81,7 +169,7 @@ RustAPI follows a **Facade Architecture** to ensure long-term stability:
81169

82170
- [x] **Phase 1: MVP**: Core routing, extractors, and server.
83171
- [x] **Phase 2: Validation & OpenAPI**: Auto-docs, strict validation, and metadata.
84-
- [ ] **Phase 3: Batteries Included**: Authentication (JWT), CORS, Rate Limiting, and Middleware.
172+
- [x] **Phase 3: Batteries Included**: Authentication (JWT), CORS, Rate Limiting, Middleware, and Configuration.
85173
- [ ] **Phase 4: v1.0 Polish**: Advanced ergonomics, CLI tool, and production hardening.
86174

87175

crates/rustapi-core/src/extract.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -928,18 +928,26 @@ mod tests {
928928
.map_err(|e| TestCaseError::fail(format!("Failed to extract headers: {}", e)))?;
929929

930930
// Verify all original headers are present
931+
// HTTP allows duplicate headers - get_all() returns all values for a header name
931932
for (name, value) in &headers {
932-
let header_value = extracted.get(name.as_str())
933-
.ok_or_else(|| TestCaseError::fail(format!("Header '{}' not found", name)))?;
933+
// Check that the header name exists
934+
let all_values: Vec<_> = extracted.get_all(name.as_str()).iter().collect();
935+
prop_assert!(
936+
!all_values.is_empty(),
937+
"Header '{}' not found",
938+
name
939+
);
934940

935-
let extracted_value = header_value.to_str()
936-
.map_err(|e| TestCaseError::fail(format!("Invalid header value: {}", e)))?;
941+
// Check that the value is among the extracted values
942+
let value_found = all_values.iter().any(|v| {
943+
v.to_str().map(|s| s == value.as_str()).unwrap_or(false)
944+
});
937945

938-
prop_assert_eq!(
939-
extracted_value,
940-
value.as_str(),
941-
"Header '{}' value mismatch",
942-
name
946+
prop_assert!(
947+
value_found,
948+
"Header '{}' value '{}' not found in extracted values",
949+
name,
950+
value
943951
);
944952
}
945953

crates/rustapi-rs/Cargo.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
3131
default = ["swagger-ui"]
3232
swagger-ui = ["rustapi-core/swagger-ui", "rustapi-openapi/swagger-ui"]
3333

34-
# Security and utility features from rustapi-extras
35-
jwt = ["rustapi-extras", "rustapi-extras/jwt"]
36-
cors = ["rustapi-extras", "rustapi-extras/cors"]
37-
rate-limit = ["rustapi-extras", "rustapi-extras/rate-limit"]
38-
config = ["rustapi-extras", "rustapi-extras/config"]
39-
cookies = ["rustapi-extras", "rustapi-extras/cookies", "rustapi-core/cookies"]
40-
sqlx = ["rustapi-extras", "rustapi-extras/sqlx"]
34+
# Security and utility features (from rustapi-extras)
35+
jwt = ["dep:rustapi-extras", "rustapi-extras/jwt"]
36+
cors = ["dep:rustapi-extras", "rustapi-extras/cors"]
37+
rate-limit = ["dep:rustapi-extras", "rustapi-extras/rate-limit"]
38+
config = ["dep:rustapi-extras", "rustapi-extras/config"]
39+
cookies = ["dep:rustapi-extras", "rustapi-extras/cookies", "rustapi-core/cookies"]
40+
sqlx = ["dep:rustapi-extras", "rustapi-extras/sqlx"]
4141

4242
# Meta features
4343
extras = ["jwt", "cors", "rate-limit"]

crates/rustapi-rs/README.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,22 @@ We believe that writing high-performance, type-safe web APIs in Rust shouldn't r
3030
- **📝 Automatic OpenAPI**: Your code *is* your documentation. Swagger UI is served at `/docs` out of the box.
3131
- **✅ Built-in Validation**: Add `#[validate(email)]` to your structs and get automatic 422 error handling.
3232
- **🧩 Intuitive Routing**: Radix-tree based routing with simple macros `#[rustapi::get]`, `#[rustapi::post]`.
33-
- **🔋 Batteries Included**: Tracing, graceful shutdown, state management, and error handling are pre-configured.
33+
- **🔋 Batteries Included**: Middleware, JWT auth, CORS, rate limiting, and configuration management.
34+
- **🔐 Security First**: JWT authentication, CORS middleware, and IP-based rate limiting out of the box.
35+
- **⚙️ Configuration**: Environment-based config with `.env` file support and typed config extraction.
3436

3537
## 📦 Quick Start
3638

3739
Add `rustapi-rs` to your `Cargo.toml`.
3840

41+
```toml
42+
[dependencies]
43+
rustapi-rs = "0.1"
44+
45+
# Optional features
46+
# rustapi-rs = { version = "0.1", features = ["jwt", "cors", "rate-limit"] }
47+
```
48+
3949
```rust
4050
use rustapi_rs::prelude::*;
4151

@@ -81,9 +91,22 @@ RustAPI follows a **Facade Architecture** to ensure long-term stability:
8191

8292
- [x] **Phase 1: MVP**: Core routing, extractors, and server.
8393
- [x] **Phase 2: Validation & OpenAPI**: Auto-docs, strict validation, and metadata.
84-
- [ ] **Phase 3: Batteries Included**: Authentication (JWT), CORS, Rate Limiting, and Middleware.
94+
- [x] **Phase 3: Batteries Included**: Authentication (JWT), CORS, Rate Limiting, Middleware, and Configuration.
8595
- [ ] **Phase 4: v1.0 Polish**: Advanced ergonomics, CLI tool, and production hardening.
8696

97+
## 🔐 Optional Features
98+
99+
| Feature | Description |
100+
|---------|-------------|
101+
| `jwt` | JWT authentication middleware and `AuthUser<T>` extractor |
102+
| `cors` | CORS middleware with builder pattern configuration |
103+
| `rate-limit` | IP-based rate limiting middleware |
104+
| `config` | Configuration management with `.env` file support |
105+
| `cookies` | Cookie parsing extractor |
106+
| `sqlx` | SQLx database error conversion to ApiError |
107+
| `extras` | Meta feature enabling jwt, cors, and rate-limit |
108+
| `full` | All optional features enabled |
109+
87110

88111
## 📄 License
89112

0 commit comments

Comments
 (0)