Skip to content

Commit c55e34f

Browse files
authored
Merge pull request #126 from Tuntii/docs-cookbook-learning-path-improvements-2026-02-17-11137848913340546016
docs: Cookbook and Learning Path Improvements
2 parents f27a98d + adbc745 commit c55e34f

File tree

11 files changed

+467
-118
lines changed

11 files changed

+467
-118
lines changed

docs/.agent/docs_coverage.md

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,30 @@
11
# Docs Coverage Map
22

3-
| Feature | Documentation | Code Location | Status |
4-
|---------|---------------|---------------|--------|
3+
| Feature Area | Documentation Page | Source Code (Key Symbols) | Status |
4+
|--------------|-------------------|--------------------------|--------|
55
| **Core** | | | |
6-
| Routing | `concepts/handlers.md` | `rustapi-macros` | OK |
7-
| Extractors | `concepts/handlers.md` | `rustapi-core/src/extract.rs` | OK |
8-
| State | `concepts/handlers.md` | `rustapi-core/src/extract.rs` | OK |
9-
| Validation | `crates/rustapi_validate.md` | `rustapi-validate` | OK |
10-
| **HATEOAS** | | | |
11-
| Pagination | `recipes/pagination.md` | `rustapi-core/src/hateoas.rs` | OK |
12-
| Links | `recipes/pagination.md` | `rustapi-core/src/hateoas.rs` | OK |
6+
| Routing | `docs/cookbook/src/concepts/routing.md` | `rustapi-core/src/router.rs` (`Router`) | OK |
7+
| Handlers | `docs/cookbook/src/concepts/handlers.md` | `rustapi-core/src/handler.rs` (`Handler`) | OK |
8+
| Extractors | `docs/cookbook/src/concepts/extractors.md` | `rustapi-core/src/extract.rs` (`FromRequest`) | OK |
9+
| Middleware | `docs/cookbook/src/recipes/custom_middleware.md` | `rustapi-core/src/middleware/mod.rs` (`MiddlewareLayer`) | OK |
10+
| State | `docs/cookbook/src/concepts/state.md` | `rustapi-core/src/extract.rs` (`State`) | OK |
11+
| Error Handling | `docs/cookbook/src/concepts/errors.md` | `rustapi-core/src/error.rs` (`ApiError`) | OK |
12+
| HTTP/3 (QUIC) | `docs/cookbook/src/recipes/http3_quic.md` | `rustapi-core/src/http3.rs` (`Http3Server`) | OK |
13+
| File Uploads | `docs/cookbook/src/recipes/file_uploads.md` | `rustapi-core/src/multipart.rs` (`Multipart`) | OK |
14+
| Compression | `docs/cookbook/src/recipes/compression.md` | `rustapi-core/src/middleware/compression.rs` (`CompressionLayer`) | OK |
15+
| **OpenAPI** | | | |
16+
| Schema Derivation | `docs/cookbook/src/crates/rustapi_openapi.md` | `rustapi-macros/src/derive_schema.rs` (`#[derive(Schema)]`) | OK |
17+
| References ($ref) | `docs/cookbook/src/recipes/openapi_refs.md` | `rustapi-openapi/src/schema.rs` (`SchemaRef`) | OK |
18+
| **Validation** | | | |
19+
| Sync Validation | `docs/cookbook/src/crates/rustapi_validate.md` | `rustapi-validate/src/lib.rs` (`Validate`) | OK |
20+
| Async Validation | `docs/cookbook/src/crates/rustapi_validate.md` | `rustapi-validate/src/v2/mod.rs` (`AsyncValidate`) | OK |
1321
| **Extras** | | | |
14-
| Auth (JWT) | `recipes/jwt_auth.md` | `rustapi-extras/src/jwt` | OK |
15-
| Auth (OAuth2) | `recipes/oauth2_client.md` | `rustapi-extras/src/oauth2` | OK |
16-
| Security | `recipes/csrf_protection.md` | `rustapi-extras/src/security` | OK |
17-
| Observability | `crates/rustapi_extras.md` | `rustapi-extras/src/telemetry` | OK |
18-
| Audit Logging | `recipes/audit_logging.md` | `rustapi-extras/src/audit` | OK |
19-
| Middleware (Advanced) | `recipes/advanced_middleware.md` | `rustapi-extras/src/{rate_limit, dedup, cache}` | OK |
20-
| **Jobs** | | | |
21-
| Job Queue (Crate) | `crates/rustapi_jobs.md` | `rustapi-jobs` | OK |
22-
| Background Jobs (Recipe) | `recipes/background_jobs.md` | `rustapi-jobs` | OK |
23-
| **Integrations** | | | |
24-
| gRPC | `recipes/grpc_integration.md` | `rustapi-grpc` | OK |
25-
| SSR | `recipes/server_side_rendering.md` | `rustapi-view` | OK |
26-
| AI / TOON | `recipes/ai_integration.md` | `rustapi-toon` | OK |
27-
| **Learning** | | | |
28-
| Structured Path | `learning/curriculum.md` | N/A | OK |
29-
| **Recipes** | | | |
30-
| File Uploads | `recipes/file_uploads.md` | `rustapi-core` | OK |
31-
| Deployment | `recipes/deployment.md` | `cargo-rustapi` | OK |
32-
| Testing | `recipes/testing.md` | `rustapi-testing` | OK |
22+
| JWT Auth | `docs/cookbook/src/recipes/jwt_auth.md` | `rustapi-extras/src/jwt.rs` (`JwtLayer`) | OK |
23+
| OAuth2 | `docs/cookbook/src/recipes/oauth2_client.md` | `rustapi-extras/src/oauth2.rs` (`OAuth2Client`) | OK |
24+
| Database | `docs/cookbook/src/recipes/db_integration.md` | N/A (Integration pattern) | Needs Update |
25+
| **Ecosystem** | | | |
26+
| WebSockets | `docs/cookbook/src/recipes/websockets.md` | `rustapi-ws/src/lib.rs` (`WebSocketUpgrade`) | OK |
27+
| SSR (View) | `docs/cookbook/src/recipes/server_side_rendering.md` | `rustapi-view/src/lib.rs` (`View`) | OK |
28+
| gRPC | `docs/cookbook/src/recipes/grpc_integration.md` | `rustapi-grpc/src/lib.rs` (`TonicServer`) | OK |
29+
| Jobs | `docs/cookbook/src/recipes/background_jobs.md` | `rustapi-jobs/src/lib.rs` (`Job`) | OK |
30+
| TOON (AI) | `docs/cookbook/src/recipes/ai_integration.md` | `rustapi-toon/src/lib.rs` (`LlmResponse`) | OK |

