Writing Data
Learn how to insert, update, upsert, and delete data using Ductape's database API. This guide covers single records, bulk operations, and advanced write patterns.
Insert Operations
Insert a Single Record
- TypeScript
- Java
- Go
- .NET
const result = await ductape.database.insert({
table: 'users',
data: {
name: 'John Doe',
email: 'john@example.com',
status: 'active',
created_at: new Date(),
},
returning: true, // Return the inserted record
});
console.log('Inserted ID:', result.insertedIds[0]);
console.log('Inserted data:', result.data);
Map<String, Object> result = ductape.database.insert(Map.of(
"table", "users",
data: Map.of(
"name", "John Doe",
"email", "john@example.com",
"status", "active",
created_at: Instant.now()
),
"returning", true, // Return the inserted record
));
System.out.println('Inserted "ID", ", result.insertedIds[0]);
System.out.println("Inserted data:', result.data);
result := client.database.insert({
"table": "users",
data: {
"name": "John Doe",
"email": "john@example.com",
"status": "active",
created_at: new Date(),
},
"returning": true, // Return the inserted record
});
fmt.Println('Inserted "ID": ", result.insertedIds[0]);
fmt.Println("Inserted data:', result.data);
var result = await ductape.database.insert({
["table"] = "users",
data: {
["name"] = "John Doe",
["email"] = "john@example.com",
["status"] = "active",
created_at: DateTime.UtcNow,
},
["returning"] = true, // Return the inserted record
});
Console.WriteLine('Inserted ["ID"] = ", result.insertedIds[0]);
Console.WriteLine("Inserted data:', result.data);
Insert Multiple Records
- TypeScript
- Java
- Go
- .NET
const result = await ductape.database.insert({
table: 'users',
data: [
{ name: 'User 1', email: 'user1@example.com' },
{ name: 'User 2', email: 'user2@example.com' },
{ name: 'User 3', email: 'user3@example.com' },
],
});
console.log('Inserted count:', result.count);
console.log('Inserted IDs:', result.insertedIds);
Map<String, Object> result = ductape.database.insert(Map.of(
"table", "users",
data: [
Map.of( "name", "User 1", "email", "user1@example.com" ),
Map.of( "name", "User 2", "email", "user2@example.com" ),
Map.of( "name", "User 3", "email", "user3@example.com" ),
]
));
System.out.println('Inserted "count", ", result.count);
System.out.println("Inserted IDs:', result.insertedIds);
result := client.database.insert({
"table": "users",
data: [
{ "name": "User 1", "email": "user1@example.com" },
{ "name": "User 2", "email": "user2@example.com" },
{ "name": "User 3", "email": "user3@example.com" },
],
});
fmt.Println('Inserted "count": ", result.count);
fmt.Println("Inserted IDs:', result.insertedIds);
var result = await ductape.database.insert({
["table"] = "users",
data: [
{ ["name"] = "User 1", ["email"] = "user1@example.com" },
{ ["name"] = "User 2", ["email"] = "user2@example.com" },
{ ["name"] = "User 3", ["email"] = "user3@example.com" },
],
});
Console.WriteLine('Inserted ["count"] = ", result.count);
Console.WriteLine("Inserted IDs:', result.insertedIds);
Insert with Conflict Handling (Upsert)
Handle duplicate key conflicts gracefully:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.database.insert({
table: 'users',
data: {
email: 'john@example.com',
name: 'John Doe',
status: 'active',
},
onConflict: {
columns: ['email'], // Conflict detection columns
action: 'update', // 'update' or 'ignore'
update: ['name', 'status'], // Columns to update on conflict
},
});
Map<String, Object> result = ductape.database.insert(Map.of(
"table", "users",
data: Map.of(
"email", "john@example.com",
"name", "John Doe",
"status", "active"
),
onConflict: Map.of(
columns: ['email'], // Conflict detection columns
"action", "update", // 'update' or 'ignore'
update: ['name', 'status'], // Columns to update on conflict
)
));
result := client.database.insert({
"table": "users",
data: {
"email": "john@example.com",
"name": "John Doe",
"status": "active",
},
onConflict: {
columns: ['email'], // Conflict detection columns
"action": "update", // 'update' or 'ignore'
update: ['name', 'status'], // Columns to update on conflict
},
});
var result = await ductape.database.insert({
["table"] = "users",
data: {
["email"] = "john@example.com",
["name"] = "John Doe",
["status"] = "active",
},
onConflict: {
columns: ['email'], // Conflict detection columns
["action"] = "update", // 'update' or 'ignore'
update: ['name', 'status'], // Columns to update on conflict
},
});
Insert with Connection Parameters
- TypeScript
- Java
- Go
- .NET
const result = await ductape.database.insert({
database: 'users-db',
table: 'users',
data: {
name: 'John Doe',
email: 'john@example.com',
},
});
Map<String, Object> result = ductape.database.insert(Map.of(
"database", "users-db",
"table", "users",
data: Map.of(
"name", "John Doe",
"email", "john@example.com"
)
));
result := client.database.insert({
"database": "users-db",
"table": "users",
data: {
"name": "John Doe",
"email": "john@example.com",
},
});
var result = await ductape.database.insert({
["database"] = "users-db",
["table"] = "users",
data: {
["name"] = "John Doe",
["email"] = "john@example.com",
},
});
Update Operations
Update Matching Records
- TypeScript
- Java
- Go
- .NET
const result = await ductape.database.update({
table: 'users',
data: {
status: 'inactive',
updated_at: new Date(),
},
where: {
last_login: { $lt: new Date('2023-01-01') },
},
returning: true,
});
console.log('Updated count:', result.count);
console.log('Updated records:', result.data);
Map<String, Object> result = ductape.database.update(Map.of(
"table", "users",
data: Map.of(
"status", "inactive",
updated_at: Instant.now()
),
where: Map.of(
last_login: Map.of( $lt: new Date('2023-01-01') )
),
"returning", true
));
System.out.println('Updated "count", ", result.count);
System.out.println("Updated records:', result.data);
result := client.database.update({
"table": "users",
data: {
"status": "inactive",
updated_at: new Date(),
},
where: {
last_login: { $lt: new Date('2023-01-01') },
},
"returning": true,
});
fmt.Println('Updated "count": ", result.count);
fmt.Println("Updated records:', result.data);
var result = await ductape.database.update({
["table"] = "users",
data: {
["status"] = "inactive",
updated_at: DateTime.UtcNow,
},
where: {
last_login: { $lt: new Date('2023-01-01') },
},
["returning"] = true,
});
Console.WriteLine('Updated ["count"] = ", result.count);
Console.WriteLine("Updated records:', result.data);
Update Operators
Ductape provides special operators for atomic update operations. These operators work across all supported databases (PostgreSQL, MySQL, MariaDB, MongoDB, DynamoDB, Cassandra) and use lowercase naming following the Mongoose/MongoDB convention.
Numeric Operators
- TypeScript
- Java
- Go
- .NET
// $inc - Increment a numeric value
await ductape.database.update({
table: 'products',
data: {
stock: { $inc: 10 }, // Add 10 to stock
views: { $inc: 1 }, // Increment view count
},
where: { id: productId },
});
// Decrement using negative value
await ductape.database.update({
table: 'products',
data: {
stock: { $inc: -5 }, // Subtract 5 from stock
},
where: { id: productId },
});
// $mul - Multiply a numeric value
await ductape.database.update({
table: 'products',
data: {
price: { $mul: 1.1 }, // Increase price by 10%
},
where: { category: 'electronics' },
});
// $min - Set to minimum (only update if new value is less than current)
await ductape.database.update({
table: 'products',
data: {
lowest_price: { $min: currentPrice }, // Track lowest price seen
},
where: { id: productId },
});
// $max - Set to maximum (only update if new value is greater than current)
await ductape.database.update({
table: 'products',
data: {
highest_price: { $max: currentPrice }, // Track highest price seen
},
where: { id: productId },
});
// $inc - Increment a numeric value
ductape.database.update(Map.of(
"table", "products",
data: Map.of(
stock: Map.of( $"inc", 10 ), // Add 10 to stock
views: Map.of( $"inc", 1 ), // Increment view count
),
where: Map.of( id: productId )
));
// Decrement using negative value
ductape.database.update(Map.of(
"table", "products",
data: Map.of(
stock: Map.of( $inc: -5 ), // Subtract 5 from stock
),
where: Map.of( id: productId )
));
// $mul - Multiply a numeric value
ductape.database.update(Map.of(
"table", "products",
data: Map.of(
price: Map.of( $"mul", 1.1 ), // Increase price by 10%
),
where: Map.of( "category", "electronics" )
));
// $min - Set to minimum (only update if new value is less than current)
ductape.database.update(Map.of(
"table", "products",
data: Map.of(
lowest_price: Map.of( $min: currentPrice ), // Track lowest price seen
),
where: Map.of( id: productId )
));
// $max - Set to maximum (only update if new value is greater than current)
ductape.database.update(Map.of(
"table", "products",
data: Map.of(
highest_price: Map.of( $max: currentPrice ), // Track highest price seen
),
where: Map.of( id: productId )
));
// $inc - Increment a numeric value
client.database.update({
"table": "products",
data: {
stock: { $"inc": 10 }, // Add 10 to stock
views: { $"inc": 1 }, // Increment view count
},
where: { id: productId },
});
// Decrement using negative value
client.database.update({
"table": "products",
data: {
stock: { $inc: -5 }, // Subtract 5 from stock
},
where: { id: productId },
});
// $mul - Multiply a numeric value
client.database.update({
"table": "products",
data: {
price: { $"mul": 1.1 }, // Increase price by 10%
},
where: { "category": "electronics" },
});
// $min - Set to minimum (only update if new value is less than current)
client.database.update({
"table": "products",
data: {
lowest_price: { $min: currentPrice }, // Track lowest price seen
},
where: { id: productId },
});
// $max - Set to maximum (only update if new value is greater than current)
client.database.update({
"table": "products",
data: {
highest_price: { $max: currentPrice }, // Track highest price seen
},
where: { id: productId },
});
// $inc - Increment a numeric value
await ductape.database.update({
["table"] = "products",
data: {
stock: { $["inc"] = 10 }, // Add 10 to stock
views: { $["inc"] = 1 }, // Increment view count
},
where: { id: productId },
});
// Decrement using negative value
await ductape.database.update({
["table"] = "products",
data: {
stock: { $inc: -5 }, // Subtract 5 from stock
},
where: { id: productId },
});
// $mul - Multiply a numeric value
await ductape.database.update({
["table"] = "products",
data: {
price: { $["mul"] = 1.1 }, // Increase price by 10%
},
where: { ["category"] = "electronics" },
});
// $min - Set to minimum (only update if new value is less than current)
await ductape.database.update({
["table"] = "products",
data: {
lowest_price: { $min: currentPrice }, // Track lowest price seen
},
where: { id: productId },
});
// $max - Set to maximum (only update if new value is greater than current)
await ductape.database.update({
["table"] = "products",
data: {
highest_price: { $max: currentPrice }, // Track highest price seen
},
where: { id: productId },
});
Field Operators
- TypeScript
- Java
- Go
- .NET
// $set - Explicitly set a value (useful when you need to distinguish from regular updates)
await ductape.database.update({
table: 'users',
data: {
settings: { $set: { theme: 'dark', language: 'en' } },
},
where: { id: userId },
});
// $unset - Remove/null a field
await ductape.database.update({
table: 'users',
data: {
temporary_token: { $unset: true }, // Set to NULL / remove field
},
where: { id: userId },
});
// $set - Explicitly set a value (useful when you need to distinguish from regular updates)
ductape.database.update(Map.of(
"table", "users",
data: Map.of(
settings: Map.of( $set: Map.of( "theme", "dark", "language", "en" ) )
),
where: Map.of( id: userId )
));
// $unset - Remove/null a field
ductape.database.update(Map.of(
"table", "users",
data: Map.of(
temporary_token: Map.of( $"unset", true ), // Set to NULL / remove field
),
where: Map.of( id: userId )
));
// $set - Explicitly set a value (useful when you need to distinguish from regular updates)
client.database.update({
"table": "users",
data: {
settings: { $set: { "theme": "dark", "language": "en" } },
},
where: { id: userId },
});
// $unset - Remove/null a field
client.database.update({
"table": "users",
data: {
temporary_token: { $"unset": true }, // Set to NULL / remove field
},
where: { id: userId },
});
// $set - Explicitly set a value (useful when you need to distinguish from regular updates)
await ductape.database.update({
["table"] = "users",
data: {
settings: { $set: { ["theme"] = "dark", ["language"] = "en" } },
},
where: { id: userId },
});
// $unset - Remove/null a field
await ductape.database.update({
["table"] = "users",
data: {
temporary_token: { $["unset"] = true }, // Set to NULL / remove field
},
where: { id: userId },
});
Array Operators
These operators work with array/list columns. Behavior varies by database:
- PostgreSQL: Works with native array types
- MySQL/MariaDB: Works with JSON arrays
- MongoDB: Native array support
- DynamoDB: Works with List and Set types
- Cassandra: Works with list and set types
- TypeScript
- Java
- Go
- .NET
// $push - Add an element to an array
await ductape.database.update({
table: 'users',
data: {
tags: { $push: 'premium' }, // Add 'premium' to tags array
},
where: { id: userId },
});
// $pull - Remove an element from an array
await ductape.database.update({
table: 'users',
data: {
tags: { $pull: 'trial' }, // Remove 'trial' from tags array
},
where: { id: userId },
});
// $addToSet - Add element only if it doesn't exist (unique add)
await ductape.database.update({
table: 'users',
data: {
roles: { $addToSet: 'editor' }, // Add 'editor' only if not present
},
where: { id: userId },
});
// $push - Add an element to an array
ductape.database.update(Map.of(
"table", "users",
data: Map.of(
tags: Map.of( $"push", "premium" ), // Add 'premium' to tags array
),
where: Map.of( id: userId )
));
// $pull - Remove an element from an array
ductape.database.update(Map.of(
"table", "users",
data: Map.of(
tags: Map.of( $"pull", "trial" ), // Remove 'trial' from tags array
),
where: Map.of( id: userId )
));
// $addToSet - Add element only if it doesn't exist (unique add)
ductape.database.update(Map.of(
"table", "users",
data: Map.of(
roles: Map.of( $"addToSet", "editor" ), // Add 'editor' only if not present
),
where: Map.of( id: userId )
));
// $push - Add an element to an array
client.database.update({
"table": "users",
data: {
tags: { $"push": "premium" }, // Add 'premium' to tags array
},
where: { id: userId },
});
// $pull - Remove an element from an array
client.database.update({
"table": "users",
data: {
tags: { $"pull": "trial" }, // Remove 'trial' from tags array
},
where: { id: userId },
});
// $addToSet - Add element only if it doesn't exist (unique add)
client.database.update({
"table": "users",
data: {
roles: { $"addToSet": "editor" }, // Add 'editor' only if not present
},
where: { id: userId },
});
// $push - Add an element to an array
await ductape.database.update({
["table"] = "users",
data: {
tags: { $["push"] = "premium" }, // Add 'premium' to tags array
},
where: { id: userId },
});
// $pull - Remove an element from an array
await ductape.database.update({
["table"] = "users",
data: {
tags: { $["pull"] = "trial" }, // Remove 'trial' from tags array
},
where: { id: userId },
});
// $addToSet - Add element only if it doesn't exist (unique add)
await ductape.database.update({
["table"] = "users",
data: {
roles: { $["addToSet"] = "editor" }, // Add 'editor' only if not present
},
where: { id: userId },
});
Operator Reference
| Operator | Description | Supported Databases |
|---|---|---|
$inc | Increment numeric value | All |
$mul | Multiply numeric value | PostgreSQL, MySQL, MariaDB, MongoDB |
$min | Set to minimum of current and new value | PostgreSQL, MySQL, MariaDB, MongoDB |
$max | Set to maximum of current and new value | PostgreSQL, MySQL, MariaDB, MongoDB |
$set | Explicitly set a value | All |
$unset | Remove/null a field | All |
$push | Add element to array | All |
$pull | Remove element from array | All (except DynamoDB) |
$addToSet | Add unique element to array | All |
$pop | Remove first/last element from array | PostgreSQL, MySQL, MongoDB |
$rename | Rename a field | MongoDB |
$currentDate | Set to current date/timestamp | MongoDB |
Uppercase operators (e.g., $INC, $SET) are still supported for backwards compatibility, but lowercase is recommended.
Combining Multiple Operators
- TypeScript
- Java
- Go
- .NET
await ductape.database.update({
table: 'game_stats',
data: {
score: { $inc: 100 }, // Increment score
high_score: { $max: newScore }, // Update high score if higher
games_played: { $inc: 1 }, // Increment game count
achievements: { $addToSet: 'first_win' }, // Add achievement
updated_at: new Date(), // Regular field update
},
where: { player_id: playerId },
});
ductape.database.update(Map.of(
"table", "game_stats",
data: Map.of(
score: Map.of( $"inc", 100 ), // Increment score
high_score: Map.of( $max: newScore ), // Update high score if higher
games_played: Map.of( $"inc", 1 ), // Increment game count
achievements: Map.of( $"addToSet", "first_win" ), // Add achievement
updated_at: Instant.now(), // Regular field update
),
where: Map.of( player_id: playerId )
));
client.database.update({
"table": "game_stats",
data: {
score: { $"inc": 100 }, // Increment score
high_score: { $max: newScore }, // Update high score if higher
games_played: { $"inc": 1 }, // Increment game count
achievements: { $"addToSet": "first_win" }, // Add achievement
updated_at: new Date(), // Regular field update
},
where: { player_id: playerId },
});
await ductape.database.update({
["table"] = "game_stats",
data: {
score: { $["inc"] = 100 }, // Increment score
high_score: { $max: newScore }, // Update high score if higher
games_played: { $["inc"] = 1 }, // Increment game count
achievements: { $["addToSet"] = "first_win" }, // Add achievement
updated_at: DateTime.UtcNow, // Regular field update
},
where: { player_id: playerId },
});
Update with Complex Conditions
- TypeScript
- Java
- Go
- .NET
await ductape.database.update({
table: 'orders',
data: {
status: 'cancelled',
cancelled_at: new Date(),
cancellation_reason: 'Customer request',
},
where: {
$and: [
{ status: { $in: ['pending', 'processing'] } },
{ created_at: { $lt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) } },
],
},
});
ductape.database.update(Map.of(
"table", "orders",
data: Map.of(
"status", "cancelled",
cancelled_at: Instant.now(),
"cancellation_reason", "Customer request"
),
where: Map.of(
$and: [
Map.of( status: Map.of( $in: ['pending', 'processing'] ) ),
Map.of( created_at: Map.of( $lt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) ) ),
]
)
));
client.database.update({
"table": "orders",
data: {
"status": "cancelled",
cancelled_at: new Date(),
"cancellation_reason": "Customer request",
},
where: {
$and: [
{ status: { $in: ['pending', 'processing'] } },
{ created_at: { $lt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) } },
],
},
});
await ductape.database.update({
["table"] = "orders",
data: {
["status"] = "cancelled",
cancelled_at: DateTime.UtcNow,
["cancellation_reason"] = "Customer request",
},
where: {
$and: [
{ status: { $in: ['pending', 'processing'] } },
{ created_at: { $lt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) } },
],
},
});
Update Single Record by ID
- TypeScript
- Java
- Go
- .NET
await ductape.database.update({
table: 'users',
data: {
name: 'Jane Doe',
email: 'jane@example.com',
},
where: { id: userId },
returning: true,
});
ductape.database.update(Map.of(
"table", "users",
data: Map.of(
"name", "Jane Doe",
"email", "jane@example.com"
),
where: Map.of( id: userId ),
"returning", true
));
client.database.update({
"table": "users",
data: {
"name": "Jane Doe",
"email": "jane@example.com",
},
where: { id: userId },
"returning": true,
});
await ductape.database.update({
["table"] = "users",
data: {
["name"] = "Jane Doe",
["email"] = "jane@example.com",
},
where: { id: userId },
["returning"] = true,
});
Upsert Operations
Insert a record or update if it already exists:
Basic Upsert
- TypeScript
- Java
- Go
- .NET
const result = await ductape.database.upsert({
table: 'user_preferences',
data: {
user_id: 123,
theme: 'dark',
language: 'en',
notifications: true,
},
conflictKeys: ['user_id'], // Unique key to check
});
console.log('Operation:', result.operation); // 'inserted' or 'updated'
console.log('Affected rows:', result.count);
Map<String, Object> result = ductape.database.upsert(Map.of(
"table", "user_preferences",
data: Map.of(
"user_id", 123,
"theme", "dark",
"language", "en",
"notifications", true
),
conflictKeys: ['user_id'], // Unique key to check
));
System.out.println('"Operation", ", result.operation); // "inserted' or 'updated'
System.out.println('Affected rows:', result.count);
result := client.database.upsert({
"table": "user_preferences",
data: {
"user_id": 123,
"theme": "dark",
"language": "en",
"notifications": true,
},
conflictKeys: ['user_id'], // Unique key to check
});
fmt.Println('"Operation": ", result.operation); // "inserted' or 'updated'
fmt.Println('Affected rows:', result.count);
var result = await ductape.database.upsert({
["table"] = "user_preferences",
data: {
["user_id"] = 123,
["theme"] = "dark",
["language"] = "en",
["notifications"] = true,
},
conflictKeys: ['user_id'], // Unique key to check
});
Console.WriteLine('["Operation"] = ", result.operation); // "inserted' or 'updated'
Console.WriteLine('Affected rows:', result.count);
Upsert with Specific Update Columns
- TypeScript
- Java
- Go
- .NET
const result = await ductape.database.upsert({
table: 'product_inventory',
data: {
product_id: 'prod-123',
warehouse_id: 'wh-1',
quantity: 100,
last_restocked: new Date(),
},
conflictKeys: ['product_id', 'warehouse_id'],
updateColumns: ['quantity', 'last_restocked'], // Only update these on conflict
});
Map<String, Object> result = ductape.database.upsert(Map.of(
"table", "product_inventory",
data: Map.of(
"product_id", "prod-123",
"warehouse_id", "wh-1",
"quantity", 100,
last_restocked: Instant.now()
),
conflictKeys: ['product_id', 'warehouse_id'],
updateColumns: ['quantity', 'last_restocked'], // Only update these on conflict
));
result := client.database.upsert({
"table": "product_inventory",
data: {
"product_id": "prod-123",
"warehouse_id": "wh-1",
"quantity": 100,
last_restocked: new Date(),
},
conflictKeys: ['product_id', 'warehouse_id'],
updateColumns: ['quantity', 'last_restocked'], // Only update these on conflict
});
var result = await ductape.database.upsert({
["table"] = "product_inventory",
data: {
["product_id"] = "prod-123",
["warehouse_id"] = "wh-1",
["quantity"] = 100,
last_restocked: DateTime.UtcNow,
},
conflictKeys: ['product_id', 'warehouse_id'],
updateColumns: ['quantity', 'last_restocked'], // Only update these on conflict
});
Delete Operations
Delete Matching Records
- TypeScript
- Java
- Go
- .NET
const result = await ductape.database.delete({
table: 'users',
where: {
status: 'deleted',
},
});
console.log('Deleted count:', result.count);
Map<String, Object> result = ductape.database.delete(Map.of(
"table", "users",
where: Map.of(
"status", "deleted"
)
));
System.out.println('Deleted count:', result.count);
result := client.database.delete({
"table": "users",
where: {
"status": "deleted",
},
});
fmt.Println('Deleted count:', result.count);
var result = await ductape.database.delete({
["table"] = "users",
where: {
["status"] = "deleted",
},
});
Console.WriteLine('Deleted count:', result.count);
Delete with Complex Conditions
- TypeScript
- Java
- Go
- .NET
await ductape.database.delete({
table: 'sessions',
where: {
$and: [
{ expires_at: { $lt: new Date() } },
{ user_id: { $isNotNull: true } },
],
},
});
ductape.database.delete(Map.of(
"table", "sessions",
where: Map.of(
$and: [
Map.of( expires_at: Map.of( $lt: Instant.now() ) ),
Map.of( user_id: Map.of( $"isNotNull", true ) ),
]
)
));
client.database.delete({
"table": "sessions",
where: {
$and: [
{ expires_at: { $lt: new Date() } },
{ user_id: { $"isNotNull": true } },
],
},
});
await ductape.database.delete({
["table"] = "sessions",
where: {
$and: [
{ expires_at: { $lt: DateTime.UtcNow } },
{ user_id: { $["isNotNull"] = true } },
],
},
});
Delete Single Record
- TypeScript
- Java
- Go
- .NET
await ductape.database.delete({
table: 'users',
where: { id: userId },
});
ductape.database.delete(Map.of(
"table", "users",
where: Map.of( id: userId )
));
client.database.delete({
"table": "users",
where: { id: userId },
});
await ductape.database.delete({
["table"] = "users",
where: { id: userId },
});
Soft Delete Pattern
Instead of permanently deleting, mark records as deleted:
- TypeScript
- Java
- Go
- .NET
// Soft delete
await ductape.database.update({
table: 'users',
data: {
deleted_at: new Date(),
status: 'deleted',
},
where: { id: userId },
});
// Query excluding soft-deleted records
const activeUsers = await ductape.database.query({
table: 'users',
where: {
deleted_at: { $isNull: true },
},
});
// Soft delete
ductape.database.update(Map.of(
"table", "users",
data: Map.of(
deleted_at: Instant.now(),
"status", "deleted"
),
where: Map.of( id: userId )
));
// Query excluding soft-deleted records
Map<String, Object> activeUsers = ductape.database.query(Map.of(
"table", "users",
where: Map.of(
deleted_at: Map.of( $"isNull", true )
)
));
// Soft delete
client.database.update({
"table": "users",
data: {
deleted_at: new Date(),
"status": "deleted",
},
where: { id: userId },
});
// Query excluding soft-deleted records
activeUsers := client.database.query({
"table": "users",
where: {
deleted_at: { $"isNull": true },
},
});
// Soft delete
await ductape.database.update({
["table"] = "users",
data: {
deleted_at: DateTime.UtcNow,
["status"] = "deleted",
},
where: { id: userId },
});
// Query excluding soft-deleted records
var activeUsers = await ductape.database.query({
["table"] = "users",
where: {
deleted_at: { $["isNull"] = true },
},
});
Transactions
Wrap multiple write operations in a transaction:
Using the Callback API (Recommended)
- TypeScript
- Java
- Go
- .NET
const result = await ductape.database.transaction({
database: 'main-db',
}, async (transaction) => {
// Insert order
const order = await ductape.database.insert({
table: 'orders',
data: {
customer_id: customerId,
total: 99.99,
status: 'pending',
},
transaction,
});
// Insert order items
await ductape.database.insert({
table: 'order_items',
data: items.map(item => ({
order_id: order.insertedIds[0],
product_id: item.productId,
quantity: item.quantity,
price: item.price,
})),
transaction,
});
// Update inventory
for (const item of items) {
await ductape.database.update({
table: 'products',
data: { stock: { $dec: item.quantity } },
where: { id: item.productId },
transaction,
});
}
return order;
});
Map<String, Object> result = ductape.database.transaction(Map.of(
"database", "main-db"
), async (transaction) => Map.of(
// Insert order
Map<String, Object> order = ductape.database.insert(Map.of(
"table", "orders",
data: Map.of(
customer_id: customerId,
"total", 99.99,
"status", "pending"
),
transaction
));
// Insert order items
ductape.database.insert(Map.of(
"table", "order_items",
data: items.map(item => (Map.of(
order_id: order.insertedIds[0],
product_id: item.productId,
quantity: item.quantity,
price: item.price
))),
transaction
));
// Update inventory
for (Map<String, Object> item of items) Map.of(
ductape.database.update(Map.of(
"table", "products",
data: Map.of( stock: Map.of( $dec: item.quantity ) ),
where: Map.of( id: item.productId ),
transaction
));
)
return order;
));
result := client.database.transaction({
"database": "main-db",
}, async (transaction) => {
// Insert order
order := client.database.insert({
"table": "orders",
data: {
customer_id: customerId,
"total": 99.99,
"status": "pending",
},
transaction,
});
// Insert order items
client.database.insert({
"table": "order_items",
data: items.map(item => ({
order_id: order.insertedIds[0],
product_id: item.productId,
quantity: item.quantity,
price: item.price,
})),
transaction,
});
// Update inventory
for (const item of items) {
client.database.update({
"table": "products",
data: { stock: { $dec: item.quantity } },
where: { id: item.productId },
transaction,
});
}
return order;
});
var result = await ductape.database.transaction({
["database"] = "main-db",
}, async (transaction) => {
// Insert order
var order = await ductape.database.insert({
["table"] = "orders",
data: {
customer_id: customerId,
["total"] = 99.99,
["status"] = "pending",
},
transaction,
});
// Insert order items
await ductape.database.insert({
["table"] = "order_items",
data: items.map(item => ({
order_id: order.insertedIds[0],
product_id: item.productId,
quantity: item.quantity,
price: item.price,
})),
transaction,
});
// Update inventory
for (var item of items) {
await ductape.database.update({
["table"] = "products",
data: { stock: { $dec: item.quantity } },
where: { id: item.productId },
transaction,
});
}
return order;
});
Manual Transaction Control
- TypeScript
- Java
- Go
- .NET
const transaction = await ductape.database.beginTransaction({
database: 'main-db',
});
try {
await ductape.database.insert({
table: 'accounts',
data: { balance: 1000 },
transaction,
});
await ductape.database.update({
table: 'accounts',
data: { balance: { $dec: 100 } },
where: { id: sourceAccountId },
transaction,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
Map<String, Object> transaction = ductape.database.beginTransaction(Map.of(
"database", "main-db"
));
try Map.of(
ductape.database.insert(Map.of(
"table", "accounts",
data: Map.of( "balance", 1000 ),
transaction
));
ductape.database.update(Map.of(
"table", "accounts",
data: Map.of( balance: Map.of( $"dec", 100 ) ),
where: Map.of( id: sourceAccountId ),
transaction
));
transaction.commit();
) catch (error) Map.of(
transaction.rollback();
throw error;
)
transaction := client.database.beginTransaction({
"database": "main-db",
});
try {
client.database.insert({
"table": "accounts",
data: { "balance": 1000 },
transaction,
});
client.database.update({
"table": "accounts",
data: { balance: { $"dec": 100 } },
where: { id: sourceAccountId },
transaction,
});
transaction.commit();
} catch (error) {
transaction.rollback();
throw error;
}
var transaction = await ductape.database.beginTransaction({
["database"] = "main-db",
});
try {
await ductape.database.insert({
["table"] = "accounts",
data: { ["balance"] = 1000 },
transaction,
});
await ductape.database.update({
["table"] = "accounts",
data: { balance: { $["dec"] = 100 } },
where: { id: sourceAccountId },
transaction,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
Write Result Structures
Insert Result
interface IInsertResult<T> {
data: T[]; // Inserted records (if returning: true)
count: number; // Number of inserted records
insertedIds: any[]; // IDs of inserted records
}
Update Result
interface IUpdateResult<T> {
data: T[]; // Updated records (if returning: true)
count: number; // Number of updated records
}
Delete Result
interface IDeleteResult {
count: number; // Number of deleted records
}
Upsert Result
interface IUpsertResult<T> {
data: T[]; // Affected records
count: number; // Number of affected records
operation: 'inserted' | 'updated';
}
Options Reference
Insert Options
| Option | Type | Description |
|---|---|---|
table | string | Table name |
data | object | object[] | Record(s) to insert |
returning | boolean | Return inserted records |
onConflict | object | Conflict handling configuration |
transaction | ITransaction | Transaction to use |
Update Options
| Option | Type | Description |
|---|---|---|
table | string | Table name |
data | object | Fields to update |
where | object | Filter conditions |
returning | boolean | Return updated records |
transaction | ITransaction | Transaction to use |
Delete Options
| Option | Type | Description |
|---|---|---|
table | string | Table name |
where | object | Filter conditions |
returning | boolean | Return deleted records |
transaction | ITransaction | Transaction to use |
Upsert Options
| Option | Type | Description |
|---|---|---|
table | string | Table name |
data | object | Record to insert/update |
conflictKeys | string[] | Unique key columns |
updateColumns | string[] | Columns to update on conflict |
transaction | ITransaction | Transaction to use |
Best Practices
1. Always Use Transactions for Related Changes
- TypeScript
- Java
- Go
- .NET
// Good: Multiple related changes in a transaction
await ductape.database.transaction({ ... }, async (trx) => {
await ductape.database.insert({ table: 'orders', data: order, transaction: trx });
await ductape.database.insert({ table: 'order_items', data: items, transaction: trx });
await ductape.database.update({ table: 'inventory', data: updates, transaction: trx });
});
// Bad: Related changes without transaction
await ductape.database.insert({ table: 'orders', data: order });
await ductape.database.insert({ table: 'order_items', data: items }); // Might fail, leaving orphaned order
// Good: Multiple related changes in a transaction
ductape.database.transaction(Map.of( ... ), async (trx) => Map.of(
ductape.database.insert(Map.of( "table", "orders", data: order, transaction: trx ));
ductape.database.insert(Map.of( "table", "order_items", data: items, transaction: trx ));
ductape.database.update(Map.of( "table", "inventory", data: updates, transaction: trx ));
));
// Bad: Related changes without transaction
ductape.database.insert(Map.of( "table", "orders", data: order ));
ductape.database.insert(Map.of( "table", "order_items", data: items )); // Might fail, leaving orphaned order
// Good: Multiple related changes in a transaction
client.database.transaction({ ... }, async (trx) => {
client.database.insert({ "table": "orders", data: order, transaction: trx });
client.database.insert({ "table": "order_items", data: items, transaction: trx });
client.database.update({ "table": "inventory", data: updates, transaction: trx });
});
// Bad: Related changes without transaction
client.database.insert({ "table": "orders", data: order });
client.database.insert({ "table": "order_items", data: items }); // Might fail, leaving orphaned order
// Good: Multiple related changes in a transaction
await ductape.database.transaction({ ... }, async (trx) => {
await ductape.database.insert({ ["table"] = "orders", data: order, transaction: trx });
await ductape.database.insert({ ["table"] = "order_items", data: items, transaction: trx });
await ductape.database.update({ ["table"] = "inventory", data: updates, transaction: trx });
});
// Bad: Related changes without transaction
await ductape.database.insert({ ["table"] = "orders", data: order });
await ductape.database.insert({ ["table"] = "order_items", data: items }); // Might fail, leaving orphaned order
2. Use Returning for Immediate Data Access
- TypeScript
- Java
- Go
- .NET
// Good: Get inserted data immediately
const result = await ductape.database.insert({
table: 'users',
data: userData,
returning: true,
});
const newUser = result.data[0];
// Avoid: Separate query after insert
const insertResult = await ductape.database.insert({ table: 'users', data: userData });
const newUser = await ductape.database.query({
table: 'users',
where: { id: insertResult.insertedIds[0] }
});
// Good: Get inserted data immediately
Map<String, Object> result = ductape.database.insert(Map.of(
"table", "users",
data: userData,
"returning", true
));
Map<String, Object> newUser = result.data[0];
// Avoid: Separate query after insert
Map<String, Object> insertResult = ductape.database.insert(Map.of( "table", "users", data: userData ));
Map<String, Object> newUser = ductape.database.query(Map.of(
"table", "users",
where: Map.of( id: insertResult.insertedIds[0] )
));
// Good: Get inserted data immediately
result := client.database.insert({
"table": "users",
data: userData,
"returning": true,
});
newUser := result.data[0];
// Avoid: Separate query after insert
insertResult := client.database.insert({ "table": "users", data: userData });
newUser := client.database.query({
"table": "users",
where: { id: insertResult.insertedIds[0] }
});
// Good: Get inserted data immediately
var result = await ductape.database.insert({
["table"] = "users",
data: userData,
["returning"] = true,
});
var newUser = result.data[0];
// Avoid: Separate query after insert
var insertResult = await ductape.database.insert({ ["table"] = "users", data: userData });
var newUser = await ductape.database.query({
["table"] = "users",
where: { id: insertResult.insertedIds[0] }
});
3. Validate Before Writing
- TypeScript
- Java
- Go
- .NET
// Validate data before database operations
function validateUser(data: any) {
if (!data.email || !data.email.includes('@')) {
throw new Error('Invalid email');
}
if (!data.name || data.name.length < 2) {
throw new Error('Name too short');
}
}
validateUser(userData);
await ductape.database.insert({ table: 'users', data: userData });
// Validate data before database operations
function validateUser(data: any) Map.of(
if (!data.email || !data.email.includes('@')) Map.of(
throw new Error('Invalid email');
)
if (!data.name || data.name.length < 2) Map.of(
throw new Error('Name too short');
)
)
validateUser(userData);
ductape.database.insert(Map.of( "table", "users", data: userData ));
// Validate data before database operations
function validateUser(data: any) {
if (!data.email || !data.email.includes('@')) {
throw new Error('Invalid email');
}
if (!data.name || data.name.length < 2) {
throw new Error('Name too short');
}
}
validateUser(userData);
client.database.insert({ "table": "users", data: userData });
// Validate data before database operations
function validateUser(data: any) {
if (!data.email || !data.email.includes('@')) {
throw new Error('Invalid email');
}
if (!data.name || data.name.length < 2) {
throw new Error('Name too short');
}
}
validateUser(userData);
await ductape.database.insert({ ["table"] = "users", data: userData });
4. Use Soft Deletes for Important Data
- TypeScript
- Java
- Go
- .NET
// Instead of hard delete
await ductape.database.delete({ table: 'users', where: { id: userId } });
// Use soft delete
await ductape.database.update({
table: 'users',
data: { deleted_at: new Date(), status: 'deleted' },
where: { id: userId },
});
// Instead of hard delete
ductape.database.delete(Map.of( "table", "users", where: Map.of( id: userId ) ));
// Use soft delete
ductape.database.update(Map.of(
"table", "users",
data: Map.of( deleted_at: Instant.now(), "status", "deleted" ),
where: Map.of( id: userId )
));
// Instead of hard delete
client.database.delete({ "table": "users", where: { id: userId } });
// Use soft delete
client.database.update({
"table": "users",
data: { deleted_at: new Date(), "status": "deleted" },
where: { id: userId },
});
// Instead of hard delete
await ductape.database.delete({ ["table"] = "users", where: { id: userId } });
// Use soft delete
await ductape.database.update({
["table"] = "users",
data: { deleted_at: DateTime.UtcNow, ["status"] = "deleted" },
where: { id: userId },
});
5. Handle Conflicts Gracefully
- TypeScript
- Java
- Go
- .NET
try {
await ductape.database.insert({
table: 'users',
data: { email: 'test@example.com', name: 'Test' },
});
} catch (error) {
if (error.code === 'UNIQUE_VIOLATION') {
// Handle duplicate email
console.log('Email already exists');
} else {
throw error;
}
}
// Or use upsert for automatic handling
await ductape.database.upsert({
table: 'users',
data: { email: 'test@example.com', name: 'Test' },
conflictKeys: ['email'],
});
try Map.of(
ductape.database.insert(Map.of(
"table", "users",
data: Map.of( "email", "test@example.com", "name", "Test" )
));
) catch (error) Map.of(
if (error.code === 'UNIQUE_VIOLATION') Map.of(
// Handle duplicate email
System.out.println('Email already exists');
) else Map.of(
throw error;
)
)
// Or use upsert for automatic handling
ductape.database.upsert(Map.of(
"table", "users",
data: Map.of( "email", "test@example.com", "name", "Test" ),
conflictKeys: ['email']
));
try {
client.database.insert({
"table": "users",
data: { "email": "test@example.com", "name": "Test" },
});
} catch (error) {
if (error.code === 'UNIQUE_VIOLATION') {
// Handle duplicate email
fmt.Println('Email already exists');
} else {
throw error;
}
}
// Or use upsert for automatic handling
client.database.upsert({
"table": "users",
data: { "email": "test@example.com", "name": "Test" },
conflictKeys: ['email'],
});
try {
await ductape.database.insert({
["table"] = "users",
data: { ["email"] = "test@example.com", ["name"] = "Test" },
});
} catch (error) {
if (error.code === 'UNIQUE_VIOLATION') {
// Handle duplicate email
Console.WriteLine('Email already exists');
} else {
throw error;
}
}
// Or use upsert for automatic handling
await ductape.database.upsert({
["table"] = "users",
data: { ["email"] = "test@example.com", ["name"] = "Test" },
conflictKeys: ['email'],
});
Next Steps
- Aggregations - Perform calculations on your data
- Transactions - Deep dive into transaction management
- Best Practices - Production-ready patterns