Skip to content

Sparse Fieldsets

When a client only needs a subset of a bridge’s output, Sparse Fieldsets let you tell the engine exactly which fields to resolve. Tools that feed exclusively into unrequested fields are never called, saving time and upstream bandwidth.

Both the interpreter (@stackables/bridge-core) and the compiler (@stackables/bridge-compiler) accept an optional requestedFields array in ExecuteBridgeOptions:

import { executeBridge, parseBridge } from "@stackables/bridge";
const document = parseBridge(bridgeText);
const { data } = await executeBridge({
document,
operation: "Query.searchTrains",
input: { from: "Bern", to: "Zürich" },
requestedFields: ["id", "status", "legs.*"],
tools: { /* ... */ },
});

When requestedFields is omitted (or empty), every output field is resolved — the default behavior.

Patterns are dot-separated paths with an optional trailing wildcard:

PatternMatches
"id"The top-level id field
"legs"The entire legs object (all children included)
"legs.duration"Only legs.duration — other legs children are skipped
"legs.*"Every immediate child of legs (e.g. legs.duration, legs.distance)

A field is included if any pattern matches it. Ancestor fields are included automatically when a deeper path is requested (e.g., requesting "legs.duration" ensures the legs object exists in the output).

Sparse fieldsets are especially useful when mapping HTTP query parameters to bridge execution, allowing mobile apps to request lightweight payloads while desktop apps fetch richer data from the same .bridge file:

// Express / Fastify handler
app.get("/api/trains", async (req, res) => {
const raw = req.query.fields; // e.g. "id,status,legs.duration"
const fields = typeof raw === "string"
? raw.split(",").filter((f) => /^[\w.*]+$/.test(f))
: undefined;
const { data } = await executeBridge({
document,
operation: "Query.searchTrains",
input: req.query,
requestedFields: fields,
tools: { /* ... */ },
});
res.json(data);
});
GET /api/trains?from=Bern&to=Zürich&fields=id,status,legs.duration

The interpreter filters the set of output fields collected from output wires before beginning the pull loop. Because execution is pull-based, dropping an output field means the engine never traces backward to the tools that feed it — they are simply never scheduled.

The compiler filters output wires at code-generation time. A backward reachability analysis then eliminates all tools that are no longer transitively referenced by any remaining output wire. The generated JavaScript function contains only the code paths needed for the requested fields.

Different requestedFields shapes produce different compiled functions. Each shape is cached independently so subsequent calls with the same field set reuse the optimally-sized function.