Skip to content

jdiamond/httpster

Repository files navigation

httpster

A hip HTTP API client with an ergonomic CLI.

Installation

cargo install --path .

Ad-Hoc Requests

The simplest way to use httpster is to make ad-hoc requests to any URL.

Note

The sub-command to send a request is request, but you can use the shorter aliases req or r for brevity. It's also possible to use alias h=httpster in your shell to shorten the full command even further.

# Simple GET request (using full sub-command name)
httpster request https://api.example.com/users/123

# POST with inline JSON (using short sub-command name)
httpster r https://api.example.com/users -m POST -t json -b '{"name":"Alice","email":"alice@example.com"}'

# Add custom headers and query parameters
httpster r https://api.example.com/search \
  -H 'X-Trace: abc123' \
  -q q=alice -q page=2

# POST with body from a file
httpster r https://api.example.com/users -m POST -t json -b @user.json

The response body goes to stdout (so you can pipe it), and the HTTP status/headers go to stderr (so you can still see them):

# Capture just the body, see status and headers
httpster r https://api.example.com/users/123 > response.json

# Pipe to jq for formatting
httpster r https://api.example.com/users/123 | jq '.[] | {name, email}'

Defined Requests

Beyond ad-hoc requests, you can define requests in TOML files to save and reuse them.

Direct File References

Create a TOML file with request details and execute it with the explicit file path:

# my-request.toml
method = "GET"
url = "https://api.example.com/users"

Run it with @ prefix to use an explicit path:

httpster r @my-request.toml

This works for any request file anywhere on your system. You can also add headers, query parameters, and other details:

# my-request.toml
method = "GET"
url = "https://api.example.com/search"
headers.Accept = "application/json"
query.q = "alice"
query.page = "1"

Using Conventions

httpster uses conventions to locate request files automatically. Create a httpster.toml file at your project root:

project/
  httpster.toml
  requests/
    users/
      list.toml
      get.toml
      create.toml

Run requests by their path under requests/ (minus .toml):

httpster r users/list
httpster r users/get
httpster r users/create

A simple request file:

# requests/users/list.toml
method = "GET"
url = "https://api.example.com/users"

By default, httpster looks for a httpster.toml file and uses these conventions:

# httpster.toml (defaults if not specified)
[conventions]
requests = "requests"
profiles = "profiles"
contexts = "contexts"

You can customize the paths in your httpster.toml to organize files however you prefer:

# httpster.toml - nested structure example
[conventions]
requests = "httpster/requests"
profiles = "httpster/profiles"
contexts = "httpster/contexts"

Profiles: Reusable Building Blocks

Profiles are reusable sets of request fields that can be referenced by multiple requests. By default, create them under profiles/:

# profiles/api.toml
url = "https://api.example.com"
type = "json"
headers.Accept = "application/json"
headers."Content-Type" = "application/json"

# profiles/auth.toml
auth.type = "bearer"
[auth.bearer]
token = "your-token-here"

Reference profiles in your requests:

# requests/users/list.toml
profiles = ["api", "auth"]
method = "GET"
path = "/users"

You can also use profiles ad-hoc in CLI commands:

httpster r https://api.example.com/users \
  -p api \
  -p auth

Or override the URL for a defined request:

# Use the users/list request but with a different URL
httpster r users/list -u https://api.staging.example.com

Contexts: Variables per Environment

Contexts are files containing variables that get interpolated into requests, making it easy to switch between environments (dev, prod, etc.). By default, create them under contexts/:

# contexts/dev.toml
api_url = "https://api.dev.example.com"
token = "dev-token"

# contexts/prod.toml
api_url = "https://api.example.com"
token = "prod-token"

Use variables in profiles and requests:

# profiles/api.toml
url = "{{ api_url }}"
type = "json"

# profiles/auth.toml
auth.type = "bearer"
[auth.bearer]
token = "{{ token }}"

Specify a context when running requests:

httpster r users/list -c dev

Set a default context in httpster.toml:

# httpster.toml
[defaults]
context = "dev"

Or use environment variable:

export HTTPSTER_CONTEXT=prod
httpster r users/list

You can also pass variables on the CLI to override context variables:

httpster r users/get -c dev --set id=42

Config Discovery

By default, httpster looks for config starting at the current directory and walking up. The first directory containing a httpster.toml file is considered the project root.

You can override this behavior:

  • HTTPSTER_CONFIG environment variable
  • --config <FILE> CLI option

Precedence: --config > HTTPSTER_CONFIG > discovered httpster.toml > defaults

Output Behavior

  • stderr: HTTP status line and response headers (optionally piped through configured header pipeline)
  • stdout: Response body (optionally piped based on content-type and pipelines)

This design makes it easy to pipe body to tools or capture it while still seeing status/headers:

# Capture body, see status/headers
httpster r https://api.example.com/users > users.json

# Pipe body through tools
httpster r https://api.example.com/users | jq '.[] | select(.active)'

# Both: see headers AND pipe body
httpster r https://api.example.com/users 2>&1 | tee response.log | jq '.'

Authentication

httpster supports Basic, Bearer, and OAuth2 client credentials. Auth configs are nested: set auth.type, populate the matching table, and optionally add auth.headers for scoped extra headers (e.g., API keys). Scoped headers are applied with auth but never override explicit headers.

Bearer Token (simple)

# profiles/auth_prod.toml
auth.type = "bearer"
[auth.bearer]
token = "your-token-here"

Set a default auth for all requests:

