Skip to main content
Mutations in the Infrahub SDK allow you to create, update, and delete objects in your infrastructure data model.

Creating Objects

Basic Create

Create a new object:
from infrahub_sdk import InfrahubClient

client = InfrahubClient()

# Create the object
device = await client.create(
    kind="InfraDevice",
    name="router-01",
    description="Edge router in NYC",
    status="active"
)

# Save to Infrahub
await device.save()

print(f"Created device with ID: {device.id}")
Remember to call save() after creating an object. The create() method only initializes the object locally.

Create with Relationships

Create objects with related entities:
# Get related objects first
site = await client.get(kind="InfraSite", name__value="NYC")
role = await client.get(kind="BuiltinRole", name__value="edge")

# Create device with relationships
device = await client.create(
    kind="InfraDevice",
    name="router-02",
    site=site,
    role=role,
    status="active"
)

await device.save()

Create in a Branch

Create objects in a specific branch:
device = await client.create(
    branch="feature-new-devices",
    kind="InfraDevice",
    name="router-03",
    status="active"
)

await device.save()

Create with Protected Values

Mark attribute values as protected:
device = await client.create(
    kind="InfraDevice",
    name={
        "value": "router-04",
        "is_protected": True  # Protect from changes
    },
    status="active"
)

await device.save()

Create with Source Tracking

Track the source of attribute values:
# Get source account
account = await client.get(kind="CoreAccount", name="admin")

device = await client.create(
    kind="InfraDevice",
    name={
        "value": "router-05",
        "source": account.id  # Track who set this value
    },
    status="active"
)

await device.save()

Updating Objects

Basic Update

Update an existing object:
# Get the object
device = await client.get(
    kind="InfraDevice",
    name__value="router-01"
)

if device:
    # Update attributes
    device.description.value = "Updated description"
    device.status.value = "maintenance"
    
    # Save changes
    await device.save()
    print(f"Updated {device.name.value}")

Update Multiple Attributes

Update several attributes at once:
device = await client.get(kind="InfraDevice", id="abc-123")

if device:
    device.name.value = "router-01-updated"
    device.description.value = "Updated router"
    device.status.value = "active"
    
    await device.save()

Update Relationships

Change relationships to other objects:
device = await client.get(kind="InfraDevice", id="abc-123")

# Get new related objects
new_site = await client.get(kind="InfraSite", name__value="LAX")
new_role = await client.get(kind="BuiltinRole", name__value="core")

if device:
    # Update relationships
    device.site.peer = new_site
    device.role.peer = new_role
    
    await device.save()

Update in a Branch

Make updates in a specific branch:
device = await client.get(
    branch="feature-updates",
    kind="InfraDevice",
    id="abc-123"
)

if device:
    device.status.value = "maintenance"
    await device.save()

Protect Attributes

Protect attribute values from changes:
device = await client.get(kind="InfraDevice", id="abc-123")

if device:
    device.name.value = "router-01-prod"
    device.name.is_protected = True  # Protect from future changes
    
    await device.save()

Deleting Objects

Basic Delete

Delete an object:
# Get the object
device = await client.get(
    kind="InfraDevice",
    name__value="router-test"
)

if device:
    # Delete it
    await client.delete(node=device)
    print(f"Deleted {device.name.value}")

Delete by ID

Delete using object ID:
device = await client.get(kind="InfraDevice", id="abc-123")
if device:
    await client.delete(node=device)

Delete in a Branch

Delete objects in a specific branch:
device = await client.get(
    branch="feature-cleanup",
    kind="InfraDevice",
    id="abc-123"
)

if device:
    await client.delete(node=device)

Working with Many Relationships

Add to Many Relationship

Add items to a many relationship:
device = await client.get(kind="InfraDevice", id="abc-123")
tag1 = await client.get(kind="BuiltinTag", name__value="production")
tag2 = await client.get(kind="BuiltinTag", name__value="critical")

if device:
    # Add tags (many relationship)
    device.tags.peers = [tag1, tag2]
    await device.save()

Update Many Relationship

Replace all items in a many relationship:
device = await client.get(kind="InfraDevice", id="abc-123")

# Get new tags
new_tags = await client.filters(
    kind="BuiltinTag",
    name__value__in=["production", "network", "edge"]
)

if device:
    # Replace all tags
    device.tags.peers = new_tags
    await device.save()

Remove from Many Relationship

Remove specific items:
device = await client.get(kind="InfraDevice", id="abc-123")

if device:
    # Get current tags
    current_tags = await device.tags.fetch()
    
    # Filter out tags to remove
    new_tags = [tag for tag in current_tags if tag.name.value != "old-tag"]
    
    # Update
    device.tags.peers = new_tags
    await device.save()

GraphQL Mutations

Custom Mutations

Build custom GraphQL mutations:
from infrahub_sdk.graphql import Mutation

# Define a custom mutation
mutation = Mutation(
    mutation={
        "InfraDeviceCreate": {
            "__args": {
                "data": {
                    "name": {"value": "router-06"},
                    "status": {"value": "active"}
                }
            },
            "object": {
                "id": None,
                "name": {"value": None}
            }
        }
    }
)

# Execute the mutation
result = await client.execute_graphql(query=mutation.render_query())

Upsert Pattern

Create or update based on existence:
async def upsert_device(name: str, **attributes):
    client = InfrahubClient()
    
    # Try to get existing
    device = await client.get(
        kind="InfraDevice",
        name__value=name
    )
    
    if device:
        # Update existing
        for attr, value in attributes.items():
            setattr(getattr(device, attr), 'value', value)
    else:
        # Create new
        device = await client.create(
            kind="InfraDevice",
            name=name,
            **attributes
        )
    
    await device.save()
    return device

