fluree multi-query
May 29, 2026 · View on GitHub
Execute a multi-query envelope — bundle multiple JSON-LD and/or SPARQL queries into a single request that runs them in parallel against one shared snapshot moment.
Usage
fluree multi-query [FILE] [OPTIONS]
Arguments
| Arguments | Behavior |
|---|---|
| (none) | Read envelope JSON from -e, -f, or stdin |
<file> | Path to envelope JSON file |
Options
| Option | Description |
|---|---|
-e, --expr <JSON> | Inline envelope JSON (alternative to positional file or -f) |
-f, --file <FILE> | Read envelope from a file |
--format <FORMAT> | Per-alias result format: json (default — per-language defaults) or typed-json (always-typed @value / @type literal shape). Matches the same flag on fluree query. |
--normalize-arrays | Always wrap multi-value JSON-LD properties in arrays. Matches the same flag on fluree query; applies to JSON-LD aliases. |
--output <VIEW> | Envelope display: json (compact, default), pretty (indented), or aliases (per-alias sections). Controls how the response envelope is printed to the terminal — does not affect alias result formatting (use --format for that). |
--remote <NAME> | Execute against a named remote server |
--as <IDENTITY> | Bearer identity to assume (subject to the impersonation gate — see Policy Contract) |
--policy-class <IRI> | Policy class IRI(s); repeatable |
--policy <JSON> | Inline policy JSON (fluree-policy) |
--policy-file <FILE> | Read inline policy JSON from a file |
--policy-values <JSON> | Variable bindings for parameterized policies |
--policy-values-file <FILE> | Read policy-values JSON from a file |
--default-allow | Permit access when no matching policy rules exist |
Policy flags ride on the underlying HTTP request as fluree-policy-*
headers; the server folds them into the envelope's top-level opts
before validation, and the standard envelope → sub-query opts merge carries them into every alias. They take effect on JSON-LD sub-queries via the same code path single-query /query uses. For SPARQL sub-queries the headers are accepted and bearer ledger-scope still applies, but identity / policy threading via QueryConnectionOptions is not consumed — the same gap that exists for connection-scoped SPARQL on /query. See Limitations for the canonical list.
Description
Bundles N independent queries against a shared per-ledger snapshot.
Each sub-query carries its own from and its own language (JSON-LD or
SPARQL); the server runs them in parallel under bounded concurrency
and returns a per-alias response map plus an aggregate status.
See Multi-query envelope for the full envelope wire format, response shape, snapshot semantics, merge rules, bounds, and current limitations.
Transport
fluree multi-query runs the envelope through whichever of three transports applies:
--remote <name>— explicit; routes through the named remote fromremotes.toml. OIDC token refresh is persisted back toconfig.tomlafter the round-trip.- Auto-route to a locally running
fluree server— used when--remoteis omitted andserver.meta.jsonreports a live pid. Suppressed by--direct. - In-process local —
fluree multi-querycallsFluree::multi_query()directly against the storage tree configured for this.fluree/directory. Used when neither--remotenor a running local server is available, or when--directis set. This is the natural counterpart tofluree queryrunning locally.
In-process mode reads the same storage path, indexing thresholds, and prefix table as every other local CLI command. No HTTP, no server, no auth — the caller already has direct access to the storage tree.
Examples
Inline envelope, locally running server
fluree server start &
fluree multi-query -e '{
"queries": {
"people": {
"language": "jsonld",
"query": {
"@context": {"ex": "http://example.org/"},
"from": "mydb",
"select": ["?name"],
"where": {"@id": "?p", "ex:name": "?name"}
}
},
"orders": {
"language": "sparql",
"query": "PREFIX ex: <http://example.org/> SELECT ?id FROM <mydb> WHERE { ?o ex:orderId ?id }"
}
}
}'
Envelope file, named remote
fluree multi-query envelope.json --remote origin
Stdin, pretty-printed response
cat envelope.json | fluree multi-query --remote origin --output pretty
Typed JSON alias results + pretty envelope view
Pick the per-alias result shape with --format and the envelope display
with --output. They're independent: --format controls what the
server formats into each results entry, --output controls how the
CLI prints the whole response on your terminal.
fluree multi-query envelope.json --format typed-json --normalize-arrays --output pretty
Per-alias section view
The --output aliases view prints a section per alias, with successful
results and per-alias errors clearly separated. Useful for shell-piping
results when you only want to inspect one alias and the response is
large.
fluree multi-query envelope.json --remote origin --output aliases
Output (abbreviated):
status: ok
asOf: 2024-01-01T12:00:00.123Z
mydb @ t:42
# people (ok)
[
{ "name": "Alice" },
...
]
# orders (ok)
{ "head": ..., "results": ... }
Per-alias result format (--format)
| Value | JSON-LD aliases | SPARQL aliases |
|---|---|---|
json (default) | JSON-LD shape | SPARQL Results JSON |
typed-json | typed @value/@type shape | typed @value/@type shape (cross-language) |
--normalize-arrays wraps single-valued JSON-LD properties in arrays.
On JSON-LD aliases it composes with whichever --format is in effect.
On SPARQL aliases it's a no-op — SPARQL Results JSON already has its own
binding shape and isn't affected. This matches the same flags on
fluree query.
--format typed-json is cross-language by design — it gives every alias
a unified typed shape. --format json (with or without
--normalize-arrays) keeps SPARQL aliases on their SPARQL Results JSON
default, so you can --normalize-arrays a mixed envelope without
silently changing the SPARQL alias's wire shape.
Format selection rides on the wire as Fluree-Output-Format /
Fluree-Normalize-Arrays headers when using --remote or auto-routing
to a local server; in-process mode wires them straight into the api
crate's MultiQueryBuilder::format(...).
Only JSON-producing shapes are valid inside a multi-query envelope —
TSV / CSV / SPARQL XML / RDF XML are rejected upstream because the
envelope's results map can't embed byte/string payloads. Use single
queries against fluree query when you need those.
Envelope display (--output)
| Value | Shape | Use case |
|---|---|---|
json (default) | Compact JSON, one line | Machine processing; piping into jq |
pretty | Indented JSON | Human reading |
aliases | Per-alias section header + indented result block | Quick visual inspection; per-alias debugging |
The response shape itself is documented in Multi-query envelope → Response shape.
Status mapping
fluree multi-query exits 0 when the server returned HTTP 200,
regardless of the body's status field. Clients that need to branch
on aggregate outcome (ok / partial / all_failed) should inspect
the response JSON. A non-zero exit code indicates an envelope-level
failure (validation 4xx, snapshot resolution 5xx, transport error, or
envelope JSON malformed).
Limitations
The full list lives in the envelope reference. Highlights:
- History queries (
tofield /FROM <…> TO <…>) are rejected with 400. - Envelope-level
opts.max-fuelis rejected with 400 (per-sub-queryopts.max-fuelstill works). - Response size cap is enforced at assembly, not throughout dispatch; peak memory during dispatch can exceed the envelope cap.
opts.tat any level inside the envelope is rejected — usefromor envelopeasOf.- SPARQL sub-queries do not consume merged policy opts — same gap as single-query connection-scoped SPARQL.