3 releases
Uses new Rust 2024
| 0.2.3 | Oct 30, 2025 |
|---|---|
| 0.2.2 | Aug 5, 2025 |
| 0.2.1 | Aug 5, 2025 |
#210 in HTTP client
Used in model-gateway-rs
30KB
366 lines
toolcraft-request
A lightweight, ergonomic HTTP client wrapper around reqwest with support for base URLs, default headers, and streaming responses.
Features
- 🚀 Simple and intuitive API
- 🔧 Base URL configuration for API clients
- 📋 Default headers management
- ⏱️ Configurable timeouts
- 🌊 Stream response support
- 📤 Multipart/form-data file upload
- 🎯 Type-safe error handling
Installation
Add this to your Cargo.toml:
[dependencies]
toolcraft-request = "*"
Check the crates.io page for the latest version.
Quick Start
use toolcraft_request::Request;
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a new request client
let mut client = Request::new()?;
client.set_base_url("https://api.example.com")?;
// Make a simple GET request
let response = client.get("/users", None, None).await?;
println!("Status: {}", response.status());
println!("Body: {}", response.text().await?);
Ok(())
}
Advanced Usage
Setting Default Headers
use toolcraft_request::{Request, HeaderMap};
let mut client = Request::new()?;
client.set_base_url("https://api.example.com")?;
// Method 1: Build headers manually
let mut headers = HeaderMap::new();
headers.insert("Authorization", "Bearer token123".to_string())?;
headers.insert("User-Agent", "MyApp/1.0".to_string())?;
client.set_default_headers(headers);
// Method 2: Use preset for JSON APIs
let headers = HeaderMap::for_json()?;
client.set_default_headers(headers);
// Sets: Content-Type: application/json
// Accept: application/json
// Method 3: Use preset for form uploads
let headers = HeaderMap::for_form();
client.set_default_headers(headers);
// Returns empty HeaderMap (Content-Type handled by post_form)
GET Request with Query Parameters
let query = vec![
("page".to_string(), "1".to_string()),
("limit".to_string(), "10".to_string()),
];
let response = client.get("/users", Some(query), None).await?;
POST Request with JSON
use serde_json::json;
let body = json!({
"name": "John Doe",
"email": "john@example.com"
});
let response = client.post("/users", &body, None).await?;
POST with Custom Headers (Dynamic Values)
use toolcraft_request::HeaderMap;
use serde_json::json;
// Support dynamic header values
let token = get_auth_token().await?;
let mut headers = HeaderMap::new();
headers.insert("Authorization", format!("Bearer {}", token))?;
headers.insert("Content-Type", "application/json".to_string())?;
let body = json!({"data": "value"});
let response = client.post("/api/data", &body, Some(headers)).await?;
File Upload with FormData
use toolcraft_request::{Request, FormField};
let mut client = Request::new()?;
client.set_base_url("https://api.example.com")?;
// Method 1: Upload file from path
let fields = vec![
FormField::text("username", "john_doe"),
FormField::text("description", "My avatar"),
FormField::file("avatar", "/path/to/image.jpg").await?,
];
let response = client.post_form("/upload", fields, None).await?;
// Method 2: Upload from bytes
let file_data = std::fs::read("/path/to/file.pdf")?;
let fields = vec![
FormField::text("title", "Document"),
FormField::file_from_bytes("document", "file.pdf", file_data),
];
let response = client.post_form("/documents", fields, None).await?;
Important: post_form() automatically removes Content-Type header to let reqwest set the correct multipart/form-data with boundary.
Managing Headers
use toolcraft_request::HeaderMap;
// Create with presets
let mut headers = HeaderMap::for_json()?; // JSON API preset
// Or: let headers = HeaderMap::for_form(); // Form upload preset
// Or: let mut headers = HeaderMap::new(); // Empty
// Insert headers (supports dynamic strings)
headers.insert("Authorization", "Bearer token".to_string())?;
let custom_header = "X-Request-ID".to_string();
headers.insert(custom_header, "12345".to_string())?;
// Check if header exists
if headers.contains("Authorization") {
println!("Auth header present");
}
// Get header value
if let Some(value) = headers.get("Authorization") {
println!("Auth: {}", value);
}
// Remove header
let removed = headers.remove("Authorization");
assert_eq!(removed, Some("Bearer token".to_string()));
// Merge headers
let mut other_headers = HeaderMap::new();
other_headers.insert("User-Agent", "MyApp/1.0".to_string())?;
headers.merge(other_headers);
Streaming Response
use futures_util::StreamExt;
use serde_json::json;
let body = json!({
"model": "gpt-4",
"messages": [{"role": "user", "content": "Hello"}],
"stream": true
});
let mut stream = client.post_stream("/chat", &body, None).await?;
while let Some(chunk) = stream.next().await {
let bytes = chunk?;
println!("Received {} bytes", bytes.len());
}
Error Handling
match client.get("/api/data", None, None).await {
Ok(response) => println!("Success: {}", response.status()),
Err(e) => eprintln!("Error: {}", e),
}
API Reference
Creating a Client
Request::new()- Create a new Request clientRequest::with_timeout(timeout_sec: u64)- Create client with timeout
Configuration Methods
set_base_url(&mut self, base_url: &str)- Set base URL for all requestsset_default_headers(&mut self, headers: HeaderMap)- Set default headers
HTTP Methods
-
get(endpoint, query, headers)- Send GET requestendpoint: &str- API endpointquery: Option<Vec<(String, String)>>- Query parametersheaders: Option<HeaderMap>- Custom headers
-
post(endpoint, body, headers)- Send POST request with JSONendpoint: &str- API endpointbody: &serde_json::Value- JSON bodyheaders: Option<HeaderMap>- Custom headers
-
put(endpoint, body, headers)- Send PUT request with JSON -
delete(endpoint, headers)- Send DELETE request -
post_form(endpoint, form_fields, headers)- Send POST with multipart/form-dataendpoint: &str- API endpointform_fields: Vec<FormField>- Form fields (text and files)headers: Option<HeaderMap>- Custom headers
-
post_stream(endpoint, body, headers)- Send POST and return byte stream
FormField Methods
FormField::text(name, value)- Create text fieldFormField::file(name, path)- Create file field from path (async)FormField::file_from_bytes(name, filename, content)- Create file field from bytes
HeaderMap Methods
Factory Methods:
new()- Create new empty HeaderMapfor_json()- Create with JSON preset headers (Content-Type + Accept)for_form()- Create for form uploads (empty, Content-Type auto-handled)
Management Methods:
insert(key, value)- Insert header (supports dynamic strings, overwrites if exists)get(key)- Get header value as Stringremove(key)- Remove header and return its valuecontains(key)- Check if header existsmerge(other)- Merge another HeaderMap (overwrites on conflict)
Response Methods
status()- Get HTTP status codeheaders()- Get response headerstext()- Get response as text (async)json<T>()- Parse response as JSON (async)bytes()- Get response as bytes (async)
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Links
Dependencies
~7–21MB
~246K SLoC