Variables and let bindings
Reqon supports variable bindings using let statements and object composition with the spread operator.
Let bindings
Create named variables to store intermediate values:
action ProcessOrder {
get "/orders/123"
// Bind response fields to variables
let orderId = response.id
let customer = response.customer
let total = response.items | sum(.price)
// Use variables in subsequent steps
post "/receipts" {
body: {
orderId: orderId,
customer: customer.email,
amount: total
}
}
}
Expression bindings
Variables can hold any expression result:
let count = length(items)
let filtered = items | filter(.active == true)
let transformed = items | map({ id: .id, name: .name })
let calculated = price * quantity * (1 - discount)
Scope
Variables are scoped to the action in which they're defined:
action FetchData {
get "/users"
let users = response
for user in users {
let userId = user.id // Scoped to this iteration
get concat("/users/", userId, "/orders")
store response -> orders { key: .id }
}
}
Spread operator
Use the spread operator (...) to compose objects:
Merging objects
map response -> EnrichedOrder {
...response, // Include all original fields
processedAt: now(), // Add new field
status: "processed" // Add/override field
}
Combining data
action EnrichCustomer {
get "/customers/123"
let customer = response
get concat("/customers/", customer.id, "/profile")
let profile = response
// Merge customer with profile data
store {
...customer,
...profile,
enrichedAt: now()
} -> enrichedCustomers { key: .id }
}
Selective spreading
Spread specific nested objects:
map order -> FlatOrder {
id: .id,
...order.metadata, // Spread metadata fields
...order.shipping, // Spread shipping fields
total: .total
}
Common patterns
Extracting and reusing values
action SyncWithPagination {
get "/items" {
paginate: cursor(cursor, 100, "meta.nextCursor"),
until: response.meta.nextCursor == null
}
// Store pagination metadata
let meta = response.meta
let items = response.data
for item in items {
store item -> items { key: .id }
}
// Log sync stats
let syncStats = {
total: meta.total,
fetched: length(items),
syncedAt: now()
}
}
Building request bodies
action CreateOrder {
let baseOrder = {
currency: "USD",
createdAt: now(),
status: "pending"
}
for item in cart.items {
let orderItem = {
...baseOrder,
...item,
orderId: generateId()
}
post "/orders" {
body: orderItem
}
}
}
Conditional field addition
action ProcessRecord {
let base = {
id: record.id,
name: record.name
}
// Add optional fields conditionally
let withEmail = if record.email != null
then { ...base, email: record.email }
else base
let final = if record.phone != null
then { ...withEmail, phone: record.phone }
else withEmail
store final -> records { key: .id }
}
Transforming collections
action TransformItems {
get "/items"
let enriched = response.items | map({
...item,
slug: slugify(.name),
tags: .categories | map(.name)
})
for item in enriched {
store item -> items { key: .id }
}
}
Variable naming
Use descriptive names that indicate the variable's purpose:
// Good
let activeUsers = users | filter(.status == "active")
let totalRevenue = orders | sum(.amount)
let customerEmail = response.customer.email
// Avoid
let x = users | filter(.status == "active")
let val = orders | sum(.amount)
let e = response.customer.email
Notes
- Variables are immutable once bound
- Variables are evaluated lazily when used
- The
responsevariable is automatically set after each fetch - Spread operator creates a shallow copy of object fields