docs/.agent/docs_inventory.md

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
# Documentation Inventory
22

3-
| File | Purpose | Owner Crate | Status |
4-
|------|---------|-------------|--------|
5-
| `README.md` | Project overview, key features, quick start | Root | OK |
6-
| `docs/README.md` | Documentation landing page | Docs | OK |
7-
| `docs/cookbook/src/SUMMARY.md` | Cookbook navigation structure | Docs | OK |
8-
| `docs/cookbook/src/getting_started/installation.md` | Installation instructions | Docs | OK |
9-
| `docs/cookbook/src/learning/README.md` | Learning path entry point | Docs | OK |
10-
| `docs/cookbook/src/recipes/*.md` | Specific implementation guides | Docs | OK |
11-
| `docs/cookbook/src/recipes/advanced_middleware.md` | Recipe for Rate Limit, Dedup, Cache | Docs | OK |
12-
| `docs/cookbook/src/recipes/audit_logging.md` | Recipe for Audit Logging | Docs | OK |
13-
| `docs/cookbook/src/recipes/oauth2_client.md` | Recipe for OAuth2 Client | Docs | OK |
14-
| `crates/rustapi-core/src/hateoas.rs` | API Reference for HATEOAS | rustapi-core | OK |
15-
| `crates/rustapi-core/src/extract.rs` | API Reference for Extractors | rustapi-core | OK |
3+
| File Path | Purpose | Last Updated Version | Owner Crate | Status |
4+
|-----------|---------|----------------------|-------------|--------|
5+
| `docs/README.md` | Main entry point | 0.1.335 | rustapi-rs | OK |
6+
| `docs/GETTING_STARTED.md` | Quick start guide | 0.1.335 | rustapi-rs | OK |
7+
| `docs/ARCHITECTURE.md` | High-level architecture | 0.1.335 | rustapi-core | OK |
8+
| `docs/FEATURES.md` | Feature list | 0.1.335 | rustapi-rs | OK |
9+
| `docs/PHILOSOPHY.md` | Design philosophy | 0.1.335 | rustapi-rs | OK |
10+
| `docs/native_openapi.md` | Native OpenAPI details | 0.1.335 | rustapi-openapi | OK |
11+
| `docs/cookbook/src/SUMMARY.md` | Cookbook ToC | 0.1.335 | rustapi-rs | Needs Update |
12+
| `docs/cookbook/src/introduction.md` | Cookbook Intro | 0.1.335 | rustapi-rs | OK |
13+
| `docs/cookbook/src/troubleshooting.md` | Common issues | 0.1.335 | rustapi-rs | OK |
14+
| `docs/cookbook/src/learning/curriculum.md` | Learning Path | 0.1.335 | rustapi-rs | Needs Update |
15+
| `docs/cookbook/src/recipes/db_integration.md` | Database recipe | 0.1.335 | rustapi-rs | Needs Update |
16+
| `docs/cookbook/src/recipes/file_uploads.md` | File upload recipe | 0.1.335 | rustapi-core | OK |
17+
| `docs/cookbook/src/recipes/compression.md` | Compression recipe | 0.1.335 | rustapi-core | OK |
18+
| `docs/cookbook/src/recipes/openapi_refs.md` | OpenAPI Refs recipe | 0.1.335 | rustapi-openapi | OK |
19+
| `docs/cookbook/src/recipes/http3_quic.md` | HTTP/3 recipe | 0.1.335 | rustapi-core | OK |
20+
| `docs/cookbook/src/recipes/jwt_auth.md` | JWT Auth recipe | 0.1.335 | rustapi-extras | OK |
21+
| `docs/cookbook/src/recipes/websockets.md` | WebSocket recipe | 0.1.335 | rustapi-ws | OK |
22+
| `docs/cookbook/src/recipes/server_side_rendering.md` | SSR recipe | 0.1.335 | rustapi-view | OK |
23+
| `docs/cookbook/src/recipes/grpc_integration.md` | gRPC recipe | 0.1.335 | rustapi-grpc | OK |
24+
| `docs/cookbook/src/recipes/background_jobs.md` | Jobs recipe | 0.1.335 | rustapi-jobs | OK |
25+
| `docs/cookbook/src/recipes/ai_integration.md` | AI/TOON recipe | 0.1.335 | rustapi-toon | OK |

