yurl 0.6.1

HTTP client — batch, interactive, concurrent, streaming, output routing, caching
yurl-0.6.1 is not a library.

yurl

HTTP client — batch, interactive, concurrent, streaming, output routing, caching. Built on yttp, the "Better HTTP" JSON/YAML facade.

Reference · Guide · Cookbook

Install: cargo install yurl

echo '{g: https://jsonplaceholder.typicode.com/posts/1}' | yurl
s: {v: HTTP/1.1, c: 200, t: OK}
h:
  content-type: application/json
b:
  id: 1
  title: sunt aut facere...

Request

Reads from stdin as JSONL (one per line) or YAML (--- separated). Streaming — requests execute before EOF.

Key Description
method (g, p, d, put, patch, head, options, trace) URL
h headers (keys and values support shortcuts)
b body (encoding follows Content-Type)
md metadata fields, available in output and file path templates

URLs without a scheme: localhost/127.0.0.1/[::1]/bare hostnames get http://, else https://.

Body encoding

Content-Type Shortcut Encoding
application/json (default) c!: j! JSON body
application/x-www-form-urlencoded c!: f! key=value&... from b object
multipart/form-data c!: m! multipart; file:// values read from disk

Header shortcuts

Shortcut Expands to
json! / j! application/json
form! / f! application/x-www-form-urlencoded
multi! / m! multipart/form-data
html! / h! text/html
text! / t! text/plain
xml! / x! application/xml
a!/suffix application/suffix
t!/suffix text/suffix
i!/suffix image/suffix
basic!user:pass Basic base64(user:pass)
bearer!token Bearer token
Key shortcuts
a! / auth! Authorization header key
c! / ct! Content-Type header key

Authorization

a! inside h sets Authorization. Scheme inferred from value:

Value Result
a!: token Bearer token
a!: [user, pass] Basic base64(user:pass)
a!: Scheme value passthrough

$VAR in config headers expands from environment. Only pure $VAR values (entire string is $ + alphanumeric/underscore).

Output

Destinations: "1" (stdout), "2" (stderr), "file://path" (supports {{atom}} templates).

Default: y(s!,h,b) for yurl, j(s!,h,b) for jurl (same binary, JSON output). Per-request output keys fully replace config defaults.

Format atoms

Atom Description
b / o.b response body (raw outside j()/y(), smart-encoded inside)
h / o.h response headers
s / o.s status line; s.code, s.text, s.version for parts
i.b, i.h, i.s request echo (input body, headers, status)
m request method
u full URL; u.scheme, u.host, u.port, u.path, u.query, u.fragment
idx auto-incrementing request index (0-based)
md, md.* metadata value or field

j(...) wraps atoms as JSON object. y(...) as YAML. Body in j()/y(): JSON -> structured, UTF-8 -> string, binary -> base64.

Batch config

CLI argument provides shared config for all stdin requests. Merge order: config -> rules -> per-request.

Key Description
h default headers (shortcuts work)
1, 2, file://... default output destinations
api API alias(es) — string or {name: url, ...}
concurrency global max in-flight requests (default: 1)
progress true (spinner) or N (progress bar)
rules conditional overrides (see below)

API aliases

