Skip to main content
The Infrahub Python SDK provides powerful methods to query and retrieve infrastructure data.

Basic Queries

Get a Single Object by ID

Retrieve an object using its unique identifier:
from infrahub_sdk import InfrahubClient

client = InfrahubClient()

device = await client.get(
    kind="InfraDevice",
    id="abc-123-def-456"
)

print(f"Device: {device.name.value}")
print(f"Serial: {device.serial_number.value}")

Get All Objects of a Kind

Retrieve all objects of a specific type:
devices = await client.all(kind="InfraDevice")

print(f"Total devices: {len(devices)}")
for device in devices:
    print(f"  - {device.name.value}")

Get with Specific Attributes

Retrieve an object with specific attribute values:
# Get by name (if unique)
device = await client.get(
    kind="InfraDevice",
    name__value="router-01"  # Use attribute filters
)

Including Relationships

Fetch related data in a single query:
device = await client.get(
    kind="InfraDevice",
    id="device-id",
    include=["location", "tags"]  # Include relationships
)

# Access related data
print(f"Location: {device.location.name.value}")
print(f"Tags: {[tag.name.value for tag in device.tags]}")

Include Nested Relationships

device = await client.get(
    kind="InfraDevice",
    id="device-id",
    include=[
        "location",
        "location__parent",  # Nested relationship
        "tags"
    ]
)

print(f"Location: {device.location.name.value}")
if device.location.parent:
    print(f"Parent Location: {device.location.parent.name.value}")

Include All Relationships

# Include all direct relationships
device = await client.get(
    kind="InfraDevice",
    id="device-id",
    include="all"
)

Property Mode

Fetch with Property Metadata

Retrieve additional property information:
device = await client.get(
    kind="InfraDevice",
    id="device-id",
    property=True  # Include property metadata
)

# Access property metadata
print(f"Name: {device.name.value}")
print(f"Name updated at: {device.name.updated_at}")
print(f"Name is from profile: {device.name.is_from_profile}")
print(f"Name source: {device.name.source}")
Property mode includes metadata like:
  • updated_at: When the attribute was last modified
  • is_from_profile: Whether the value comes from a profile
  • source: The source of the attribute value
  • owner: Who owns/set the value

Filtering Results

Filter by Attribute Values

While the SDK doesn’t provide built-in filtering on all(), you can filter in Python:
all_devices = await client.all(kind="InfraDevice")

# Filter by attribute
routers = [
    device for device in all_devices
    if device.device_type.value == "router"
]

# Filter by name pattern
prod_devices = [
    device for device in all_devices
    if "prod" in device.name.value.lower()
]

Custom GraphQL Queries

For complex filtering, use custom GraphQL queries:
query = """
query GetDevicesByType($device_type: String!) {
  InfraDevice(device_type__value: $device_type) {
    edges {
      node {
        id
        name {
          value
        }
        serial_number {
          value
        }
      }
    }
  }
}
"""

variables = {"device_type": "router"}
result = await client.execute_graphql(
    query=query,
    variables=variables
)

devices = result["InfraDevice"]["edges"]
for device in devices:
    print(device["node"]["name"]["value"])

Accessing Attributes

Basic Attribute Access

Attributes are accessed with .value:
device = await client.get(kind="InfraDevice", id="device-id")

# Read attribute values
name = device.name.value
serial = device.serial_number.value
height = device.height.value  # Integer/Number
is_active = device.is_active.value  # Boolean

Attribute Types

Different attribute kinds:
# Text
device.name.value  # str

# Number
device.height.value  # int

# Boolean
device.is_active.value  # bool

# DateTime
device.commissioned_at.value  # datetime object

# JSON
device.metadata.value  # dict or list

# IPAddress
device.ip_address.value  # str (IP address)

Accessing Relationships

Single Cardinality Relationships

Relationships with cardinality “one”:
device = await client.get(
    kind="InfraDevice",
    id="device-id",
    include=["location"]
)

# Access the related object's ID
location_id = device.location.id

# Access the related object's attributes (if included)
location_name = device.location.name.value

Multiple Cardinality Relationships

Relationships with cardinality “many”:
device = await client.get(
    kind="InfraDevice",
    id="device-id",
    include=["tags"]
)

# Iterate over related objects
for tag in device.tags:
    print(f"Tag: {tag.name.value}")

# Get list of IDs
tag_ids = device.tags.peer_ids  # List of UUIDs

# Count related objects
tag_count = len(device.tags)

Relationship Metadata

