← All showcases

jsonreq — a command-line client for JSON APIs

Source on GitHub ↗

curl+jq as one small Raku program: make an HTTP(S) request, parse the JSON that comes back, optionally pull one value out, print it pretty or minified. Unlike every other showcase, almost none of it is hand-rolled — the program is an argument parser and glue around two modules from github.com/ash/raku-modules:

Usage

export RAKULIB=$HOME/raku-modules/JSON-Native/lib,$HOME/raku-modules/HTTP-Simple/lib

build/rakupp showcase/jsonreq/jsonreq.raku https://api.github.com/repos/ash/rakupp --query=.stargazers_count
build/rakupp showcase/jsonreq/jsonreq.raku URL                             # GET, pretty-printed
build/rakupp showcase/jsonreq/jsonreq.raku URL --compact                   # minified
build/rakupp showcase/jsonreq/jsonreq.raku URL --query=.users[0].name -r   # one value, bare
build/rakupp showcase/jsonreq/jsonreq.raku POST URL --json='{"a": 1}'      # send JSON
build/rakupp showcase/jsonreq/jsonreq.raku URL --json=@body.json           # body from a file (implies POST)
build/rakupp showcase/jsonreq/jsonreq.raku URL --header='X-Token: t' -v    # -v shows the exchange on stderr
build/rakupp showcase/jsonreq/jsonreq.raku data.json --query=.users[-1]    # a local file — no socket at all
cat data.json | build/rakupp showcase/jsonreq/jsonreq.raku - --compact     # or stdin

A target without an http(s):// scheme is a local document — a path, a file:// URL, or - for stdin — run through the same query/pretty-print pipeline. It is presumed JSON, so a broken file dies with the parse error instead of echoing through (--raw prints it untouched); the HTTP-only options (--json, --header, --auth, --bearer, --insecure) refuse a local target.

The RAKULIB line is temporary: once the two distributions are on fez, zef install JSON::Native HTTP::Simple replaces it.

OptionWhat it does
--query=<path> / -qextract one value: .key, [n], [-n] steps, e.g. .users[0].name
--compact / -cminified output instead of pretty-printed
-r / --stringprint a Str result bare, like jq -r
--sortedsort object keys — stable output for diffs
--rawprint the body untouched, whatever it is
--header='Name: value'add a request header (repeatable)
--json=<body>request body: JSON text, @file, or - for stdin; implies POST
--auth=user:pass, --bearer=<token>HTTP Basic / Bearer authorization
--timeout=<s>, --insecureexchange timeout; skip TLS certificate verification
-v / --verboserequest line, JSON backend, response status + headers, on stderr

Behaviour worth knowing: the request body is parsed before it is sent, so a typo dies locally instead of round-tripping as a server-side 400; a non-JSON response body prints as-is (a JSON tool still shows you the HTML error page); and an HTTP status ≥ 400 exits 1 after printing the body, like curl --fail except the error body is not thrown away.

The MAIN story

The whole option table above is one sub MAIN signature — the usage text, type checks, repeatable --header, and the -q/--query aliases all come from it. %*SUB-MAIN-OPTS<named-anywhere> lets the options follow the URL, which is how a curl-shaped tool reads naturally; supporting that (and collecting a repeated option into an array, oracle-verified against Rakudo) went into the Raku++ MAIN dispatcher as part of building this showcase.

The compare

RAKUPP=build/rakupp sh showcase/jsonreq/compare.sh

starts the rakus showcase serving sample/ on the loopback, runs twelve jsonreq commands under Rakudo and under Raku++ — queries, negative indexes, null, a POST refused with a 405, a 404, and two local-file reads — and diffs STDOUT and exit codes byte-for-byte.

Like every showcase, it compiles: build/rakupp --exe -o jsonreq showcase/jsonreq/jsonreq.raku produces a single binary (interpreter-bundled, as the program has a CATCH block).