docs/.agent/last_run.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"last_processed_ref": "v0.1.335",
3-
"date": "2026-02-16",
4-
"notes": "Added recipes for Advanced Middleware, Audit Logging, and OAuth2 Client. Updated Learning Path to include these features."
3+
"date": "2026-02-17",
4+
"notes": "Added recipes for Compression, OpenAPI Refs, File Uploads, and Database Integration. Updated Learning Path with new modules."
55
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Run Report: 2026-02-17
2+
3+
## Detected Version
4+
- **Target Version:** 0.1.335
5+
- **Commit:** (No new commits since last run)
6+
7+
## Changes
8+
No code changes detected. This run focused on documentation improvements and cookbook expansion.
9+
10+
## Documentation Updates
11+
12+
### Learning Path (`docs/cookbook/src/learning/curriculum.md`)
13+
- Added **Module 4.5: Database Integration** covering connection pooling with `sqlx`.
14+
- Added **Module 5.5: Error Handling** covering `ApiError` and production masking.
15+
- Added **Module 6.5: File Uploads & Multipart** covering streaming uploads.
16+
- Updated **Module 6: OpenAPI & HATEOAS** to include OpenAPI References.
17+
- Updated **Module 14: High Performance** to include Response Compression.
18+
19+
### Cookbook Recipes
20+
- **Created `recipes/compression.md`:** Detailed guide on using `CompressionLayer` with Gzip/Brotli/Deflate.
21+
- **Created `recipes/openapi_refs.md`:** Explanation of automatic `$ref` generation with `#[derive(Schema)]` and handling recursive types.
22+
- **Updated `recipes/file_uploads.md`:** Fixed body limit configuration (using `RustApi::new().body_limit(...)`), improved security notes, and added a complete example.
23+
- **Updated `recipes/db_integration.md`:** Expanded with production connection pool settings, transactions, and integration testing with `testcontainers`.
24+
25+
### Documentation Management
26+
- **Created `docs/.agent/docs_inventory.md`:** Full inventory of documentation files and their status.
27+
- **Created `docs/.agent/docs_coverage.md`:** Mapping of features to documentation pages.
28+
29+
## Improvements
30+
- Addressed user feedback regarding missing recipes for DB integration patterns, file uploads, error types, OpenAPI refs, and compression.
31+
- Standardized the Learning Path structure.
32+
33+
## TODOs
34+
- Verify `rustapi-grpc` examples with the latest `tonic` version.
35+
- Add a specific recipe for `rustapi-view` with HTMX.