device = await client.get(
    kind="InfraDevice",
    id="device-id",
    include=["location"],
    property=True
)

# Access relationship properties
print(f"Location ID: {device.location.id}")
print(f"Location updated: {device.location.updated_at}")
print(f"From profile: {device.location.is_from_profile}")

Querying on Branches

Query on a Specific Branch

# Query on main branch (default)
device = await client.get(
    kind="InfraDevice",
    id="device-id"
)

# Query on a different branch
device_on_branch = await client.get(
    kind="InfraDevice",
    id="device-id",
    branch="feature-branch"
)

Compare Across Branches

# Get object on main branch
main_device = await client.get(
    kind="InfraDevice",
    id="device-id",
    branch="main"
)

# Get same object on feature branch
feature_device = await client.get(
    kind="InfraDevice",
    id="device-id",
    branch="feature-update"
)

# Compare values
if main_device.name.value != feature_device.name.value:
    print(f"Name changed: {main_device.name.value} -> {feature_device.name.value}")

Handling Missing Data

Check for None Values

device = await client.get(kind="InfraDevice", id="device-id")

# Optional attributes may be None
if device.description.value is None:
    print("No description set")
else:
    print(f"Description: {device.description.value}")

Handle Missing Relationships

device = await client.get(
    kind="InfraDevice",
    id="device-id",
    include=["location"]
)

# Check if relationship exists
if device.location:
    print(f"Location: {device.location.name.value}")
else:
    print("No location assigned")

Try-Except for Not Found

from infrahub_sdk.exceptions import NodeNotFoundError

try:
    device = await client.get(
        kind="InfraDevice",
        id="nonexistent-id"
    )
except NodeNotFoundError:
    print("Device not found")

Querying Schema

Fetch Schema Information

# Fetch schema for specific namespaces
await client.schema.fetch(namespaces=["Infra", "Network"])

# Get schema for a specific kind
device_schema = await client.schema.get(
    kind="InfraDevice",
    branch="main"
)

print(f"Schema name: {device_schema.name}")
print(f"Namespace: {device_schema.namespace}")
print(f"Attributes: {[attr.name for attr in device_schema.attributes]}")

Check Schema Sync Status

# Wait for schema to converge
await client.schema.wait_until_converged(branch="main")

# Check if schema is in sync
is_synced = await client.schema.in_sync()
if is_synced:
    print("Schema is synchronized")
else:
    print("Schema update in progress")

Performance Optimization

Batch Queries

Query multiple objects efficiently:
device_ids = ["id-1", "id-2", "id-3"]

# Query all devices
devices = []
for device_id in device_ids:
    device = await client.get(kind="InfraDevice", id=device_id)
    devices.append(device)

Limit Included Relationships

Only include what you need:
# Include only required relationships
device = await client.get(
    kind="InfraDevice",
    id="device-id",
    include=["location"]  # Don't include unnecessary relationships
)

Use Pagination

For large datasets, use pagination (see Pagination guide):
# Instead of loading all objects
# all_devices = await client.all(kind="InfraDevice")  # May be slow

# Use pagination for large datasets
offset = 0
limit = 100

while True:
    batch = await client.execute_graphql(
        query="""query GetDevices($offset: Int!, $limit: Int!) {
          InfraDevice(offset: $offset, limit: $limit) {
            edges { node { id name { value } } }
          }
        }""",
        variables={"offset": offset, "limit": limit}
    )
    
    devices = batch["InfraDevice"]["edges"]
    if not devices:
        break
    
    for device in devices:
        print(device["node"]["name"]["value"])
    
    offset += limit

Common Query Patterns

Get Object with Full Context

device = await client.get(
    kind="InfraDevice",
    id="device-id",
    include=["location", "tags", "manufacturer"],
    property=True
)

Search by Attribute Value

all_devices = await client.all(kind="InfraDevice")
target_device = next(
    (d for d in all_devices if d.serial_number.value == "SN12345"),
    None
)

if target_device:
    print(f"Found: {target_device.name.value}")

Count Objects

devices = await client.all(kind="InfraDevice")
total = len(devices)

active = sum(1 for d in devices if d.is_active.value)
inactive = total - active

print(f"Total: {total}, Active: {active}, Inactive: {inactive}")

Next Steps

Creating Objects

Learn how to create new infrastructure objects

Updating Objects

Update existing objects and attributes

Relationships

Work with object relationships

Pagination

Handle large datasets with pagination

Build docs developers (and LLMs) love