Writing Data#

Learn to create, update, and delete entities in B-Fabric.

Note

For quick one-off operations, consider using bfabric-cli API operations.

Understanding the Write API#

bfabricPy’s write operations are thin wrappers around B-Fabric’s SOAP webservices. This means:

  • No local validation: bfabricPy passes your data directly to B-Fabric

  • Field requirements: You need to know what fields B-Fabric expects for each entity type

  • Error handling: Validation errors come from B-Fabric, not bfabricPy

  • Direct passthrough: The API forwards requests with minimal processing

To write to B-Fabric effectively, you’ll need some familiarity with:

  • B-Fabric entity types and their field structures

  • Which fields are required vs optional

  • Relationship fields (e.g., containerid, applicationid)

Use the CLI to discover field requirements:

# Inspect endpoint structure
bfabric-cli api inspect sample

# Inspect specific method
bfabric-cli api inspect sample save

# Read existing records to see field examples
bfabric-cli api read sample --limit 1 --format json

Configuration for Testing#

Set up a TEST environment in ~/.bfabricpy.yml, if you don’t already have one. See Configuration Guide for details.

# Customize for your test system
TEST_PROJECT_ID = 3000

# Always connect to test instance
from bfabric import Bfabric

client = Bfabric.connect(config_file_env="TEST")

Core Operations#

Create#

Create new entities using client.save() without an id field:

new_sample = {
    "name": "TEST_MySample",
    "containerid": TEST_PROJECT_ID,
    "type": "Biological Sample - Generic",
    "description": "A test sample",
}
result = client.save(endpoint="sample", obj=new_sample)
sample_id = result[0]["id"]
print(f"Created: {sample_id}")

Tips:

  • Include all required fields for the entity type

  • Omit the id field - B-Fabric will assign one

  • The ResultContainer returns the created object with its new ID

Update#

Updates require the id field and only the fields you want to change:

result = client.save(
    endpoint="sample",
    obj={
        "id": sample_id,  # Required for updates
        "description": "Updated description",
    },
)
print("Updated")

Important warnings:

  • List fields: List-valued fields (e.g., tags, custom attributes) will be replaced entirely, not merged

  • Relationships: Be careful when modifying relationship fields like containerid or applicationid

  • Log entries: Updates create log entries visible in B-Fabric’s “log” tab

Delete#

Delete entities by ID:

result = client.delete(endpoint="sample", id=sample_id)
print("Deleted")

Warning: Deletion is permanent. Consider using TEST environment first.

Advanced Operations#

Batch Operations#

Save multiple objects in a single API call for efficiency:

# Create multiple samples
objects = [
    {
        "name": "Sample1",
        "containerid": TEST_PROJECT_ID,
        "type": "Biological Sample - Generic",
    },
    {
        "name": "Sample2",
        "containerid": TEST_PROJECT_ID,
        "type": "Biological Sample - Generic",
    },
    {
        "name": "Sample3",
        "containerid": TEST_PROJECT_ID,
        "type": "Biological Sample - Generic",
    },
]
result = client.save(endpoint="sample", obj=objects)

# Process all results
for saved_object in result:
    print(f"Created: {saved_object['id']} - {saved_object['name']}")

The method Parameter#

Most endpoints use method="save" (the default), but some require specific methods:

# Default method (works for most endpoints)
result = client.save(endpoint="sample", obj={"name": "Test"})

# Explicit method
result = client.save(endpoint="sample", obj={"name": "Test"}, method="save")

# Some endpoints may require different methods
result = client.save(endpoint="special_endpoint", obj=data, method="update")

Check the B-Fabric documentation or use bfabric-cli api inspect to determine if a specific method is required.

Checking for Existence#

Avoid creating duplicates by checking existence first:

# Check if sample exists with given name
if not client.exists(endpoint="sample", key="name", value="TEST_MySample"):
    # Only create if doesn't exist
    result = client.save(
        endpoint="sample",
        obj={"name": "TEST_MySample", "containerid": TEST_PROJECT_ID},
    )
    sample_id = result[0]["id"]
    print(f"Created new sample: {sample_id}")
else:
    print("Sample already exists, skipping creation")

Use additional query conditions for more specific checks:

# Check for sample with specific name in specific project
if not client.exists(
    endpoint="sample",
    key="name",
    value="TEST_MySample",
    query={"containerid": TEST_PROJECT_ID},
):
    # Safe to create
    pass

Deleting Multiple Entities#

Delete multiple entities in a single call:

# Delete by list of IDs
result = client.delete(endpoint="sample", id=[1001, 1002, 1003])
print(f"Deleted {len(result)} samples")

Common Writable Endpoints#

Endpoint

Description

Common Fields

sample

Biological samples

name, containerid, type, description

project

Projects

name, description

order

Orders

name, description

container

Projects and orders

name, description

workunit

Workunits

name, applicationid, containerid, status

resource

Resources

name, workunitid, base64 (content), relativepath

importresource

Import resources

name, containerid, relativepath, filechecksum, size

workflow

Workflows

containerid, workflowtemplateid

workflowstep

Workflow steps

workflowid, workflowtemplatestepid, workunitid

Use bfabric-cli api inspect <endpoint> to see complete field lists.

Response Handling#

Successful Operations#

result = client.save(endpoint="sample", obj={"name": "Test", "containerid": 123})

# ResultContainer with saved objects
for saved_object in result:
    print(f"ID: {saved_object['id']}")
    print(f"Name: {saved_object['name']}")
    print(f"Created: {saved_object['created']}")

# Access as list
objects = result.to_list_dict()

Handling Errors#

# Manual error handling
result = client.save(endpoint="sample", obj={"name": "Test"}, check=False)

if result.is_success:
    print("Saved successfully")
    print(f"ID: {result[0]['id']}")
else:
    print("Save failed:")
    for error in result.errors:
        print(f"  - {error.message}")

See Error Handling for comprehensive error handling patterns.

Real-World Patterns#

Pattern 1: Safe Create-or-Update#

def create_or_update_sample(client, name, containerid, **attrs):
    """Create sample if it doesn't exist, otherwise update it."""
    # Try to find existing sample
    result = client.read(
        endpoint="sample",
        obj={"name": name, "containerid": containerid},
        max_results=1,
    )

    if len(result) > 0:
        # Update existing
        obj = {"id": result[0]["id"], **attrs}
        return client.save(endpoint="sample", obj=obj)
    else:
        # Create new
        obj = {"name": name, "containerid": containerid, **attrs}
        return client.save(endpoint="sample", obj=obj)

Error Handling#

See Error Handling for comprehensive error handling patterns and ResultContainer API documentation.