docs/cookbook/src/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
- [Part IV: Recipes](recipes/README.md)
3131
- [Creating Resources](recipes/crud_resource.md)
3232
- [Pagination & HATEOAS](recipes/pagination.md)
33+
- [OpenAPI & Schemas](recipes/openapi_refs.md)
3334
- [JWT Authentication](recipes/jwt_auth.md)
3435
- [OAuth2 Client](recipes/oauth2_client.md)
3536
- [CSRF Protection](recipes/csrf_protection.md)
@@ -43,6 +44,7 @@
4344
- [Server-Side Rendering (SSR)](recipes/server_side_rendering.md)
4445
- [AI Integration (TOON)](recipes/ai_integration.md)
4546
- [Production Tuning](recipes/high_performance.md)
47+
- [Response Compression](recipes/compression.md)
4648
- [Resilience Patterns](recipes/resilience.md)
4749
- [Audit Logging](recipes/audit_logging.md)
4850
- [Time-Travel Debugging (Replay)](recipes/replay.md)

docs/cookbook/src/learning/curriculum.md

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,18 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u
6969
2. Which extractor retrieves the application state?
7070
3. Why should you use `Arc` for shared state?
7171

72+
### Module 4.5: Database Integration
73+
- **Prerequisites:** Module 4.
74+
- **Reading:** [Database Integration](../recipes/db_integration.md).
75+
- **Task:** Replace the in-memory `Mutex<Vec<User>>` with a PostgreSQL connection pool (`sqlx::PgPool`).
76+
- **Expected Output:** Data persists across server restarts.
77+
- **Pitfalls:** Blocking the async runtime with synchronous DB drivers (use `sqlx` or `tokio-postgres`).
78+
79+
#### 🧠 Knowledge Check
80+
1. Why is connection pooling important?
81+
2. How do you share a DB pool across handlers?
82+
3. What is the benefit of compile-time query checking in SQLx?
83+
7284
### Module 5: Validation
7385
- **Prerequisites:** Module 4.
7486
- **Reading:** [Validation](../crates/rustapi_validation.md).
@@ -81,18 +93,42 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u
8193
2. What HTTP status code is returned on validation failure?
8294
3. How do you combine JSON extraction and validation?
8395

