Your Postman in the Terminal - A Professional HTTP Client & API Testing Tool
Lightning-fast, feature-rich HTTP client for developers who live in the terminal
Features • Installation • Quick Start • Documentation • Examples
- All HTTP Methods - GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
- Interactive REPL Mode - Natural command-line interface with history
- Beautiful Output - Color-coded responses optimized for light & dark terminals
- Fast & Lightweight - Built with Rust for maximum performance
- Custom Headers - Full control over request headers
- Query Parameters - Easy query string management
- Response Timing - Automatic performance measurement
- Authentication Suite - Basic, Bearer, API Key, OAuth 2.0 (5 grant types)
- File Uploads - Multipart form data with automatic MIME detection
- Request Chaining - Build complex workflows with variable extraction
- Scripting Engine - Pre/post-request scripts with JavaScript-like syntax
- Assertions - Comprehensive validation with 14+ matcher types
- Collections - Organize and save request collections
- Environments - Manage variables across dev/staging/prod
- Sessions & Cookies - Automatic cookie handling and persistence
- Request History - Track and replay past requests
- Workspaces - Organize projects efficiently
- Syntax Highlighting - Pretty-printed JSON responses
- Comprehensive Testing - 280+ unit tests for reliability
- Modular Architecture - Clean, maintainable codebase
- Rich Documentation - Examples and guides for every feature
- Rust 1.70+ - Install Rust
# Clone the repository
git clone https://github.com/yourusername/bazzounquester.git
cd bazzounquester
# Install using cargo
cargo install --path .
# Verify installation
bazzounquester --version# Clone and build
git clone https://github.com/yourusername/bazzounquester.git
cd bazzounquester
cargo build --release
# Run directly
./target/release/bazzounquesterIf bazzounquester command is not found, add Cargo's bin directory to your PATH:
# For Bash/Zsh
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# For Fish
echo 'set -gx PATH $HOME/.cargo/bin $PATH' >> ~/.config/fish/config.fishalias bzq='bazzounquester'
alias req='bazzounquester'bazzounquesterYou'll see:
╔═══════════════════════════════════════════════════════════════╗
║ Bazzounquester - Interactive Mode ║
║ Your Postman in the Terminal - HTTP CLI Tool ║
╚═══════════════════════════════════════════════════════════════╝
Version: v1.8.0
Author: Hassan Bazzoun <hassan.bazzoundev@gmail.com>
Type 'help' for commands | 'version' for info | 'exit' to quit
bazzounquester>
# Simple GET request
bazzounquester> get https://httpbin.org/json
# POST with JSON body
bazzounquester> post https://httpbin.org/post \
-H "Content-Type:application/json" \
-b '{"name":"John","email":"john@example.com"}'# Direct execution without interactive mode
bazzounquester get https://api.github.com/users/octocat
# With authentication
bazzounquester get https://api.github.com/user \
-H "Authorization:Bearer YOUR_TOKEN"# GET
get https://api.example.com/users
# POST with JSON
post https://api.example.com/users \
-H "Content-Type:application/json" \
-b '{"name":"Alice","role":"admin"}'
# PUT
put https://api.example.com/users/123 \
-b '{"name":"Alice Updated"}'
# PATCH
patch https://api.example.com/users/123 \
-b '{"email":"newemail@example.com"}'
# DELETE
delete https://api.example.com/users/123
# HEAD (headers only)
head https://api.example.com/users
# OPTIONS (check allowed methods)
options https://api.example.com/usersget https://api.example.com/search \
-q "query=rust" \
-q "page=1" \
-q "limit=20"get https://api.example.com/data \
-H "Authorization:Bearer token123" \
-H "X-API-Key:secret" \
-H "Accept:application/json"Bazzounquester supports all major authentication methods:
use bazzounquester::auth::BasicAuth;
let auth = BasicAuth::new("username", "password");
let encoded = auth.encode(); // Base64 encoded credentialsuse bazzounquester::auth::BearerAuth;
let auth = BearerAuth::new("your-jwt-token");use bazzounquester::auth::ApiKeyAuth;
// Header-based
let auth = ApiKeyAuth::header("X-API-Key", "secret-key");
// Query-based
let auth = ApiKeyAuth::query("api_key", "secret-key");Supports all 5 OAuth 2.0 grant types:
use bazzounquester::auth::{OAuth2Auth, GrantType};
// Authorization Code
let auth = OAuth2Auth::new(
"client_id",
"client_secret",
GrantType::AuthorizationCode {
auth_url: "https://auth.example.com/authorize".to_string(),
token_url: "https://auth.example.com/token".to_string(),
redirect_uri: "http://localhost:8080/callback".to_string(),
}
);
// Client Credentials
let auth = OAuth2Auth::new(
"client_id",
"client_secret",
GrantType::ClientCredentials {
token_url: "https://auth.example.com/token".to_string(),
}
);
// Password Grant
let auth = OAuth2Auth::new(
"client_id",
"client_secret",
GrantType::Password {
token_url: "https://auth.example.com/token".to_string(),
username: "user@example.com".to_string(),
password: "password".to_string(),
}
);Upload files with automatic MIME type detection and multipart form data support:
use bazzounquester::upload::{FileUpload, FormData};
// Single file upload
let file = FileUpload::new("./document.pdf", "file".to_string())?;
// Form data with multiple fields
let mut form = FormData::new();
form.add_field("name", "John Doe");
form.add_field("email", "john@example.com");
form.add_file(file)?;
// Build multipart request
let multipart = form.build()?;Features:
- Automatic MIME type detection
- Custom filenames
- Multiple file uploads
- Mixed form fields and files
- File size validation
Execute JavaScript-like scripts before and after requests using the Rhai scripting engine:
use bazzounquester::scripts::{Script, ScriptEngine, ScriptContext};
// Pre-request script
let pre_script = Script::pre(r#"
// Set dynamic timestamp
let timestamp = new Date().getTime();
variables.set("timestamp", timestamp);
// Generate request ID
let request_id = "req_" + Math.random();
variables.set("request_id", request_id);
console.log("Request prepared: " + request_id);
"#);
// Post-request script
let post_script = Script::post(r#"
// Extract auth token from response
let data = JSON.parse(response.body);
variables.set("auth_token", data.token);
// Log response time
console.log("Request took: " + response.time + "ms");
// Conditional logic
if (response.status == 200) {
console.log("Success!");
}
"#);
// Execute scripts
let mut context = ScriptContext::new();
let mut engine = ScriptEngine::new();
engine.execute(&pre_script, &mut context)?;Available in Scripts:
variables- Get/set variablesresponse- Access response data (post-request only)console.log()- Debug outputJSON.parse()/JSON.stringify()- JSON handling- Math operations and string manipulation
Validate responses with powerful assertion system:
use bazzounquester::assertions::{Assertion, Matcher, MatcherType, ResponseValidator};
// Status code assertion
let assertion = Assertion::new(
"$.status",
Matcher::new(MatcherType::Equals, "200")
);
// Response body assertions
let assertions = vec![
Assertion::new("$.data.name", Matcher::new(MatcherType::Equals, "John Doe")),
Assertion::new("$.data.email", Matcher::new(MatcherType::Contains, "@example.com")),
Assertion::new("$.data.age", Matcher::new(MatcherType::GreaterThan, "18")),
Assertion::new("$.data.roles", Matcher::new(MatcherType::Contains, "admin")),
];
// Validate response
let validator = ResponseValidator::new(assertions);
let results = validator.validate(&response)?;
for result in results {
if result.passed {
println!("✓ {}", result.assertion.description);
} else {
println!("✗ {} - {}", result.assertion.description, result.error.unwrap());
}
}Supported Matchers (14 types):
Equals,NotEquals- Exact matchingContains,NotContains- Substring matchingStartsWith,EndsWith- Prefix/suffix matchingRegex- Regular expression matchingLessThan,LessThanOrEqual,GreaterThan,GreaterThanOrEqual- Numeric comparisonIsEmpty,IsNotEmpty- Empty checksHasLength- Length validationIsNull,IsNotNull- Null checks
Build complex multi-step workflows with variable extraction and substitution:
use bazzounquester::workflow::{WorkflowStep, RequestChain, WorkflowExecutor};
// Step 1: Login and extract token
let login_step = WorkflowStep::new("login")
.request(
RequestBuilder::new(HttpMethod::Post, "https://api.example.com/login")
.body(r#"{"username":"admin","password":"secret"}"#)
)
.extract_variable("auth_token", "$.data.token")
.extract_variable("user_id", "$.data.user_id");
// Step 2: Fetch user data using extracted token
let fetch_step = WorkflowStep::new("fetch_user")
.request(
RequestBuilder::new(HttpMethod::Get, "https://api.example.com/users/{{user_id}}")
.header("Authorization", "Bearer {{auth_token}}")
)
.assertion(Assertion::new("$.status", Matcher::equals("success")))
.extract_variable("user_email", "$.data.email");
// Step 3: Update user profile
let update_step = WorkflowStep::new("update_user")
.request(
RequestBuilder::new(HttpMethod::Put, "https://api.example.com/users/{{user_id}}")
.header("Authorization", "Bearer {{auth_token}}")
.body(r#"{"email":"{{user_email}}","verified":true}"#)
)
.assertion(Assertion::new("$.status", Matcher::equals("updated")));
// Build and execute workflow
let chain = RequestChain::new("user_update_flow")
.step(login_step)
.step(fetch_step)
.step(update_step);
let executor = WorkflowExecutor::new(client);
let result = executor.execute(&chain)?;
println!("Workflow completed in {:.2}s", result.total_duration.as_secs_f64());
println!("Steps: {}/{} successful", result.successful_steps, result.total_steps);Workflow Features:
- Variable extraction from responses (JSON path)
- Variable substitution in URLs, headers, and bodies
- Conditional execution
- Iteration over data sets
- Pre/post-request scripts per step
- Assertions per step
- Detailed execution results
Organize and save your requests:
use bazzounquester::collections::{Collection, Workspace};
// Create a collection
let mut collection = Collection::new("API Tests");
collection.add_request("Get Users", request_builder);
collection.add_request("Create User", create_request);
// Organize into workspace
let mut workspace = Workspace::new("Development");
workspace.add_collection(collection);
// Save to disk
workspace.save()?;
// Load later
let workspace = Workspace::load("Development")?;Manage variables across different environments:
use bazzounquester::env::{Environment, EnvironmentManager};
// Create environments
let mut dev_env = Environment::new("development");
dev_env.set("api_url", "http://localhost:3000");
dev_env.set("api_key", "dev-key-123");
let mut prod_env = Environment::new("production");
prod_env.set("api_url", "https://api.example.com");
prod_env.set("api_key", "prod-key-secret");
// Manage environments
let mut manager = EnvironmentManager::new();
manager.add_environment(dev_env);
manager.add_environment(prod_env);
manager.set_active("development");
// Use variables in requests
let url = manager.substitute("{{api_url}}/users");Automatic cookie handling and persistence:
use bazzounquester::session::{Session, CookieJar};
// Create session
let mut session = Session::new("my-session");
// Cookies are automatically extracted and sent with subsequent requests
let response1 = client.get("https://example.com/login")?;
session.update_from_response(&response1);
let response2 = client.get("https://example.com/profile")?;
// Cookies from response1 are automatically included
// Persist session
session.save()?;
// Restore later
let session = Session::load("my-session")?;Track and replay past requests:
use bazzounquester::history::{HistoryManager, HistoryEntry};
let mut history = HistoryManager::new();
// Automatically logged
history.log(request, response);
// Search history
let entries = history.search("github.com")?;
// Replay request
let entry = history.get(entry_id)?;
let response = entry.replay()?;
// Export history
history.export_json("history.json")?;# Test JSONPlaceholder API
bazzounquester get https://jsonplaceholder.typicode.com/users/1
# Create a post
bazzounquester post https://jsonplaceholder.typicode.com/posts \
-H "Content-Type:application/json" \
-b '{"title":"My Post","body":"Content","userId":1}'# Fetch authenticated user
bazzounquester get https://api.github.com/user \
-H "Authorization:Bearer YOUR_GITHUB_TOKEN"
# List repositories
bazzounquester get https://api.github.com/user/repos \
-H "Authorization:Bearer YOUR_GITHUB_TOKEN" \
-q "sort=updated" \
-q "per_page=10"Run the comprehensive showcase example:
cargo run --example showcaseThis demonstrates:
- Simple HTTP requests
- Authentication (Basic, Bearer, OAuth)
- File uploads
- Pre/post-request scripting
- Response assertions
- Multi-step workflows
Bazzounquester features a clean, modular architecture:
src/
├── main.rs # CLI entry point
├── lib.rs # Library exports
├── http/ # HTTP client & request handling
│ ├── client.rs # HTTP client implementation
│ ├── request.rs # Request builder
│ ├── response.rs # Response handling
│ └── method.rs # HTTP methods
├── auth/ # Authentication
│ ├── basic.rs # Basic auth
│ ├── bearer.rs # Bearer tokens
│ ├── apikey.rs # API key auth
│ └── oauth2.rs # OAuth 2.0 (5 grant types)
├── upload/ # File uploads
│ ├── file.rs # File upload handling
│ ├── form.rs # Form data
│ └── multipart.rs # Multipart builder
├── scripts/ # Scripting engine
│ ├── script.rs # Script definitions
│ ├── engine.rs # Rhai execution engine
│ └── context.rs # Script context
├── assertions/ # Validation system
│ ├── assertion.rs # Assertion definitions
│ ├── matcher.rs # 14 matcher types
│ └── validator.rs # Response validation
├── workflow/ # Request chaining
│ ├── step.rs # Workflow steps
│ ├── chain.rs # Request chains
│ ├── executor.rs # Workflow execution
│ └── variables.rs # Variable substitution
├── collections/ # Collections & workspaces
├── env/ # Environment management
├── session/ # Session & cookies
└── history/ # Request history
280+ Unit Tests covering all modules
# All tests
cargo test
# Library tests only
cargo test --lib
# With output
cargo test -- --nocapturecargo bench# Format code
cargo fmt
# Run linter
cargo clippy -- -D warnings- Core HTTP client (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
- Interactive REPL mode
- Beautiful terminal output (light & dark mode optimized)
- Modular architecture with 13+ modules
- Comprehensive test suite (280+ tests)
- Collections and workspaces
- Environment variables
- Request/response history
- Sessions and cookie management
- File upload and multipart forms
- Authentication suite (Basic, Bearer, API Key, OAuth 2.0)
- Pre/post-request scripting (Rhai engine)
- Response assertions (14 matcher types)
- Request chaining and workflows
- Full async/await with Tokio
- HTTP/2 and HTTP/3 support
- WebSocket client
- GraphQL support
- Performance testing & load generation
- Mock server capabilities
- CI/CD test runner (Newman-like)
- OpenAPI/Swagger import
- Enhanced TUI with syntax highlighting
- Autocomplete for URLs and headers
- Security testing (OWASP checks)
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes and add tests
- Run tests:
cargo test - Format code:
cargo fmt - Commit:
git commit -m 'Add amazing feature' - Push:
git push origin feature/amazing-feature - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Hassan Bazzoun
- Email: hassan.bazzoundev@gmail.com
- GitHub: @yourusername
Built with these open-source libraries:
- Rust - Systems programming language
- reqwest - HTTP client
- clap - Command-line parser
- rustyline - Readline implementation
- colored - Terminal colors
- serde - Serialization framework
- rhai - Embedded scripting language
- tokio - Async runtime