# Usage
device = await upsert_device(
    name="router-01",
    status="active",
    description="Updated or created"
)

Bulk Operations

Create Multiple Objects

Create many objects efficiently:
# Create a batch
batch = await client.create_batch()

# Add multiple creates
for i in range(10):
    device = await client.create(
        kind="InfraDevice",
        name=f"router-{i:02d}",
        status="active"
    )
    batch.add(task=device.save, node=device)

# Execute all at once
async for node, result in batch.execute():
    print(f"Created: {node.name.value}")
See Batch Operations for more details.

Update Multiple Objects

Update many objects in bulk:
# Get devices to update
devices = await client.filters(
    kind="InfraDevice",
    status__value="provisioning"
)

# Create batch
batch = await client.create_batch()

# Update each
for device in devices:
    device.status.value = "active"
    batch.add(task=device.save, node=device)

# Execute
async for node, result in batch.execute():
    print(f"Updated: {node.name.value}")

Delete Multiple Objects

Delete many objects:
# Get objects to delete
old_devices = await client.filters(
    kind="InfraDevice",
    status__value="obsolete"
)

# Delete each
for device in old_devices:
    await client.delete(node=device)
    print(f"Deleted: {device.name.value}")

Validation

Handle Validation Errors

Catch validation errors during save:
from infrahub_sdk.exceptions import GraphQLError

try:
    device = await client.create(
        kind="InfraDevice",
        name="",  # Invalid: empty name
        status="active"
    )
    await device.save()
except GraphQLError as e:
    print(f"Validation error: {e.message}")
    # Handle the error

Uniqueness Constraints

Handle uniqueness constraint violations:
try:
    device = await client.create(
        kind="InfraDevice",
        name="router-01",  # Name already exists
        status="active"
    )
    await device.save()
except GraphQLError as e:
    if "unique" in e.message.lower():
        print("Device with this name already exists")
    else:
        raise

Transaction Patterns

Rollback on Failure

Use branches for transaction-like behavior:
async def safe_multi_create():
    client = InfrahubClient()
    
    # Create a temporary branch
    branch = await client.branch.create(
        branch_name="temp-transaction",
        description="Temporary transaction branch"
    )
    
    try:
        # Create objects in the branch
        device1 = await client.create(
            branch="temp-transaction",
            kind="InfraDevice",
            name="router-01"
        )
        await device1.save()
        
        device2 = await client.create(
            branch="temp-transaction",
            kind="InfraDevice",
            name="router-02"
        )
        await device2.save()
        
        # If successful, merge to main
        await client.branch.merge(branch_name="temp-transaction")
        
    except Exception as e:
        # On error, delete the branch (rollback)
        await client.branch.delete(branch_name="temp-transaction")
        raise e

Examples

Create Site and Devices

async def setup_site(site_name: str, device_count: int):
    client = InfrahubClient()
    
    # Create site
    site = await client.create(
        kind="InfraSite",
        name=site_name,
        description=f"{site_name} datacenter"
    )
    await site.save()
    print(f"Created site: {site.name.value}")
    
    # Create devices
    batch = await client.create_batch()
    for i in range(1, device_count + 1):
        device = await client.create(
            kind="InfraDevice",
            name=f"{site_name.lower()}-router-{i:02d}",
            site=site,
            status="active"
        )
        batch.add(task=device.save, node=device)
    
    async for node, result in batch.execute():
        print(f"Created device: {node.name.value}")

# Usage
await setup_site("NYC", 5)

Update Device Status

async def set_maintenance_mode(device_name: str):
    client = InfrahubClient()
    
    device = await client.get(
        kind="InfraDevice",
        name__value=device_name
    )
    
    if device:
        # Update status
        device.status.value = "maintenance"
        
        # Add maintenance tag
        maint_tag = await client.get(
            kind="BuiltinTag",
            name__value="maintenance"
        )
        
        if maint_tag:
            current_tags = await device.tags.fetch()
            device.tags.peers = current_tags + [maint_tag]
        
        await device.save()
        print(f"Set {device_name} to maintenance mode")

# Usage
await set_maintenance_mode("router-01")

Decommission Device

async def decommission_device(device_id: str):
    client = InfrahubClient()
    
    device = await client.get(kind="InfraDevice", id=device_id)
    
    if device:
        # Update status
        device.status.value = "obsolete"
        await device.save()
        
        # Wait for confirmation
        print(f"Device {device.name.value} marked as obsolete")
        
        # Delete after marking obsolete
        await client.delete(node=device)
        print(f"Deleted device {device.name.value}")

# Usage
await decommission_device("abc-123")

Error Handling

Common Patterns

from infrahub_sdk.exceptions import (
    GraphQLError,
    ValidationError,
    Error
)

async def safe_create_device(name: str, **kwargs):
    client = InfrahubClient()
    
    try:
        device = await client.create(
            kind="InfraDevice",
            name=name,
            **kwargs
        )
        await device.save()
        return device
        
    except GraphQLError as e:
        if "unique" in e.message.lower():
            print(f"Device {name} already exists")
            # Return existing device
            return await client.get(
                kind="InfraDevice",
                name__value=name
            )
        else:
            print(f"GraphQL error: {e.message}")
            raise
            
    except Error as e:
        print(f"SDK error: {e.message}")
        raise

Next Steps

Batch Operations

Learn efficient bulk operations

Queries

Query data from Infrahub

Build docs developers (and LLMs) love