Skip to content

C# Database Client

The TerraScale Database Client provides a strongly-typed, high-performance SDK for C# and .NET applications.


Terminal window
dotnet add package TerraScale.Database.Client

using TerraScale.Database.Client;
using TerraScale.Database.Client.Configuration;
var options = new TerraScaleDatabaseOptions
{
ApiKey = "ts_live_your_api_key",
Endpoint = "https://api.terrascale.io",
Region = "us-east-1",
DefaultDatabase = "my-database",
Timeout = TimeSpan.FromSeconds(30),
Retry = new RetryPolicyOptions
{
MaxRetries = 3,
BaseDelay = TimeSpan.FromMilliseconds(500),
MaxDelay = TimeSpan.FromSeconds(10),
UseJitter = true
}
};
var client = new TerraScaleDatabase(options);
OptionTypeDefaultDescription
ApiKeystringRequiredYour API key
EndpointstringRequiredAPI endpoint URL
Regionstring"us-east-1"Default region
DefaultDatabasestringnullDefault database ID
TimeoutTimeSpan30 secondsHTTP request timeout
Retry.MaxRetriesint3Max retry attempts
Retry.BaseDelayTimeSpan500msInitial retry delay
Retry.MaxDelayTimeSpan10 secondsMaximum retry delay
Retry.UseJitterbooltrueAdd randomness to delays
var client = TerraScaleDatabase.Create(options => options
.WithApiKey("ts_live_your_api_key")
.WithEndpoint("https://api.terrascale.io")
.WithDefaultDatabase("my-database")
.WithTimeout(TimeSpan.FromSeconds(60))
.WithRetry(retry => retry
.MaxRetries(5)
.BaseDelay(TimeSpan.FromSeconds(1))
)
);

var item = new DatabaseItem
{
PartitionKey = "user#123",
SortKey = "profile",
Attributes = new Dictionary<string, object?>
{
["name"] = "John Doe",
["email"] = "john@example.com",
["age"] = 30,
["tags"] = new[] { "premium", "verified" }
}
};
var result = await client.PutItemAsync(item);
if (result.IsSuccess)
{
Console.WriteLine("Item saved!");
}
else
{
Console.WriteLine($"Error: {result.Errors.First().Message}");
}
// Get by partition key only
var result = await client.GetItemAsync("user#123");
// Get by partition key and sort key
var result = await client.GetItemAsync("user#123", "profile");
if (result.IsSuccess)
{
var item = result.Value;
var name = item.GetAttribute<string>("name");
var age = item.GetAttribute<int>("age", defaultValue: 0);
Console.WriteLine($"User: \{name\}, Age: {age}");
}
var result = await client.DeleteItemAsync("user#123", "profile");
if (result.IsSuccess)
{
Console.WriteLine("Item deleted!");
}

var filter = new QueryFilter
{
PartitionKey = "user#123",
SortKeyCondition = SortKeyCondition.BeginsWith("order#")
};
var options = new QueryOptions
{
Limit = 50,
ScanForward = false // Descending order
};
var result = await client.QueryAsync(filter, options);
if (result.IsSuccess)
{
foreach (var item in result.Value.Items)
{
Console.WriteLine($"Order: {item.SortKey}");
}
// Handle pagination
if (result.Value.HasMore)
{
var nextPage = await client.QueryAsync(filter, new QueryOptions
{
NextToken = result.Value.NextToken
});
}
}
// Exact match
SortKeyCondition.Equal("profile")
// Prefix match
SortKeyCondition.BeginsWith("order#")
// Range (inclusive)
SortKeyCondition.Between("2024-01-01", "2024-12-31")
// Comparison
SortKeyCondition.LessThan("2024-06-01")
SortKeyCondition.GreaterThan("2024-01-01")
SortKeyCondition.LessThanOrEquals("2024-06-01")
SortKeyCondition.GreaterThanOrEquals("2024-01-01")
var options = new PaginationOptions
{
Limit = 100
};
var result = await client.ScanAsync(options);
if (result.IsSuccess)
{
Console.WriteLine($"Found {result.Value.Items.Count} items");
}

var writeItems = new List<BatchWriteItem>
{
new()
{
Operation = BatchOperation.Put,
PartitionKey = "user#123",
SortKey = "profile",
Data = new Dictionary<string, object?> { ["name"] = "John" }
},
new()
{
Operation = BatchOperation.Delete,
PartitionKey = "user#789",
SortKey = "profile"
}
};
var result = await client.BatchWriteAsync(writeItems);
Console.WriteLine($"Success: {result.Value.SuccessCount}");
var keys = new List<ItemKey>
{
new("user#123", "profile"),
new("user#456", "profile")
};
var result = await client.BatchGetAsync(keys);
foreach (var item in result.Value.Items)
{
Console.WriteLine($"{item.PartitionKey}: {item.GetAttribute<string>("name")}");
}

var writeItems = new List<TransactWriteItem>
{
new()
{
Action = TransactAction.Put,
PartitionKey = "account#A",
SortKey = "balance",
Data = new Dictionary<string, object?> { ["amount"] = 900 }
},
new()
{
Action = TransactAction.Put,
PartitionKey = "account#B",
SortKey = "balance",
Data = new Dictionary<string, object?> { ["amount"] = 1100 }
}
};
var result = await client.TransactWriteAsync(writeItems, "transfer-12345");
if (result.IsSuccess)
{
Console.WriteLine("Transaction completed!");
}
else
{
Console.WriteLine("Transaction failed - rolled back");
}
var getItems = new List<TransactGetItem>
{
new() { PartitionKey = "user#123", SortKey = "profile" },
new() { PartitionKey = "user#123", SortKey = "settings" }
};
var result = await client.TransactGetAsync(getItems);
foreach (var item in result.Value.Items)
{
if (item.ItemExists)
{
Console.WriteLine($"{item.PartitionKey}: found");
}
}

For typed entities, use the repository pattern:

// Define your entity
public record Customer : EntityBase
{
public required string Name { get; init; }
public required string Email { get; init; }
public CustomerTier Tier { get; init; } = CustomerTier.Standard;
}
// Get repository
var customers = client.GetRepository<Customer>("repo_customers");
// Create
var customer = new Customer
{
Id = Guid.NewGuid().ToString(),
Name = "Acme Corp",
Email = "contact@acme.com"
};
await customers.CreateAsync(customer);
// Read
var result = await customers.GetAsync(customer.Id);
// Update
var updated = customer with { Tier = CustomerTier.Premium };
await customers.UpdateAsync(updated);
// Delete
await customers.DeleteAsync(customer.Id);
// List
var list = await customers.ListAsync(new PaginationOptions { Limit = 50 });
// Check exists
var exists = await customers.ExistsAsync(customer.Id);

See Repository Pattern Guide for more details.


All operations return Result<T> objects:

var result = await client.GetItemAsync("user#123");
if (result.IsSuccess)
{
var item = result.Value;
// Process item
}
else if (result.IsFailed)
{
foreach (var error in result.Errors)
{
Console.WriteLine($"Error: {error.Message}");
}
}
// Throw on failure
var item = result.ValueOrThrow();
// Provide default
var item = result.ValueOr(defaultItem);
// Match pattern
result.Match(
onSuccess: item => Console.WriteLine($"Found: {item.PartitionKey}"),
onFailure: errors => Console.WriteLine($"Failed: {errors.First().Message}")
);

Always dispose the client when done:

await client.DisposeAsync();
// Or use 'using' statement
await using var client = new TerraScaleDatabase(options);