Fetch Braids: Deep Dive #85
State Modeling Specialist
Fetch braids are a disciplined way to combine multiple data streams into one route surface without pretending you have "a single request". A braid is a visible structure:
- multiple fetch strands (list + summary + detail),
- an explicit join point (where you combine them),
- and evidence that proves the join happened (ledger + DOM).
Braids are what you do after lanes, ledgers, and pacts are in place. Once the route can choose posture, record receipts, and verify surfaces, the next failure mode is accidental composition: combining streams silently and producing drift.
Required docs links:
Extra docs links for this entry:
A braid is only real when the join is explicit and recorded. If you merge data "inline" inside render without a braid key, you've created an un-auditable surface that will drift during migrations.
Strand taxonomy (the three common braids)
Most routes have the same three strands:
- list: the primary collection (
orders:list) - summary: aggregate metadata (
orders:summary) - detail: a selected entity (
orders:detail)
The braid is the route-owned join that produces a render-ready surface.
Worked Example: braiding list + summary into a single OrdersSurface
Continuing the /warehouse orders track:
- list fetch gives us items
- summary fetch gives totals/status counts
- we braid them into a single surface that the UI renders
Step 1: Define a braid key and surface shape
export const OrdersBraid = {
id: "braid:orders:v1",
strands: ["orders:list", "orders:summary"],
};
The braid id is versioned. That’s what makes it stable across refactors.
Step 2: Fetch the strands (still lane-aware, still ledgered)
import { useLedgeredFetch } from "../useLedgeredFetch";
export function useOrdersStrands({ lane, ledger }: any) {
const list = useLedgeredFetch({
lane,
ledger,
requestKey: "orders:list",
url: "/api/orders?limit=50",
});
const summary = useLedgeredFetch({
lane,
ledger,
requestKey: "orders:summary",
url: "/api/orders/summary",
});
return { list, summary };
}
Step 3: Join at a single point (braid join function)
export function braidOrdersSurface(input: any) {
const items = Array.isArray(input.list?.items) ? input.list.items : [];
const counts = input.summary?.counts ?? {};
const enriched = items.map((o: any) => ({
...o,
statusCountHint: counts[o.status] ?? 0,
}));
return {
braidId: "braid:orders:v1",
total: input.summary?.total ?? enriched.length,
items: enriched,
counts,
};
}
This function is intentionally pure and boring. The drama happens in the evidence.
Step 4: Record the join as evidence (ledger + DOM)
import { useLiveState, useRenderEffect } from "react";
export function useBraidLedger(namespace: string) {
return useLiveState(namespace, {
braidId: "",
joins: [],
lastJoin: null,
});
}
export function useOrdersBraid({ lane, ledger, braidLedger }: any) {
const strands = useOrdersStrands({ lane, ledger });
const surface = braidOrdersSurface(strands);
useRenderEffect(() => {
const join = {
id: `${lane.laneKey}:${surface.braidId}`,
at: Date.now(),
laneId: lane.laneId,
laneKey: lane.laneKey,
braidId: surface.braidId,
strands: OrdersBraid.strands,
total: surface.total,
items: surface.items.length,
};
braidLedger.write("braidId", surface.braidId);
braidLedger.write("lastJoin", join);
braidLedger.write("joins", [...braidLedger.read("joins"), join].slice(-40));
const receipt = {
id: `${lane.laneKey}:braid:orders`,
at: join.at,
laneId: lane.laneId,
laneKey: lane.laneKey,
requestKey: `braid:${surface.braidId}`,
url: "braid://orders",
ok: true,
status: 200,
ms: 0,
cache: "braid",
dedupe: "required",
note: `joined:${OrdersBraid.strands.join("+")}`,
};
ledger.write("receipts", [...ledger.read("receipts"), receipt]);
});
return surface;
}
Step 5: Render the braid surface (and the braid evidence)
export default function OrdersTrack({ lane, ledger }: any) {
const braidLedger = useBraidLedger("ledger:/warehouse:braids");
const surface = useOrdersBraid({ lane, ledger, braidLedger });
return (
<section data-track="orders" data-braid={surface.braidId}>
<h2>Orders</h2>
<div data-surface="orders">
<div>Total: {surface.total}</div>
<pre data-counts>{JSON.stringify(surface.counts, null, 2)}</pre>
</div>
<ul>
{surface.items.map((o: any) => (
<li key={o.id} data-order={o.id} data-status={o.status}>
{o.number}{" "}
<span className="text-sm text-[color:var(--docs-muted)]">
hint:{o.statusCountHint}
</span>
</li>
))}
</ul>
<aside aria-label="Braid evidence">
<strong>Braid Ledger</strong>
<pre data-braid-ledger>{JSON.stringify(braidLedger.read(), null, 2)}</pre>
</aside>
</section>
);
}
Why braids beat “just combine it”
Routes drift when joins are implicit:
- multiple requests happen in render,
- the UI merges them ad-hoc,
- nobody records which strands were used,
- and migrations silently change behavior.
Braids prevent that by creating a named join:
- stable braid ids,
- recorded joins (counts + strand keys),
- ledger receipts for "join events",
- and DOM evidence to assert against.
Checklist
- Braid ids are versioned and stable.
- Strands have stable request keys.
- Joins happen at one explicit function boundary.
- Join evidence is recorded (ledger + DOM).
- Prefer braids when multiple surfaces depend on the same combined data.