96+
### Module 5.5: Error Handling
97+
- **Prerequisites:** Module 5.
98+
- **Reading:** [Error Handling](../concepts/errors.md).
99+
- **Task:** Create a custom `ApiError` enum and implement `IntoResponse`. Return robust error messages.
100+
- **Expected Output:** `GET /users/999` returns `404 Not Found` with a structured JSON error body.
101+
- **Pitfalls:** Exposing internal database errors (like SQL strings) to the client.
102+
103+
#### 🧠 Knowledge Check
104+
1. What is the standard error type in RustAPI?
105+
2. How do you mask internal errors in production?
106+
3. What is the purpose of the `error_id` field?
107+
84108
### Module 6: OpenAPI & HATEOAS
85109
- **Prerequisites:** Module 5.
86-
- **Reading:** [OpenAPI](../crates/rustapi_openapi.md), [Pagination Recipe](../recipes/pagination.md).
87-
- **Task:** Add `#[derive(Schema)]` to all DTOs. Implement pagination for `GET /users`.
88-
- **Expected Output:** Swagger UI at `/docs` showing full schema. Paginated responses with `_links`.
89-
- **Pitfalls:** Using types that don't implement `Schema` (like raw `serde_json::Value`) inside response structs.
110+
- **Reading:** [OpenAPI](../crates/rustapi_openapi.md), [OpenAPI Refs](../recipes/openapi_refs.md), [Pagination Recipe](../recipes/pagination.md).
111+
- **Task:** Add `#[derive(Schema)]` to all DTOs. Use `#[derive(Schema)]` on a shared struct and reference it in multiple places.
112+
- **Expected Output:** Swagger UI at `/docs` showing full schema with shared components.
113+
- **Pitfalls:** Recursive schemas without `Box` or `Option`.
90114

91115
#### 🧠 Knowledge Check
92116
1. What does `#[derive(Schema)]` do?
93-
2. Where is the Swagger UI served by default?
117+
2. How does RustAPI handle shared schema components?
94118
3. What is HATEOAS and why is it useful?
95119

120+
### Module 6.5: File Uploads & Multipart
121+
- **Prerequisites:** Module 6.
122+
- **Reading:** [File Uploads](../recipes/file_uploads.md).
123+
- **Task:** Create an endpoint `POST /upload` that accepts a file and saves it to disk.
124+
- **Expected Output:** `curl -F file=@image.png` uploads the file.
125+
- **Pitfalls:** Loading large files entirely into memory (use streaming).
126+
127+
#### 🧠 Knowledge Check
128+
1. Which extractor is used for file uploads?
129+
2. Why should you use `field.chunk()` instead of `field.bytes()`?
130+
3. How do you increase the request body size limit?
131+
96132
### 🏆 Phase 2 Capstone: "The Secure Blog Engine"
97133
**Objective:** Enhance the Todo API into a Blog Engine.
98134
**Requirements:**
@@ -224,18 +260,18 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u
224260

225261
### Module 14: High Performance
226262
- **Prerequisites:** Phase 3.
227-
- **Reading:** [HTTP/3 (QUIC)](../recipes/http3_quic.md), [Performance Tuning](../recipes/high_performance.md).
263+
- **Reading:** [HTTP/3 (QUIC)](../recipes/http3_quic.md), [Performance Tuning](../recipes/high_performance.md), [Compression](../recipes/compression.md).
228264
- **Task:**
229265
1. Enable `http3` feature and generate self-signed certs.
230266
2. Serve traffic over QUIC.
231-
3. Implement response caching for a heavy computation endpoint.
232-
- **Expected Output:** Browser/Client connects via HTTP/3. Repeated requests are served instantly from cache.
233-
- **Pitfalls:** Caching private user data without proper keys.
267+
3. Add `CompressionLayer` to compress large responses.
268+
- **Expected Output:** Browser/Client connects via HTTP/3. Responses have `content-encoding: gzip`.
269+
- **Pitfalls:** Compressing small responses (waste of CPU) or already compressed data (images).
234270

235271
#### 🧠 Knowledge Check
236272
1. What transport protocol does HTTP/3 use?
237273
2. How does `simd-json` improve performance?
238-
3. When should you *not* use caching?
274+
3. Why shouldn't you compress JPEG images?
239275

240276
### 🏆 Phase 4 Capstone: "The High-Scale Event Platform"
241277
**Objective:** Architect a system capable of handling thousands of events per second.

docs/cookbook/src/recipes/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Each recipe follows a simple structure:
1313