api: https://api.example.com/v1        # single, used as api!/path
api: {prod: https://api.example.com, staging: https://staging.example.com}

name!/path in URLs expands to base/path. Unmatched names pass through unchanged.

Rules

Match key Matching
u URL glob (* = segment, ** = any)
m HTTP method (case-insensitive)
md.<field> exact metadata field match

Rule fields: h (headers), concurrency, cache. Multiple match criteria are ANDed. See batch config reference for examples.

Concurrency

  • Global: concurrency: N in config
  • Per-endpoint: concurrency on rules. Request acquires global + all matching rule permits
  • Outputs buffered and flushed atomically with concurrency > 1
  • File paths with {{idx}} auto-stream; ?stream suffix to force streaming
  • With concurrency: 1 (default), everything auto-streams

Caching

cache: true on a rule is shorthand for {ttl: 0, keys: [m, u, b], at: ~/Library/Caches/yurl}.

Key Default Description
ttl 0 seconds until expiry (0 = no expiry)
keys [m, u, b] hash components: m u b a h h.<name>
at OS cache dir cache directory path

Application-level caching (not HTTP-compliant). Does not respect Cache-Control/ETag/Vary.

Progress

progress: true (spinner) or progress: N (progress bar). Suppresses stderr output; shows suppressed count.

Step mode

--step flag for interactive debugging of piped requests. See Guide for full walkthrough.

$ echo '
{g: api!/toys}
{g: api!/toys/1}
{p: api!/toys, b: {name: Owl}}
' | yurl --step '{api: localhost:3000, h: {a!: bearer!tok}, 1: "j(s,b)"}'

yurl v0.5.0

> .c
  config: api: api | h: 1 header | output: 1

> .next                              # pre-fills with {g: api!/toys}
> .x {g: api!/toys}                  # Ctrl-A, prepend .x to expand
> {"get":"http://localhost:3000/toys","h":{"Authorization":"Bearer tok"},"1":"j(s,b)"}
{"s":"200 OK","b":[{"id":1,"name":"Fox"},{"id":2,"name":"Cat"}]}

> .go                                # run remaining 2 requests
{"s":"200 OK","b":{"id":1,"name":"Fox","price":12.99}}
{"s":"201 Created","b":{"id":3,"name":"Owl"}}
  2 requests executed

> .c {api: {s: staging.example.com}}
  config: api: s

> {g: s!/toys}                       # ad-hoc request with new config
{"s":"200 OK","b":[...]}
Command Description
.next / .n load next piped request, edit, Enter to send
.go / .g run all remaining, Ctrl-C to stop
.x {req} expand with config, review before sending
.c show config; .c {cfg} to replace
.help / .h show help

Reference

yttp — request and response (spec)

# request
g: https://example.com                # method shortcuts: g p d, or full names
h: {a!: my-token, c!: j!}             # header key/value shortcuts expand in place
b: {city: Berlin}                     # body encoding follows Content-Type

# response — default output: y(s!,h,b)
s: {v: HTTP/1.1, c: 200, t: OK}       # s! -> status inline object
h: {content-type: application/json}   # response headers
b: {city: Berlin, lang: de}           # JSON -> structured, UTF-8 -> string, binary -> base64

yurl extensions

# metadata
md: {env: prod, batch: 7}            # available in output and file path templates

# output destinations
1: j(s!,h,b)                          # stdout (jurl default)
1: y(s!,h,b)                          # stdout (yurl default)
2: s                                  # stderr
file://response.raw: b                # file
file://{{md.env}}/{{idx}}.json: j(s!,h,b)  # templated path, auto-streamed
file://large.bin?stream: b            # explicit streaming

# atoms
# response:  b    h    s!   s    s.c  s.t  s.v  (or s.code s.text s.version)
#     or:    o.b  o.h  ...
# request:   i.b  i.h  i.s
# URL:       u.scheme  u.host  u.port  u.path  u.query  u.fragment
# other:     m    u    idx  md  md.*

Batch config

api: https://api.example.com/v1       # string or {name: url, ...}

h:                                    # default headers
  a!: bearer!my-token
  User-Agent: jurl/0.1

1: j(idx, s.code)                     # default output

concurrency: 10                       # global max (default: 1)
progress: true                        # spinner or N for progress bar

rules:
  - match: {u: "**slow-api**"}
    concurrency: 2
  - match: {m: POST}
    h: {c!: f!}
  - match: {md.env: prod}
    h: {X-Debug: "false"}
  - match: {u: "**api.openai.com**"}
    cache: true                       # {ttl: 0, keys: [m,u,b], at: default}
  - match: {u: "**api.example.com**"}
    cache: {ttl: 3600, keys: [u, b, a], at: ./.cache}

# merge order: config -> rules (in order) -> per-request