JSONPath Reference

JSONPath is the notation used to navigate and select data within JSON structures. TinySystems uses JSONPath expressions to access data in your flows.

Root Reference

The $ symbol always refers to the root of the data:

// Data
{
  "name": "Alice",
  "age": 30
}
# Expressions
root: {{$}}              # The entire object
name: "{{$.name}}"       # "Alice"
age: {{$.age}}           # 30

Property Access

Simple Properties

Access object properties with dot notation:

{
  "user": "alice",
  "status": "active"
}
user: "{{$.user}}"       # "alice"
status: "{{$.status}}"   # "active"

Nested Properties

Chain dots for nested access:

{
  "user": {
    "profile": {
      "name": "Alice",
      "email": "alice@example.com"
    }
  }
}
name: "{{$.user.profile.name}}"    # "Alice"
email: "{{$.user.profile.email}}"  # "alice@example.com"

Special Characters

Use bracket notation for properties with special characters:

{
  "first-name": "Alice",
  "last name": "Smith",
  "user.id": "12345"
}
first: "{{$['first-name']}}"   # "Alice"
last: "{{$['last name']}}"     # "Smith"
id: "{{$['user.id']}}"         # "12345"

Array Access

By Index

Arrays are zero-indexed:

{
  "items": ["apple", "banana", "cherry"]
}
first: "{{$.items[0]}}"   # "apple"
second: "{{$.items[1]}}"  # "banana"
third: "{{$.items[2]}}"   # "cherry"

Negative Indices

Count from the end with negative numbers:

last: "{{$.items[-1]}}"      # "cherry"
secondLast: "{{$.items[-2]}}"  # "banana"

Array of Objects

Access properties within array elements:

{
  "users": [
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25}
  ]
}
firstName: "{{$.users[0].name}}"   # "Alice"
firstAge: {{$.users[0].age}}       # 30
secondName: "{{$.users[1].name}}"  # "Bob"

Array Properties

Length

Get the number of elements:

{
  "items": [1, 2, 3, 4, 5]
}
count: {{$.items.length}}  # 5

Empty Check

isEmpty: {{$.items.length == 0}}
hasItems: {{$.items.length > 0}}

Complex Paths

Deep Nesting

{
  "response": {
    "data": {
      "users": [
        {
          "profile": {
            "contact": {
              "email": "user@example.com"
            }
          }
        }
      ]
    }
  }
}
email: "{{$.response.data.users[0].profile.contact.email}}"

Mixed Access

Combine dot and bracket notation:

{
  "results": [
    {"field-1": "value1"},
    {"field-1": "value2"}
  ]
}
first: "{{$.results[0]['field-1']}}"   # "value1"
second: "{{$.results[1]['field-1']}}"  # "value2"

Common Data Patterns

HTTP Request Data

{
  "method": "POST",
  "path": "/api/users",
  "headers": {
    "Content-Type": "application/json",
    "Authorization": "Bearer token123"
  },
  "body": {
    "name": "Alice",
    "email": "alice@example.com"
  },
  "queryParams": {
    "page": "1",
    "limit": "10"
  }
}
method: "{{$.method}}"                          # "POST"
contentType: "{{$.headers['Content-Type']}}"    # "application/json"
auth: "{{$.headers.Authorization}}"             # "Bearer token123"
userName: "{{$.body.name}}"                     # "Alice"
page: "{{$.queryParams.page}}"                  # "1"

Nested Arrays

{
  "matrix": [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
  ]
}
center: {{$.matrix[1][1]}}   # 5
topRight: {{$.matrix[0][2]}} # 3

Array of Nested Objects

{
  "orders": [
    {
      "id": "ord-1",
      "items": [
        {"product": "Widget", "qty": 2},
        {"product": "Gadget", "qty": 1}
      ]
    }
  ]
}
orderId: "{{$.orders[0].id}}"                    # "ord-1"
firstProduct: "{{$.orders[0].items[0].product}}" # "Widget"
firstQty: {{$.orders[0].items[0].qty}}           # 2

Dynamic Access

Variable Keys

Use bracket notation with expressions:

{
  "data": {
    "name": "Alice",
    "age": 30
  },
  "fieldToRead": "name"
}
# Access field dynamically
value: "{{$.data[$.fieldToRead]}}"  # "Alice"

Computed Indices

{
  "items": ["a", "b", "c", "d"],
  "index": 2
}
item: "{{$.items[$.index]}}"  # "c"

Edge Cases

Missing Properties

When a property doesn't exist, the expression returns undefined:

{
  "user": {
    "name": "Alice"
  }
}
# $.user.email doesn't exist
email: "{{$.user.email}}"  # undefined

# Use default value
email: "{{$.user.email || 'not set'}}"  # "not set"

Null Values

Null is different from undefined:

{
  "name": "Alice",
  "nickname": null
}
nickname: "{{$.nickname}}"              # null
hasNickname: {{$.nickname != null}}     # false
display: "{{$.nickname || $.name}}"     # "Alice"

Empty Arrays

{
  "items": []
}
first: "{{$.items[0]}}"        # undefined
count: {{$.items.length}}      # 0
hasItems: {{$.items.length > 0}}  # false

Path Validation

Check Path Exists

Before accessing deep paths, verify they exist:

# Risk of error if user or profile doesn't exist
name: "{{$.user.profile.name}}"

# Safer with defaults
name: "{{$.user != null ? $.user.profile.name : 'Unknown'}}"

# Or use default at the end
name: "{{$.user.profile.name || 'Unknown'}}"

Type Checking

Verify expected types:

# Check if array before accessing
items: "{{Array.isArray($.data) ? $.data : []}}"

# Check if object
value: "{{typeof $.data == 'object' ? $.data.value : null}}"

Performance Tips

Direct Access

Prefer direct paths over complex operations:

# ✅ Efficient - direct path
name: "{{$.users[0].name}}"

# ⚠️ Less efficient - operations on arrays
# (when you just need one element)

Cache Repeated Access

If you need the same nested value multiple times, consider restructuring your mapping:

# Instead of repeating deep paths
userName: "{{$.response.data.user.profile.name}}"
userEmail: "{{$.response.data.user.profile.email}}"

# Consider mapping the parent object once
# (in components that support it)

Next Steps