1414
- [Creating Resources](crud_resource.md)
1515
- [Pagination & HATEOAS](pagination.md)
16+
- [OpenAPI & Schemas](openapi_refs.md)
1617
- [JWT Authentication](jwt_auth.md)
1718
- [CSRF Protection](csrf_protection.md)
1819
- [Database Integration](db_integration.md)
@@ -24,6 +25,7 @@ Each recipe follows a simple structure:
2425
- [Server-Side Rendering (SSR)](server_side_rendering.md)
2526
- [AI Integration (TOON)](ai_integration.md)
2627
- [Production Tuning](high_performance.md)
28+
- [Response Compression](compression.md)
2729
- [Resilience Patterns](resilience.md)
2830
- [Time-Travel Debugging (Replay)](replay.md)
2931
- [Deployment](deployment.md)
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Response Compression
2+
3+
RustAPI supports automatic response compression (Gzip, Deflate, Brotli) via the `CompressionLayer`. This middleware negotiates the best compression algorithm based on the client's `Accept-Encoding` header.
4+
5+
## Dependencies
6+
7+
To use compression, you must enable the `compression` feature in `rustapi-core` (or `rustapi-rs`). For Brotli support, enable `compression-brotli`.
8+
9+
```toml
10+
[dependencies]
11+
rustapi-rs = { version = "0.1.335", features = ["compression", "compression-brotli"] }
12+
```
13+
14+
## Basic Usage
15+
16+
The simplest way to enable compression is to add the layer to your application:
17+
18+
```rust
19+
use rustapi_rs::prelude::*;
20+
use rustapi_core::middleware::CompressionLayer;
21+
22+
#[tokio::main]
23+
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
24+
RustApi::new()
25+
.layer(CompressionLayer::new())
26+
.route("/", get(hello))
27+
.run("127.0.0.1:8080")
28+
.await
29+
}
30+
31+
async fn hello() -> &'static str {
32+
"Hello, World! This response will be compressed if the client supports it."
33+
}
34+
```
35+
36+
## Configuration
37+
38+
You can customize the compression behavior using `CompressionConfig`:
39+
40+
```rust
41+
use rustapi_rs::prelude::*;
42+
use rustapi_core::middleware::{CompressionLayer, CompressionConfig};
43+
44+
#[tokio::main]
45+
async fn main() -> Result<()> {
46+
let config = CompressionConfig::new()
47+
.min_size(1024) // Only compress responses larger than 1KB
48+
.level(6) // Compression level (0-9)
49+
.gzip(true) // Enable Gzip
50+
.deflate(false) // Disable Deflate
51+
.brotli(true) // Enable Brotli (if feature enabled)
52+
.add_content_type("application/custom-json"); // Add custom type
53+
54+
RustApi::new()
55+
.layer(CompressionLayer::with_config(config))
56+
.route("/data", get(get_large_data))
57+
.run("127.0.0.1:8080")
58+
.await
59+
}
60+
```
61+
62+
## Default Configuration
63+
64+
By default, `CompressionLayer` is configured with:
65+
- `min_size`: 1024 bytes (1KB)
66+
- `level`: 6
67+
- `gzip`: enabled
68+
- `deflate`: enabled
69+
- `brotli`: enabled (if feature is present)
70+
- `content_types`: `text/*`, `application/json`, `application/javascript`, `application/xml`, `image/svg+xml`
71+
72+
## Best Practices
73+
74+
### 1. Don't Compress Already Compressed Data
75+
Images (JPEG, PNG), Videos, and Archives (ZIP) are already compressed. Compressing them again wastes CPU cycles and might even increase the file size. The default configuration excludes most binary formats, but be careful with custom types.
76+
77+
### 2. Set Minimum Size
78+
Compressing very small responses (e.g., "OK") can actually make them larger due to framing overhead. The default 1KB threshold is a good starting point.
79+
80+
### 3. Order of Middleware
81+
Compression should usually be one of the *last* layers added (outermost), so it compresses the final response after other middleware (like logging or headers) have run.
82+
83+
```rust
84+
RustApi::new()
85+
.layer(CompressionLayer::new()) // Runs last on response (first on request)
86+
.layer(LoggingLayer::new()) // Runs before compression on response
87+
```

0 commit comments

Comments
 (0)