Array Mapping
When you need to return a list of items in GraphQL, you cannot write standard for or map loops. Instead, The Bridge provides a declarative syntax for mapping array elements into your expected output shape, complete with explicit control flow to filter or halt the iteration.
1. The [] as iter Syntax
Section titled “1. The [] as iter Syntax”To map an array, you use the [] as <variableName> operator followed by a scoping block.
bridge Query.getJourneys { with routerApi as router with output as o
# Iterate over every element in router.journeys o.journeys <- router.journeys[] as j {
# Map fields for each individual element .label <- j.label .departureTime <- j.departure }}Shadow Scopes
Section titled “Shadow Scopes”When the engine executes an array mapping block, it creates a Shadow Scope (or shadow tree) for every single element in the array.
The variable j represents the current element being processed. Because each element executes in its own isolated scope, you can nest array mappings arbitrarily deep without worrying about variable collisions.
Aliasing Sub-fields for Readability
Section titled “Aliasing Sub-fields for Readability”You can also use aliases inside loops purely for readability, without triggering any tools. If your iterator has deeply nested data, bind it to a short variable:
o.list <- api.items[] as it {
# Bind a deep sub-object to a cleaner name alias it.metadata.authorInfo as author
.name <- author.name .role <- author.role}2. Array Control Flow
Section titled “2. Array Control Flow”APIs often return messy arrays containing nulls, missing IDs, or corrupt data. Instead of returning null to the frontend, you can use the explicit control flow keywords continue and break on the right side of any fallback gate (??, ||, catch) to filter the array directly.
Skipping Items (continue)
Section titled “Skipping Items (continue)”The continue keyword instructs the engine to omit this specific item from the final array, but keep looping and processing the rest of the elements.
o.items <- billingApi.items[] as item { # If the item is missing an ID, skip it entirely. # The frontend will not receive a null object; the item just won't exist. .id <- item.id ?? continue
.name <- item.name}Halting the Array (break)
Section titled “Halting the Array (break)”The break keyword instructs the engine to stop processing the array entirely and return the items processed up to that point.
o.items <- searchApi.results[] as item { .id <- item.id
# If we hit an item without a price, halt the entire array map. # Returns only the valid items that came before it. .price <- item.price ?? break}3. Common Use Cases
Section titled “3. Common Use Cases”Use Case 1: Filtering an Array Before Mapping
Section titled “Use Case 1: Filtering an Array Before Mapping”While continue is great for dropping items based on missing structural fields, sometimes you want to pre-filter a massive array based on explicit logic before mapping it. You can do this using the built-in std.filterObject tool.
bridge Query.getActiveAdmins { with std.filterObject as filter with usersApi as api with output as o
# 1. Provide the array to filter filter.in <- api.users
# 2. Define the exact criteria to match filter.role = "admin" filter.isActive = true
# 3. Map over ONLY the filtered results! o.admins <- filter[] as admin { .id <- admin.id .name <- admin.name }}Use Case 2: Fanout (API Call Per Element)
Section titled “Use Case 2: Fanout (API Call Per Element)”If you have an array of IDs and you need to fetch detailed information from an external API for every single item, you can perform a “Fanout.”
By combining array mapping with a Pipe and an alias, the engine will dynamically fork the tool and fire a parallel API request for every element in the array.
tool getUserDetail from std.httpCall { .baseUrl = "https://api.example.com/users" .method = "GET" # We leave .path blank so we can inject it dynamically!}
bridge Query.getEnrichedUsers { with getUserDetail with input as i with output as o
o.enrichedUsers <- i.userIds[] as id {
# 1. Dynamically build the path for this specific element # 2. Pipe the path into the HTTP tool # 3. Safely swallow any 500 errors so one bad user doesn't break the list # 4. Cache the result for this element as 'detail' alias getUserDetail?.path:"/{id}" catch null as detail
# Map the resulting HTTP response back to the array # If 'detail' is null (from the catch), we skip this user! .id <- id .email <- detail.email ?? continue .status <- detail.status }}