Skip to content

Fallbacks & Resilience

When wiring external data sources, you must account for missing data and network failures. The Bridge engine handles this natively without requiring imperative try/catch or if/else blocks.

Instead, it relies on a 3-Layer Architecture that gives you surgical control over exactly how and when your APIs fail.

By default, if a tool throws a network exception (e.g., HTTP 500, timeout), the error bubbles up and crashes the GraphQL field.

To gracefully handle I/O failures, you use the Safe Execution Modifier (?.), perfectly mirroring TypeScript’s Optional Chaining. Appending ?. tells the engine: “If this tool throws an error, or the data is missing, swallow the error and return undefined.”

# STRICT: If the billing API crashes, the field crashes.
o.receipt <- billingApi.charge
# SAFE: If the avatar API crashes, swallow the 500 error and yield `undefined`.
o.avatar <- avatarApi?.url

To ensure API contracts are respected, path traversal in The Bridge behaves exactly like standard TypeScript. Where you place the ?. matters immensely.

Consider these two lines:

  1. o.out <- user?.info.name
  2. o.out <- user.info?.name

If the user API is called but the backend returns a completely empty object {}, here is how the engine responds:

  • Scenario 1: .info evaluates safely to undefined. We then attempt to access .name strictly. The engine throws a TypeError and crashes the field!
  • Scenario 2: .info is accessed strictly, resulting in undefined. The ?. on name intercepts the subsequent structural crash and safely yields undefined.

If the user API throws a 500 Network Error:

  • Scenario 1: The ?. is on the root tool. It intercepts the network crash and yields undefined.
  • Scenario 2: The root tool is evaluated strictly. The network error crashes the field!

2. Layer 2: Data-Level Routing (|| and ??)

Section titled “2. Layer 2: Data-Level Routing (|| and ??)”

Once Layer 1 resolves the data (or yields undefined), the engine evaluates Data Routing gates to determine what value to pass down the wire.

When you chain multiple sources together, the engine evaluates them left to right, sequentially. It stops and short-circuits at the very first acceptable result. Sources to the right of the winner are never called.

The || operator triggers if the left side is falsy (0, "", false, null, undefined).

# If the user has no nickname, or it is an empty string "", fall back to "Guest".
o.displayName <- userApi?.nickname || "Guest"

The ?? operator triggers only if the left side is exactly null or undefined. It respects 0, "", and false as valid data.

# If the user's balance is exactly $0, we WANT to show 0.
# We only fall back if the data is actually missing.
o.walletBalance <- billingApi?.balance ?? -1

The || and ?? operators do not catch network errors. If a strict tool throws an HTTP 500, it blows past the data routing gates entirely.

If you want to provide an absolute, final safety net for the entire wire in case any strict evaluation fails, use the catch keyword.

# 1. Evaluate B strictly.
# 2. If it is nullish, evaluate C strictly.
# 3. If B or C throw a 500 network error, catch it and return "Error".
o.value <- B.field ?? C.field catch "Error"

The catch fallback is evaluated only if an unhandled exception occurred during the execution of that specific wire.

Sometimes a fallback isn’t enough, and you actively want to break the execution flow based on business logic. The Bridge provides explicit control flow keywords to seamlessly bridge the gap between declarative mappings and GraphQL’s robust error-handling spec.

Fails the specific output field, but allows the rest of the graph to continue evaluating. This maps perfectly to a standard GraphQL partial error in the errors array.

# If the API is missing data or crashes, throw a domain-specific error
o.receipt <- billingApi?.charge ?? throw "Billing system unavailable"

Stops all engine processing immediately and fails the entire request. This acts exactly like an external client disconnect (AbortSignal) and results in a top-level error (e.g., HTTP 403/500).

o.secureData <- authApi.token ?? panic "CRITICAL: Unauthorized access"

What happens if you define multiple completely separate lines to target the exact same field?

o.city <- i.city
o.city <- geoApi.cityName

This is called Overdefinition. Instead of throwing a syntax error or blindly racing all the sources in parallel, the engine forms an implicit coalesce group and sorts them by estimated execution cost.

Cost TierSourcesWhy
Free (0)input, context, const, aliasPure memory reads. Zero I/O overhead.
Computed (1)Tool calls, Pipes, DefinesRequire scheduling and possible network I/O.

When a target is overdefined, the engine evaluates the sources cheapest first:

  1. It tries all Free (Cost 0) sources.
  2. If they all return null or undefined, it moves to Computed (Cost 1) sources.
  3. It stops and returns the very first non-nullish result it finds.

(Note: The engine respects falsy values like 0, "", and false. If a cheap source returns 0, the engine accepts it and short-circuits, saving the cost of evaluating the more expensive sources.)