[defaults]
auth.type = "bearer"
[auth.bearer]
token = "your-token-here"

CLI override: --bearer-token <TOKEN>.

OAuth2 Client Credentials (automatic token fetch)

auth.type = "oauth2-client-credentials"
[auth.oauth2_client_credentials]
token_url = "https://auth.example.com/oauth/token"
client_id = "your-client-id"
client_secret = "your-client-secret"
scopes = ["read", "write"]

CLI override: --oauth2-token-url, --oauth2-client-id, --oauth2-client-secret, --oauth2-scope <SCOPE> (repeatable).

Basic Auth

auth.type = "basic"
[auth.basic]
username = "user"
password = "pass"

CLI override: --basic-username and --basic-password.

Auth-scoped headers (API keys or extra headers)

Attach additional headers that should be grouped with your auth (useful for API keys that should be redacted):

auth.type = "bearer"
[auth.bearer]
token = "{{ token }}"
[auth.headers]
X-API-Key = "{{ api_key }}"

CLI override: --auth-header KEY=VALUE (repeatable). If you only need an API-key-style header and no Authorization header, use regular headers instead (e.g., headers.X-API-Key or -H "X-API-Key: value").

Advanced: Project layout example

A typical project layout using default conventions:

project/
  httpster.toml
  contexts/
    dev.toml
    prod.toml
  profiles/
    api.toml
    auth.toml
    format.json.toml
  requests/
    users/
      list.toml
      get.toml
      create.toml

httpster.toml:

[defaults]
context = "dev"
type = "json"
headers.Accept = "application/json"
headers."Content-Type" = "application/json"
profiles = ["api", "auth"]

contexts/dev.toml:

api_url = "https://api.dev.example.com"
token = "dev-token"
user_id = "1"

profiles/api.toml:

url = "{{ api_url }}"
type = "json"

profiles/auth.toml:

auth.type = "bearer"
[auth.bearer]
token = "{{ token }}"

profiles/format.json.toml:

response_body_pipeline = ["jq", "bat -l json --style=numbers --color=always"]

requests/users/list.toml:

profiles = ["api", "auth", "format.json"]
method = "GET"
path = "/users"

requests/users/get.toml:

profiles = ["api", "auth", "format.json"]
method = "GET"
path = "/users/{{ user_id }}"

requests/users/create.toml:

profiles = ["api", "auth"]
method = "POST"
path = "/users"
body = '{"name":"{{ name }}","email":"{{ email }}"}'

Commands Reference

  • httpster request <REQUEST_PATH|URL> [...] (alias: httpster r) — Execute a predefined request by path or an ad-hoc URL
  • httpster contexts — List contexts under the configured contexts directory (marks default with *)
  • httpster context — Show the current context (from HTTPSTER_CONTEXT env or default)
  • httpster switch <CONTEXT_PATH> — Print an export statement to set HTTPSTER_CONTEXT (use: eval "$(httpster switch prod)")
  • httpster completions <SHELL> — Generate shell completions

Common flags (ad-hoc and request):

  • --set key=value — Set a variable (overrides context variables, repeatable)
  • -c, --context <PATH> — Use a specific context (convention path or @file)
  • -t, --type <type> — Set content type and auto-configure headers (e.g., json, xml, form, or any MIME type)
  • -p, --profile <PATH> — Include a profile (repeatable, convention path or @file)
  • -H, --header key:value — Add a header (repeatable)
  • -q, --query key=value — Add a query parameter (repeatable)
  • --bearer-token <TOKEN> — Set bearer auth via CLI
  • --basic-username <USER> / --basic-password <PASS> — Set basic auth via CLI
  • --oauth2-token-url <URL>, --oauth2-client-id <ID>, --oauth2-client-secret <SECRET>, --oauth2-scope <SCOPE> (repeatable) — Set OAuth2 client-credentials auth via CLI
  • --auth-header KEY=VALUE — Add auth-scoped headers (repeatable)

Defaults & Environment

  • No default content type is assumed. Use --type, request type = "...", or explicit headers.
  • Set HTTPSTER_TYPE to define a session-wide default (e.g., export HTTPSTER_TYPE=json).
  • Precedence (content type): CLI headers > --type > request type > profile type > HTTPSTER_TYPE > none.
  • Query parameter ordering: Query parameters preserve their order from TOML configuration and CLI arguments. If both the URL and config/CLI provide query parameters with the same key, all values are appended (not overridden).

Response Pipelines

Response formatting pipelines let you automatically format responses through external tools. Pipelines can be specified as:

  • A single string (shorthand): response_body_pipeline = "jq"
  • An array of commands: response_body_pipeline = ["jq", "bat -l json"]
  • An object with content-type keys: response_body_pipeline = { "default" = [...], "application/json" = [...] }

Pipelines are specified on profiles or individual requests with this precedence:

  • Headers pipeline: request > profile > global defaults
  • Body pipeline: request > profile > content-type-specific (request/profile/global) > global defaults

For body pipelines, content-type matching works as follows:

  1. Try exact match (e.g., "application/json; charset=utf-8")
  2. Try base type (e.g., "application/json")
  3. Try "default" key

Development

Build the project:

cargo build

Run tests:

cargo test

Run locally:

cargo run -- https://api.example.com/users

For code coverage reports, install cargo-llvm-cov:

cargo install cargo-llvm-cov

Print a summary to the console:

just coverage

View HTML reports:

just coverage --html --open

About

A hip HTTP API testing tool

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors