This guide walks you through creating your first application using the Infrahub Python SDK.
Prerequisites
Install Python 3.12+
Ensure you have Python 3.12 or higher installed:
Install the SDK
Install the Infrahub SDK:
Verify Infrahub is Running
Ensure your Infrahub instance is accessible: curl http://localhost:8000/api/
Your First Script
Create a file called quickstart.py:
import asyncio
from infrahub_sdk import InfrahubClient
async def main ():
# Initialize the client
client = InfrahubClient()
# Create a tag
tag = await client.create(
kind = "BuiltinTag" ,
name = "production" ,
description = "Production environment"
)
await tag.save()
print ( f "Created tag: { tag.name.value } (ID: { tag.id } )" )
# Query the tag back
retrieved_tag = await client.get(
kind = "BuiltinTag" ,
id = tag.id
)
print ( f "Retrieved tag: { retrieved_tag.name.value } " )
# Update the tag
retrieved_tag.description.value = "Production environment - updated"
await retrieved_tag.save()
print ( f "Updated description: { retrieved_tag.description.value } " )
# Query all tags
all_tags = await client.all( kind = "BuiltinTag" )
print ( f "Total tags: { len (all_tags) } " )
for t in all_tags:
print ( f " - { t.name.value } " )
if __name__ == "__main__" :
asyncio.run(main())
Run the script:
The SDK is async-first , so all I/O operations use async/await. Always run your code with asyncio.run().
Understanding the Code
1. Import and Initialize
from infrahub_sdk import InfrahubClient
client = InfrahubClient()
The InfrahubClient is your main interface to Infrahub. By default, it connects to http://localhost:8000.
2. Create an Object
tag = await client.create(
kind = "BuiltinTag" ,
name = "production" ,
description = "Production environment"
)
await tag.save()
create() instantiates a new object in memory
save() persists it to Infrahub via the API
The kind parameter specifies the schema type
3. Query an Object
retrieved_tag = await client.get(
kind = "BuiltinTag" ,
id = tag.id
)
Fetch an object by its kind and ID.
4. Update an Object
retrieved_tag.description.value = "Production environment - updated"
await retrieved_tag.save()
Modify attributes and call save() to persist changes.
5. Query All Objects
all_tags = await client.all( kind = "BuiltinTag" )
Retrieve all objects of a specific kind.
Working with Relationships
Create objects with relationships:
import asyncio
from infrahub_sdk import InfrahubClient
async def create_with_relationships ():
client = InfrahubClient()
# Load a schema first (example)
schema = {
"version" : "1.0" ,
"nodes" : [
{
"name" : "Device" ,
"namespace" : "Infra" ,
"attributes" : [
{ "name" : "name" , "kind" : "Text" },
{ "name" : "serial_number" , "kind" : "Text" }
],
"relationships" : [
{
"name" : "location" ,
"peer" : "InfraLocation" ,
"kind" : "Attribute" ,
"cardinality" : "one"
}
]
},
{
"name" : "Location" ,
"namespace" : "Infra" ,
"attributes" : [
{ "name" : "name" , "kind" : "Text" },
{ "name" : "address" , "kind" : "Text" , "optional" : True }
]
}
]
}
await client.schema.load( schemas = [schema])
# Create a location
location = await client.create(
kind = "InfraLocation" ,
name = "Datacenter-1" ,
address = "123 Main St"
)
await location.save()
# Create a device with a relationship to the location
device = await client.create(
kind = "InfraDevice" ,
name = "router-01" ,
serial_number = "SN123456" ,
location = location # Pass the related object
)
await device.save()
print ( f "Created device ' { device.name.value } ' at location ' { location.name.value } '" )
# Query back with relationships
device_with_location = await client.get(
kind = "InfraDevice" ,
id = device.id,
include = [ "location" ] # Include related data
)
print ( f "Device location: { device_with_location.location.name.value } " )
if __name__ == "__main__" :
asyncio.run(create_with_relationships())
Working with Branches
Infrahub supports Git-like branching:
import asyncio
from infrahub_sdk import InfrahubClient
async def branch_workflow ():
client = InfrahubClient()
# Create a new branch
branch = await client.branch.create( branch_name = "feature-update" )
print ( f "Created branch: { branch.name } " )
# Work on the branch
tag = await client.create(
kind = "BuiltinTag" ,
name = "staging" ,
branch = branch.name # Specify the branch
)
await tag.save()
print ( f "Created tag on branch ' { branch.name } '" )
# Query on the branch
tags_on_branch = await client.all(
kind = "BuiltinTag" ,
branch = branch.name
)
print ( f "Tags on branch: { len (tags_on_branch) } " )
if __name__ == "__main__" :
asyncio.run(branch_workflow())
Error Handling
Handle errors gracefully:
import asyncio
from infrahub_sdk import InfrahubClient
from infrahub_sdk.exceptions import GraphQLError, NodeNotFoundError
async def error_handling_example ():
client = InfrahubClient()
try :
# Try to get a non-existent object
tag = await client.get( kind = "BuiltinTag" , id = "invalid-id" )
except NodeNotFoundError:
print ( "Tag not found" )
except GraphQLError as e:
print ( f "GraphQL error: { e.message } " )
except Exception as e:
print ( f "Unexpected error: { e } " )
if __name__ == "__main__" :
asyncio.run(error_handling_example())
Configuration
Configure the client for different environments:
from infrahub_sdk import Config, InfrahubClient
# Production configuration
config = Config(
address = "https://infrahub.example.com" ,
api_token = "your-api-token" ,
timeout = 60
)
client = InfrahubClient( config = config)
Or use environment variables:
export INFRAHUB_ADDRESS = "https://infrahub.example.com"
export INFRAHUB_API_TOKEN = "your-api-token"
from infrahub_sdk import InfrahubClient
# Automatically uses environment variables
client = InfrahubClient()
Common Patterns
Bulk Creation
devices = []
for i in range ( 10 ):
device = await client.create(
kind = "InfraDevice" ,
name = f "device- { i :02d} " ,
serial_number = f "SN { i :06d} "
)
await device.save()
devices.append(device)
print ( f "Created { len (devices) } devices" )
Filtering and Searching
# Get all devices with specific properties
all_devices = await client.all( kind = "InfraDevice" )
# Filter in Python
production_devices = [
d for d in all_devices
if "prod" in d.name.value.lower()
]
Accessing Nested Relationships
device = await client.get(
kind = "InfraDevice" ,
id = "device-id" ,
include = [ "location" , "tags" ]
)
print ( f "Device: { device.name.value } " )
print ( f "Location: { device.location.name.value } " )
print ( f "Tags: { [tag.name.value for tag in device.tags] } " )
Next Steps
Client Setup Learn advanced client configuration
Querying Data Master data querying techniques
Creating Objects Deep dive into object creation
Error Handling Handle errors and exceptions properly