Database Actions
Create reusable query templates that can be executed with different input parameters. Database actions allow you to define queries once and reuse them across your application with variable interpolation.
Quick Example
- TypeScript
- Java
- Go
- .NET
// Create an action template
await ductape.database.action.create({
name: 'Get Users Paginated',
tag: 'postgresdb:get-users-paginated',
tableName: 'users',
operation: DatabaseActionTypes.QUERY,
description: 'Fetch paginated list of active users',
template: {
where: {
is_active: true,
},
limit: '{{limit}}',
offset: '{{offset}}',
orderBy: [
{
column: '{{orderColumn}}',
order: '{{orderDirection}}',
},
],
},
});
// Execute the action with different inputs
const users = await ductape.database.execute({
database: 'postgresdb',
action: 'get-users-paginated',
input: {
limit: 25,
offset: 0,
orderColumn: 'created_at',
orderDirection: 'DESC',
},
});
// Create an action template
ductape.database.action.create(Map.of(
"name", "Get Users Paginated",
"tag", "postgresdb:get-users-paginated",
"tableName", "users",
operation: DatabaseActionTypes.QUERY,
"description", "Fetch paginated list of active users",
template: Map.of(
where: Map.of(
"is_active", true
),
"limit", "Map.of(Map.of(limit))",
"offset", "Map.of(Map.of(offset))",
orderBy: [
Map.of(
"column", "Map.of(Map.of(orderColumn))",
"order", "Map.of(Map.of(orderDirection))"
),
]
)
));
// Execute the action with different inputs
Map<String, Object> users = ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "get-users-paginated",
input: Map.of(
"limit", 25,
"offset", 0,
"orderColumn", "created_at",
"orderDirection", "DESC"
)
));
// Create an action template
client.database.action.create({
"name": "Get Users Paginated",
"tag": "postgresdb:get-users-paginated",
"tableName": "users",
operation: DatabaseActionTypes.QUERY,
"description": "Fetch paginated list of active users",
template: {
where: {
"is_active": true,
},
"limit": "{{limit}}",
"offset": "{{offset}}",
orderBy: [
{
"column": "{{orderColumn}}",
"order": "{{orderDirection}}",
},
],
},
});
// Execute the action with different inputs
users := client.database.execute({
"database": "postgresdb",
"action": "get-users-paginated",
input: {
"limit": 25,
"offset": 0,
"orderColumn": "created_at",
"orderDirection": "DESC",
},
});
// Create an action template
await ductape.database.action.create({
["name"] = "Get Users Paginated",
["tag"] = "postgresdb:get-users-paginated",
["tableName"] = "users",
operation: DatabaseActionTypes.QUERY,
["description"] = "Fetch paginated list of active users",
template: {
where: {
["is_active"] = true,
},
["limit"] = "{{limit}}",
["offset"] = "{{offset}}",
orderBy: [
{
["column"] = "{{orderColumn}}",
["order"] = "{{orderDirection}}",
},
],
},
});
// Execute the action with different inputs
var users = await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "get-users-paginated",
input: {
["limit"] = 25,
["offset"] = 0,
["orderColumn"] = "created_at",
["orderDirection"] = "DESC",
},
});
Why Use Actions?
Benefits:
- Reusability - Define query logic once, use everywhere
- Type Safety - Template validation at creation time
- Variable Interpolation - Dynamic queries with
{{placeholder}}syntax - Maintainability - Update query logic in one place
- Security - Centralized query management
- Testing - Easy to test query templates
Action Types
QUERY - Read Data
Fetch records from a table:
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Get Active Users',
tag: 'postgresdb:get-active-users',
tableName: 'users',
operation: DatabaseActionTypes.QUERY,
template: {
where: {
status: '{{status}}',
created_at: { $GTE: '{{startDate}}' },
},
select: ['id', 'name', 'email', 'created_at'],
limit: '{{limit}}',
orderBy: [{ column: 'created_at', order: 'DESC' }],
},
});
// Execute
const users = await ductape.database.execute({
database: 'postgresdb',
action: 'get-active-users',
input: {
status: 'active',
startDate: '2024-01-01',
limit: 50,
},
});
ductape.database.action.create(Map.of(
"name", "Get Active Users",
"tag", "postgresdb:get-active-users",
"tableName", "users",
operation: DatabaseActionTypes.QUERY,
template: Map.of(
where: Map.of(
"status", "Map.of(Map.of(status))",
created_at: Map.of( $"GTE", "Map.of(Map.of(startDate))" )
),
select: ['id', 'name', 'email', 'created_at'],
"limit", "Map.of(Map.of(limit))",
orderBy: [Map.of( "column", "created_at", "order", "DESC" )]
)
));
// Execute
Map<String, Object> users = ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "get-active-users",
input: Map.of(
"status", "active",
"startDate", "2024-01-01",
"limit", 50
)
));
client.database.action.create({
"name": "Get Active Users",
"tag": "postgresdb:get-active-users",
"tableName": "users",
operation: DatabaseActionTypes.QUERY,
template: {
where: {
"status": "{{status}}",
created_at: { $"GTE": "{{startDate}}" },
},
select: ['id', 'name', 'email', 'created_at'],
"limit": "{{limit}}",
orderBy: [{ "column": "created_at", "order": "DESC" }],
},
});
// Execute
users := client.database.execute({
"database": "postgresdb",
"action": "get-active-users",
input: {
"status": "active",
"startDate": "2024-01-01",
"limit": 50,
},
});
await ductape.database.action.create({
["name"] = "Get Active Users",
["tag"] = "postgresdb:get-active-users",
["tableName"] = "users",
operation: DatabaseActionTypes.QUERY,
template: {
where: {
["status"] = "{{status}}",
created_at: { $["GTE"] = "{{startDate}}" },
},
select: ['id', 'name', 'email', 'created_at'],
["limit"] = "{{limit}}",
orderBy: [{ ["column"] = "created_at", ["order"] = "DESC" }],
},
});
// Execute
var users = await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "get-active-users",
input: {
["status"] = "active",
["startDate"] = "2024-01-01",
["limit"] = 50,
},
});
INSERT - Create Records
Insert new records:
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Create User',
tag: 'postgresdb:create-user',
tableName: 'users',
operation: DatabaseActionTypes.INSERT,
template: {
records: [
{
name: '{{name}}',
email: '{{email}}',
status: 'active',
created_at: '{{createdAt}}',
},
],
},
});
// Execute
await ductape.database.execute({
database: 'postgresdb',
action: 'create-user',
input: {
name: 'Alice Johnson',
email: 'alice@example.com',
createdAt: new Date().toISOString(),
},
});
ductape.database.action.create(Map.of(
"name", "Create User",
"tag", "postgresdb:create-user",
"tableName", "users",
operation: DatabaseActionTypes.INSERT,
template: Map.of(
records: [
Map.of(
"name", "Map.of(Map.of(name))",
"email", "Map.of(Map.of(email))",
"status", "active",
"created_at", "Map.of(Map.of(createdAt))"
),
]
)
));
// Execute
ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "create-user",
input: Map.of(
"name", "Alice Johnson",
"email", "alice@example.com",
createdAt: Instant.now().toISOString()
)
));
client.database.action.create({
"name": "Create User",
"tag": "postgresdb:create-user",
"tableName": "users",
operation: DatabaseActionTypes.INSERT,
template: {
records: [
{
"name": "{{name}}",
"email": "{{email}}",
"status": "active",
"created_at": "{{createdAt}}",
},
],
},
});
// Execute
client.database.execute({
"database": "postgresdb",
"action": "create-user",
input: {
"name": "Alice Johnson",
"email": "alice@example.com",
createdAt: new Date().toISOString(),
},
});
await ductape.database.action.create({
["name"] = "Create User",
["tag"] = "postgresdb:create-user",
["tableName"] = "users",
operation: DatabaseActionTypes.INSERT,
template: {
records: [
{
["name"] = "{{name}}",
["email"] = "{{email}}",
["status"] = "active",
["created_at"] = "{{createdAt}}",
},
],
},
});
// Execute
await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "create-user",
input: {
["name"] = "Alice Johnson",
["email"] = "alice@example.com",
createdAt: DateTime.UtcNow.toISOString(),
},
});
UPDATE - Modify Records
Update existing records:
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Update User Status',
tag: 'postgresdb:update-user-status',
tableName: 'users',
operation: DatabaseActionTypes.UPDATE,
template: {
status: '{{status}}',
updated_at: '{{updatedAt}}',
},
filterTemplate: {
id: '{{userId}}',
},
});
// Execute
await ductape.database.execute({
database: 'postgresdb',
action: 'update-user-status',
input: {
userId: 123,
status: 'suspended',
updatedAt: new Date().toISOString(),
},
});
ductape.database.action.create(Map.of(
"name", "Update User Status",
"tag", "postgresdb:update-user-status",
"tableName", "users",
operation: DatabaseActionTypes.UPDATE,
template: Map.of(
"status", "Map.of(Map.of(status))",
"updated_at", "Map.of(Map.of(updatedAt))"
),
filterTemplate: Map.of(
"id", "Map.of(Map.of(userId))"
)
));
// Execute
ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "update-user-status",
input: Map.of(
"userId", 123,
"status", "suspended",
updatedAt: Instant.now().toISOString()
)
));
client.database.action.create({
"name": "Update User Status",
"tag": "postgresdb:update-user-status",
"tableName": "users",
operation: DatabaseActionTypes.UPDATE,
template: {
"status": "{{status}}",
"updated_at": "{{updatedAt}}",
},
filterTemplate: {
"id": "{{userId}}",
},
});
// Execute
client.database.execute({
"database": "postgresdb",
"action": "update-user-status",
input: {
"userId": 123,
"status": "suspended",
updatedAt: new Date().toISOString(),
},
});
await ductape.database.action.create({
["name"] = "Update User Status",
["tag"] = "postgresdb:update-user-status",
["tableName"] = "users",
operation: DatabaseActionTypes.UPDATE,
template: {
["status"] = "{{status}}",
["updated_at"] = "{{updatedAt}}",
},
filterTemplate: {
["id"] = "{{userId}}",
},
});
// Execute
await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "update-user-status",
input: {
["userId"] = 123,
["status"] = "suspended",
updatedAt: DateTime.UtcNow.toISOString(),
},
});
DELETE - Remove Records
Delete records:
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Delete Old Sessions',
tag: 'postgresdb:delete-old-sessions',
tableName: 'sessions',
operation: DatabaseActionTypes.DELETE,
filterTemplate: {
expires_at: { $LT: '{{expirationDate}}' },
},
});
// Execute
await ductape.database.execute({
database: 'postgresdb',
action: 'delete-old-sessions',
input: {
expirationDate: new Date().toISOString(),
},
});
ductape.database.action.create(Map.of(
"name", "Delete Old Sessions",
"tag", "postgresdb:delete-old-sessions",
"tableName", "sessions",
operation: DatabaseActionTypes.DELETE,
filterTemplate: Map.of(
expires_at: Map.of( $"LT", "Map.of(Map.of(expirationDate))" )
)
));
// Execute
ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "delete-old-sessions",
input: Map.of(
expirationDate: Instant.now().toISOString()
)
));
client.database.action.create({
"name": "Delete Old Sessions",
"tag": "postgresdb:delete-old-sessions",
"tableName": "sessions",
operation: DatabaseActionTypes.DELETE,
filterTemplate: {
expires_at: { $"LT": "{{expirationDate}}" },
},
});
// Execute
client.database.execute({
"database": "postgresdb",
"action": "delete-old-sessions",
input: {
expirationDate: new Date().toISOString(),
},
});
await ductape.database.action.create({
["name"] = "Delete Old Sessions",
["tag"] = "postgresdb:delete-old-sessions",
["tableName"] = "sessions",
operation: DatabaseActionTypes.DELETE,
filterTemplate: {
expires_at: { $["LT"] = "{{expirationDate}}" },
},
});
// Execute
await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "delete-old-sessions",
input: {
expirationDate: DateTime.UtcNow.toISOString(),
},
});
UPSERT - Insert or Update
Insert new record or update if exists:
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Upsert User Preferences',
tag: 'postgresdb:upsert-user-prefs',
tableName: 'user_preferences',
operation: DatabaseActionTypes.UPSERT,
template: {
records: [
{
user_id: '{{userId}}',
theme: '{{theme}}',
notifications: '{{notifications}}',
updated_at: '{{updatedAt}}',
},
],
conflictColumns: ['user_id'],
},
});
// Execute
await ductape.database.execute({
database: 'postgresdb',
action: 'upsert-user-prefs',
input: {
userId: 123,
theme: 'dark',
notifications: true,
updatedAt: new Date().toISOString(),
},
});
ductape.database.action.create(Map.of(
"name", "Upsert User Preferences",
"tag", "postgresdb:upsert-user-prefs",
"tableName", "user_preferences",
operation: DatabaseActionTypes.UPSERT,
template: Map.of(
records: [
Map.of(
"user_id", "Map.of(Map.of(userId))",
"theme", "Map.of(Map.of(theme))",
"notifications", "Map.of(Map.of(notifications))",
"updated_at", "Map.of(Map.of(updatedAt))"
),
],
conflictColumns: ['user_id']
)
));
// Execute
ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "upsert-user-prefs",
input: Map.of(
"userId", 123,
"theme", "dark",
"notifications", true,
updatedAt: Instant.now().toISOString()
)
));
client.database.action.create({
"name": "Upsert User Preferences",
"tag": "postgresdb:upsert-user-prefs",
"tableName": "user_preferences",
operation: DatabaseActionTypes.UPSERT,
template: {
records: [
{
"user_id": "{{userId}}",
"theme": "{{theme}}",
"notifications": "{{notifications}}",
"updated_at": "{{updatedAt}}",
},
],
conflictColumns: ['user_id'],
},
});
// Execute
client.database.execute({
"database": "postgresdb",
"action": "upsert-user-prefs",
input: {
"userId": 123,
"theme": "dark",
"notifications": true,
updatedAt: new Date().toISOString(),
},
});
await ductape.database.action.create({
["name"] = "Upsert User Preferences",
["tag"] = "postgresdb:upsert-user-prefs",
["tableName"] = "user_preferences",
operation: DatabaseActionTypes.UPSERT,
template: {
records: [
{
["user_id"] = "{{userId}}",
["theme"] = "{{theme}}",
["notifications"] = "{{notifications}}",
["updated_at"] = "{{updatedAt}}",
},
],
conflictColumns: ['user_id'],
},
});
// Execute
await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "upsert-user-prefs",
input: {
["userId"] = 123,
["theme"] = "dark",
["notifications"] = true,
updatedAt: DateTime.UtcNow.toISOString(),
},
});
AGGREGATE - Aggregations
Perform aggregations using the operations object with $COUNT, $SUM, $AVG, $MIN, $MAX:
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'User Statistics',
tag: 'postgresdb:user-statistics',
tableName: 'users',
operation: DatabaseActionTypes.QUERY,
template: {
where: {
status: '{{status}}',
created_at: { $GTE: '{{startDate}}' },
},
operations: {
total_count: { $COUNT: '*' },
avg_age: { $AVG: 'age' },
min_age: { $MIN: 'age' },
max_age: { $MAX: 'age' },
},
},
});
// Execute
const result = await ductape.database.execute({
database: 'postgresdb',
action: 'user-statistics',
input: {
status: 'active',
startDate: '2024-01-01',
},
});
console.log('Statistics:', result);
// { total_count: 150, avg_age: 32.5, min_age: 18, max_age: 65 }
ductape.database.action.create(Map.of(
"name", "User Statistics",
"tag", "postgresdb:user-statistics",
"tableName", "users",
operation: DatabaseActionTypes.QUERY,
template: Map.of(
where: Map.of(
"status", "Map.of(Map.of(status))",
created_at: Map.of( $"GTE", "Map.of(Map.of(startDate))" )
),
operations: Map.of(
total_count: Map.of( $"COUNT", "*" ),
avg_age: Map.of( $"AVG", "age" ),
min_age: Map.of( $"MIN", "age" ),
max_age: Map.of( $"MAX", "age" )
)
)
));
// Execute
Map<String, Object> result = ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "user-statistics",
input: Map.of(
"status", "active",
"startDate", "2024-01-01"
)
));
System.out.println('Statistics:', result);
// Map.of( "total_count", 150, "avg_age", 32.5, "min_age", 18, "max_age", 65 )
client.database.action.create({
"name": "User Statistics",
"tag": "postgresdb:user-statistics",
"tableName": "users",
operation: DatabaseActionTypes.QUERY,
template: {
where: {
"status": "{{status}}",
created_at: { $"GTE": "{{startDate}}" },
},
operations: {
total_count: { $"COUNT": "*" },
avg_age: { $"AVG": "age" },
min_age: { $"MIN": "age" },
max_age: { $"MAX": "age" },
},
},
});
// Execute
result := client.database.execute({
"database": "postgresdb",
"action": "user-statistics",
input: {
"status": "active",
"startDate": "2024-01-01",
},
});
fmt.Println('Statistics:', result);
// { "total_count": 150, "avg_age": 32.5, "min_age": 18, "max_age": 65 }
await ductape.database.action.create({
["name"] = "User Statistics",
["tag"] = "postgresdb:user-statistics",
["tableName"] = "users",
operation: DatabaseActionTypes.QUERY,
template: {
where: {
["status"] = "{{status}}",
created_at: { $["GTE"] = "{{startDate}}" },
},
operations: {
total_count: { $["COUNT"] = "*" },
avg_age: { $["AVG"] = "age" },
min_age: { $["MIN"] = "age" },
max_age: { $["MAX"] = "age" },
},
},
});
// Execute
var result = await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "user-statistics",
input: {
["status"] = "active",
["startDate"] = "2024-01-01",
},
});
Console.WriteLine('Statistics:', result);
// { ["total_count"] = 150, ["avg_age"] = 32.5, ["min_age"] = 18, ["max_age"] = 65 }
Single Aggregation:
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Total Sales',
tag: 'postgresdb:total-sales',
tableName: 'orders',
operation: DatabaseActionTypes.QUERY,
template: {
where: {
status: 'completed',
created_at: {
$GTE: '{{startDate}}',
$LTE: '{{endDate}}',
},
},
operations: {
total_revenue: { $SUM: 'total' },
},
},
});
ductape.database.action.create(Map.of(
"name", "Total Sales",
"tag", "postgresdb:total-sales",
"tableName", "orders",
operation: DatabaseActionTypes.QUERY,
template: Map.of(
where: Map.of(
"status", "completed",
created_at: Map.of(
$"GTE", "Map.of(Map.of(startDate))",
$"LTE", "Map.of(Map.of(endDate))"
)
),
operations: Map.of(
total_revenue: Map.of( $"SUM", "total" )
)
)
));
client.database.action.create({
"name": "Total Sales",
"tag": "postgresdb:total-sales",
"tableName": "orders",
operation: DatabaseActionTypes.QUERY,
template: {
where: {
"status": "completed",
created_at: {
$"GTE": "{{startDate}}",
$"LTE": "{{endDate}}",
},
},
operations: {
total_revenue: { $"SUM": "total" },
},
},
});
await ductape.database.action.create({
["name"] = "Total Sales",
["tag"] = "postgresdb:total-sales",
["tableName"] = "orders",
operation: DatabaseActionTypes.QUERY,
template: {
where: {
["status"] = "completed",
created_at: {
$["GTE"] = "{{startDate}}",
$["LTE"] = "{{endDate}}",
},
},
operations: {
total_revenue: { $["SUM"] = "total" },
},
},
});
RAW_SQL - Custom Queries
Execute raw SQL:
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Complex User Report',
tag: 'postgresdb:complex-user-report',
tableName: 'users', // Required but not used for raw SQL
operation: DatabaseActionTypes.RAW_SQL,
template: {
query: `
SELECT
u.id,
u.name,
COUNT(o.id) as order_count,
SUM(o.total) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = $1
AND o.created_at >= $2
GROUP BY u.id, u.name
HAVING COUNT(o.id) > $3
ORDER BY total_spent DESC
LIMIT $4
`,
params: ['{{status}}', '{{startDate}}', '{{minOrders}}', '{{limit}}'],
},
});
// Execute
const report = await ductape.database.execute({
database: 'postgresdb',
action: 'complex-user-report',
input: {
status: 'active',
startDate: '2024-01-01',
minOrders: 5,
limit: 100,
},
});
ductape.database.action.create(Map.of(
"name", "Complex User Report",
"tag", "postgresdb:complex-user-report",
"tableName", "users", // Required but not used for raw SQL
operation: DatabaseActionTypes.RAW_SQL,
template: Map.of(
query: `
SELECT
u.id,
u.name,
COUNT(o.id) as order_count,
SUM(o.total) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = $1
AND o.created_at >= $2
GROUP BY u.id, u.name
HAVING COUNT(o.id) > $3
ORDER BY total_spent DESC
LIMIT $4
`,
params: ['Map.of(Map.of(status))', 'Map.of(Map.of(startDate))', 'Map.of(Map.of(minOrders))', 'Map.of(Map.of(limit))']
)
));
// Execute
Map<String, Object> report = ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "complex-user-report",
input: Map.of(
"status", "active",
"startDate", "2024-01-01",
"minOrders", 5,
"limit", 100
)
));
client.database.action.create({
"name": "Complex User Report",
"tag": "postgresdb:complex-user-report",
"tableName": "users", // Required but not used for raw SQL
operation: DatabaseActionTypes.RAW_SQL,
template: {
query: `
SELECT
u.id,
u.name,
COUNT(o.id) as order_count,
SUM(o.total) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = $1
AND o.created_at >= $2
GROUP BY u.id, u.name
HAVING COUNT(o.id) > $3
ORDER BY total_spent DESC
LIMIT $4
`,
params: ['{{status}}', '{{startDate}}', '{{minOrders}}', '{{limit}}'],
},
});
// Execute
report := client.database.execute({
"database": "postgresdb",
"action": "complex-user-report",
input: {
"status": "active",
"startDate": "2024-01-01",
"minOrders": 5,
"limit": 100,
},
});
await ductape.database.action.create({
["name"] = "Complex User Report",
["tag"] = "postgresdb:complex-user-report",
["tableName"] = "users", // Required but not used for raw SQL
operation: DatabaseActionTypes.RAW_SQL,
template: {
query: `
SELECT
u.id,
u.name,
COUNT(o.id) as order_count,
SUM(o.total) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = $1
AND o.created_at >= $2
GROUP BY u.id, u.name
HAVING COUNT(o.id) > $3
ORDER BY total_spent DESC
LIMIT $4
`,
params: ['{{status}}', '{{startDate}}', '{{minOrders}}', '{{limit}}'],
},
});
// Execute
var report = await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "complex-user-report",
input: {
["status"] = "active",
["startDate"] = "2024-01-01",
["minOrders"] = 5,
["limit"] = 100,
},
});
Variable Interpolation
Basic Placeholders
Use {{variableName}} syntax:
- TypeScript
- Java
- Go
- .NET
template: {
where: {
status: '{{status}}',
age: { $GT: '{{minAge}}' },
},
limit: '{{limit}}',
}
template: Map.of(
where: Map.of(
"status", "Map.of(Map.of(status))",
age: Map.of( $"GT", "Map.of(Map.of(minAge))" )
),
"limit", "Map.of(Map.of(limit))"
)
template: {
where: {
"status": "{{status}}",
age: { $"GT": "{{minAge}}" },
},
"limit": "{{limit}}",
}
template: {
where: {
["status"] = "{{status}}",
age: { $["GT"] = "{{minAge}}" },
},
["limit"] = "{{limit}}",
}
Nested Placeholders
Placeholders work in nested structures:
- TypeScript
- Java
- Go
- .NET
template: {
where: {
$OR: {
email: '{{email}}',
username: '{{username}}',
},
},
select: ['{{field1}}', '{{field2}}', '{{field3}}'],
}
template: Map.of(
where: Map.of(
$OR: Map.of(
"email", "Map.of(Map.of(email))",
"username", "Map.of(Map.of(username))"
)
),
select: ['Map.of(Map.of(field1))', 'Map.of(Map.of(field2))', 'Map.of(Map.of(field3))']
)
template: {
where: {
$OR: {
"email": "{{email}}",
"username": "{{username}}",
},
},
select: ['{{field1}}', '{{field2}}', '{{field3}}'],
}
template: {
where: {
$OR: {
["email"] = "{{email}}",
["username"] = "{{username}}",
},
},
select: ['{{field1}}', '{{field2}}', '{{field3}}'],
}
Array Placeholders
Use in arrays:
- TypeScript
- Java
- Go
- .NET
template: {
where: {
status: { $IN: ['{{status1}}', '{{status2}}', '{{status3}}'] },
},
}
template: Map.of(
where: Map.of(
status: Map.of( $IN: ['Map.of(Map.of(status1))', 'Map.of(Map.of(status2))', 'Map.of(Map.of(status3))'] )
)
)
template: {
where: {
status: { $IN: ['{{status1}}', '{{status2}}', '{{status3}}'] },
},
}
template: {
where: {
status: { $IN: ['{{status1}}', '{{status2}}', '{{status3}}'] },
},
}
Default Values
Provide defaults in input:
- TypeScript
- Java
- Go
- .NET
await ductape.database.execute({
database: 'postgresdb',
action: 'get-users',
input: {
limit: 50, // Default limit
offset: 0, // Default offset
},
});
ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "get-users",
input: Map.of(
"limit", 50, // Default limit
"offset", 0, // Default offset
)
));
client.database.execute({
"database": "postgresdb",
"action": "get-users",
input: {
"limit": 50, // Default limit
"offset": 0, // Default offset
},
});
await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "get-users",
input: {
["limit"] = 50, // Default limit
["offset"] = 0, // Default offset
},
});
Managing Actions
Create Action
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Action Name',
tag: 'database-tag:action-tag', // Format: database:action
tableName: 'table_name',
operation: DatabaseActionTypes.QUERY,
description: 'Optional description',
template: {
// Your query template
},
});
ductape.database.action.create(Map.of(
"name", "Action Name",
"tag", "database-tag:action-tag", // Format: database:action
"tableName", "table_name",
operation: DatabaseActionTypes.QUERY,
"description", "Optional description",
template: Map.of(
// Your query template
)
));
client.database.action.create({
"name": "Action Name",
"tag": "database-tag:action-tag", // Format: database:action
"tableName": "table_name",
operation: DatabaseActionTypes.QUERY,
"description": "Optional description",
template: {
// Your query template
},
});
await ductape.database.action.create({
["name"] = "Action Name",
["tag"] = "database-tag:action-tag", // Format: database:action
["tableName"] = "table_name",
operation: DatabaseActionTypes.QUERY,
["description"] = "Optional description",
template: {
// Your query template
},
});
Required Fields:
| Field | Type | Description |
|---|---|---|
name | string | Display name for the action |
tag | string | Unique identifier (format: database:action) |
tableName | string | Table to operate on |
operation | DatabaseActionTypes | Action operation (QUERY, INSERT, etc.) |
template | object | Query template with placeholders |
Optional Fields:
| Field | Type | Description |
|---|---|---|
description | string | Action description |
filterTemplate | object | Filter criteria (for UPDATE/DELETE) |
Update Action
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.update({
tag: 'postgresdb:get-users',
template: {
// Updated template
where: {
status: '{{status}}',
},
limit: '{{limit}}',
},
});
ductape.database.action.update(Map.of(
"tag", "postgresdb:get-users",
template: Map.of(
// Updated template
where: Map.of(
"status", "Map.of(Map.of(status))"
),
"limit", "Map.of(Map.of(limit))"
)
));
client.database.action.update({
"tag": "postgresdb:get-users",
template: {
// Updated template
where: {
"status": "{{status}}",
},
"limit": "{{limit}}",
},
});
await ductape.database.action.update({
["tag"] = "postgresdb:get-users",
template: {
// Updated template
where: {
["status"] = "{{status}}",
},
["limit"] = "{{limit}}",
},
});
Fetch Action
- TypeScript
- Java
- Go
- .NET
const action = await ductape.database.action.fetch('postgresdb:get-users');
console.log('Action:', action);
Map<String, Object> action = ductape.database.action.fetch('postgresdb:get-users');
System.out.println('Action:', action);
action := client.database.action.fetch('postgresdb:get-users');
fmt.Println('Action:', action);
var action = await ductape.database.action.fetch('postgresdb:get-users');
Console.WriteLine('Action:', action);
List Actions for Database
- TypeScript
- Java
- Go
- .NET
const actions = await ductape.database.action.fetchAll('postgresdb');
console.log(`Found ${actions.length} actions`);
actions.forEach(action => {
console.log(`${action.tag}: ${action.name} (${action.type})`);
});
Map<String, Object> actions = ductape.database.action.fetchAll('postgresdb');
System.out.println(`Found $Map.of(actions.length) actions`);
actions.forEach(action => Map.of(
System.out.println(`$Map.of(action.tag): $Map.of(action.name) ($Map.of(action.type))`);
));
actions := client.database.action.fetchAll('postgresdb');
fmt.Println(`Found ${actions.length} actions`);
actions.forEach(action => {
fmt.Println(`${action.tag}: ${action.name} (${action.type})`);
});
var actions = await ductape.database.action.fetchAll('postgresdb');
Console.WriteLine(`Found ${actions.length} actions`);
actions.forEach(action => {
Console.WriteLine(`${action.tag}: ${action.name} (${action.type})`);
});
Execute Actions
Basic Execution
- TypeScript
- Java
- Go
- .NET
const result = await ductape.database.execute({
database: 'postgresdb',
action: 'get-users',
input: {
status: 'active',
limit: 50,
},
});
Map<String, Object> result = ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "get-users",
input: Map.of(
"status", "active",
"limit", 50
)
));
result := client.database.execute({
"database": "postgresdb",
"action": "get-users",
input: {
"status": "active",
"limit": 50,
},
});
var result = await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "get-users",
input: {
["status"] = "active",
["limit"] = 50,
},
});
With Type Safety
- TypeScript
- Java
- Go
- .NET
interface User {
id: number;
name: string;
email: string;
status: string;
}
const users = await ductape.database.execute<User[]>({
database: 'postgresdb',
action: 'get-users',
input: { status: 'active' },
});
// TypeScript knows users is User[]
users.forEach(user => {
console.log(`${user.name} - ${user.email}`);
});
interface User Map.of(
id: number;
name: string;
email: string;
status: string;
)
Map<String, Object> users = ductape.database.execute<User[]>(Map.of(
"database", "postgresdb",
"action", "get-users",
input: Map.of( "status", "active" )
));
// TypeScript knows users is User[]
users.forEach(user => Map.of(
System.out.println(`$Map.of(user.name) - $Map.of(user.email)`);
));
interface User {
id: number;
name: string;
email: string;
status: string;
}
users := client.database.execute<User[]>({
"database": "postgresdb",
"action": "get-users",
input: { "status": "active" },
});
// TypeScript knows users is User[]
users.forEach(user => {
fmt.Println(`${user.name} - ${user.email}`);
});
interface User {
id: number;
name: string;
email: string;
status: string;
}
var users = await ductape.database.execute<User[]>({
["database"] = "postgresdb",
["action"] = "get-users",
input: { ["status"] = "active" },
});
// TypeScript knows users is User[]
users.forEach(user => {
Console.WriteLine(`${user.name} - ${user.email}`);
});
Error Handling
- TypeScript
- Java
- Go
- .NET
try {
const users = await ductape.database.execute({
database: 'postgresdb',
action: 'get-users',
input: { status: 'active' },
});
} catch (error) {
if (error.message.includes('not found')) {
console.error('Action not found');
} else if (error.message.includes('validation')) {
console.error('Invalid input parameters');
} else {
console.error('Execution failed:', error.message);
}
}
try Map.of(
Map<String, Object> users = ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "get-users",
input: Map.of( "status", "active" )
));
) catch (error) Map.of(
if (error.message.includes('not found')) Map.of(
console.error('Action not found');
) else if (error.message.includes('validation')) Map.of(
console.error('Invalid input parameters');
) else Map.of(
console.error('Execution failed:', error.message);
)
)
try {
users := client.database.execute({
"database": "postgresdb",
"action": "get-users",
input: { "status": "active" },
});
} catch (error) {
if (error.message.includes('not found')) {
console.error('Action not found');
} else if (error.message.includes('validation')) {
console.error('Invalid input parameters');
} else {
console.error('Execution failed:', error.message);
}
}
try {
var users = await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "get-users",
input: { ["status"] = "active" },
});
} catch (error) {
if (error.message.includes('not found')) {
console.error('Action not found');
} else if (error.message.includes('validation')) {
console.error('Invalid input parameters');
} else {
console.error('Execution failed:', error.message);
}
}
Common Patterns
Paginated Queries
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Get Products Paginated',
tag: 'postgresdb:get-products-paginated',
tableName: 'products',
operation: DatabaseActionTypes.QUERY,
template: {
where: {
category: '{{category}}',
in_stock: true,
},
select: ['id', 'name', 'price', 'stock'],
limit: '{{limit}}',
offset: '{{offset}}',
orderBy: [
{ column: '{{sortBy}}', order: '{{sortOrder}}' },
],
},
});
// Pagination helper
async function getProductsPage(
category: string,
page: number = 1,
pageSize: number = 20
) {
return ductape.database.execute({
database: 'postgresdb',
action: 'get-products-paginated',
input: {
category,
limit: pageSize,
offset: (page - 1) * pageSize,
sortBy: 'name',
sortOrder: 'ASC',
},
});
}
ductape.database.action.create(Map.of(
"name", "Get Products Paginated",
"tag", "postgresdb:get-products-paginated",
"tableName", "products",
operation: DatabaseActionTypes.QUERY,
template: Map.of(
where: Map.of(
"category", "Map.of(Map.of(category))",
"in_stock", true
),
select: ['id', 'name', 'price', 'stock'],
"limit", "Map.of(Map.of(limit))",
"offset", "Map.of(Map.of(offset))",
orderBy: [
Map.of( "column", "Map.of(Map.of(sortBy))", "order", "Map.of(Map.of(sortOrder))" ),
]
)
));
// Pagination helper
async function getProductsPage(
category: string,
page: number = 1,
pageSize: number = 20
) Map.of(
return ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "get-products-paginated",
input: Map.of(
category,
limit: pageSize,
offset: (page - 1) * pageSize,
"sortBy", "name",
"sortOrder", "ASC"
)
));
)
client.database.action.create({
"name": "Get Products Paginated",
"tag": "postgresdb:get-products-paginated",
"tableName": "products",
operation: DatabaseActionTypes.QUERY,
template: {
where: {
"category": "{{category}}",
"in_stock": true,
},
select: ['id', 'name', 'price', 'stock'],
"limit": "{{limit}}",
"offset": "{{offset}}",
orderBy: [
{ "column": "{{sortBy}}", "order": "{{sortOrder}}" },
],
},
});
// Pagination helper
async function getProductsPage(
category: string,
page: number = 1,
pageSize: number = 20
) {
return client.database.execute({
"database": "postgresdb",
"action": "get-products-paginated",
input: {
category,
limit: pageSize,
offset: (page - 1) * pageSize,
"sortBy": "name",
"sortOrder": "ASC",
},
});
}
await ductape.database.action.create({
["name"] = "Get Products Paginated",
["tag"] = "postgresdb:get-products-paginated",
["tableName"] = "products",
operation: DatabaseActionTypes.QUERY,
template: {
where: {
["category"] = "{{category}}",
["in_stock"] = true,
},
select: ['id', 'name', 'price', 'stock'],
["limit"] = "{{limit}}",
["offset"] = "{{offset}}",
orderBy: [
{ ["column"] = "{{sortBy}}", ["order"] = "{{sortOrder}}" },
],
},
});
// Pagination helper
async function getProductsPage(
category: string,
page: number = 1,
pageSize: number = 20
) {
return ductape.database.execute({
["database"] = "postgresdb",
["action"] = "get-products-paginated",
input: {
category,
limit: pageSize,
offset: (page - 1) * pageSize,
["sortBy"] = "name",
["sortOrder"] = "ASC",
},
});
}
Search Actions
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Search Users',
tag: 'postgresdb:search-users',
tableName: 'users',
operation: DatabaseActionTypes.QUERY,
template: {
where: {
$OR: {
name: { $CONTAINS: '{{query}}' },
email: { $CONTAINS: '{{query}}' },
},
status: 'active',
},
limit: '{{limit}}',
},
});
ductape.database.action.create(Map.of(
"name", "Search Users",
"tag", "postgresdb:search-users",
"tableName", "users",
operation: DatabaseActionTypes.QUERY,
template: Map.of(
where: Map.of(
$OR: Map.of(
name: Map.of( $"CONTAINS", "Map.of(Map.of(query))" ),
email: Map.of( $"CONTAINS", "Map.of(Map.of(query))" )
),
"status", "active"
),
"limit", "Map.of(Map.of(limit))"
)
));
client.database.action.create({
"name": "Search Users",
"tag": "postgresdb:search-users",
"tableName": "users",
operation: DatabaseActionTypes.QUERY,
template: {
where: {
$OR: {
name: { $"CONTAINS": "{{query}}" },
email: { $"CONTAINS": "{{query}}" },
},
"status": "active",
},
"limit": "{{limit}}",
},
});
await ductape.database.action.create({
["name"] = "Search Users",
["tag"] = "postgresdb:search-users",
["tableName"] = "users",
operation: DatabaseActionTypes.QUERY,
template: {
where: {
$OR: {
name: { $["CONTAINS"] = "{{query}}" },
email: { $["CONTAINS"] = "{{query}}" },
},
["status"] = "active",
},
["limit"] = "{{limit}}",
},
});
Batch Inserts
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Bulk Create Orders',
tag: 'postgresdb:bulk-create-orders',
tableName: 'orders',
operation: DatabaseActionTypes.INSERT,
template: {
records: '{{orders}}', // Array of order objects
},
});
// Execute with array
await ductape.database.execute({
database: 'postgresdb',
action: 'bulk-create-orders',
input: {
orders: [
{ user_id: 1, total: 99.99, status: 'pending' },
{ user_id: 2, total: 149.99, status: 'pending' },
{ user_id: 3, total: 79.99, status: 'pending' },
],
},
});
ductape.database.action.create(Map.of(
"name", "Bulk Create Orders",
"tag", "postgresdb:bulk-create-orders",
"tableName", "orders",
operation: DatabaseActionTypes.INSERT,
template: Map.of(
"records", "Map.of(Map.of(orders))", // Array of order objects
)
));
// Execute with array
ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "bulk-create-orders",
input: Map.of(
orders: [
Map.of( "user_id", 1, "total", 99.99, "status", "pending" ),
Map.of( "user_id", 2, "total", 149.99, "status", "pending" ),
Map.of( "user_id", 3, "total", 79.99, "status", "pending" ),
]
)
));
client.database.action.create({
"name": "Bulk Create Orders",
"tag": "postgresdb:bulk-create-orders",
"tableName": "orders",
operation: DatabaseActionTypes.INSERT,
template: {
"records": "{{orders}}", // Array of order objects
},
});
// Execute with array
client.database.execute({
"database": "postgresdb",
"action": "bulk-create-orders",
input: {
orders: [
{ "user_id": 1, "total": 99.99, "status": "pending" },
{ "user_id": 2, "total": 149.99, "status": "pending" },
{ "user_id": 3, "total": 79.99, "status": "pending" },
],
},
});
await ductape.database.action.create({
["name"] = "Bulk Create Orders",
["tag"] = "postgresdb:bulk-create-orders",
["tableName"] = "orders",
operation: DatabaseActionTypes.INSERT,
template: {
["records"] = "{{orders}}", // Array of order objects
},
});
// Execute with array
await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "bulk-create-orders",
input: {
orders: [
{ ["user_id"] = 1, ["total"] = 99.99, ["status"] = "pending" },
{ ["user_id"] = 2, ["total"] = 149.99, ["status"] = "pending" },
{ ["user_id"] = 3, ["total"] = 79.99, ["status"] = "pending" },
],
},
});
Conditional Updates
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Deactivate Inactive Users',
tag: 'postgresdb:deactivate-inactive-users',
tableName: 'users',
operation: DatabaseActionTypes.UPDATE,
template: {
status: 'inactive',
deactivated_at: '{{deactivatedAt}}',
},
filterTemplate: {
last_login: { $LT: '{{cutoffDate}}' },
status: 'active',
},
});
ductape.database.action.create(Map.of(
"name", "Deactivate Inactive Users",
"tag", "postgresdb:deactivate-inactive-users",
"tableName", "users",
operation: DatabaseActionTypes.UPDATE,
template: Map.of(
"status", "inactive",
"deactivated_at", "Map.of(Map.of(deactivatedAt))"
),
filterTemplate: Map.of(
last_login: Map.of( $"LT", "Map.of(Map.of(cutoffDate))" ),
"status", "active"
)
));
client.database.action.create({
"name": "Deactivate Inactive Users",
"tag": "postgresdb:deactivate-inactive-users",
"tableName": "users",
operation: DatabaseActionTypes.UPDATE,
template: {
"status": "inactive",
"deactivated_at": "{{deactivatedAt}}",
},
filterTemplate: {
last_login: { $"LT": "{{cutoffDate}}" },
"status": "active",
},
});
await ductape.database.action.create({
["name"] = "Deactivate Inactive Users",
["tag"] = "postgresdb:deactivate-inactive-users",
["tableName"] = "users",
operation: DatabaseActionTypes.UPDATE,
template: {
["status"] = "inactive",
["deactivated_at"] = "{{deactivatedAt}}",
},
filterTemplate: {
last_login: { $["LT"] = "{{cutoffDate}}" },
["status"] = "active",
},
});
Reporting Actions
Use aggregations with operations for analytics:
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Sales Summary',
tag: 'postgresdb:sales-summary',
tableName: 'orders',
operation: DatabaseActionTypes.AGGREGATE,
template: {
where: {
status: 'completed',
created_at: {
$GTE: '{{startDate}}',
$LTE: '{{endDate}}',
},
},
operations: {
total_revenue: { $SUM: 'total' },
order_count: { $COUNT: 'id' },
avg_order_value: { $AVG: 'total' },
highest_order: { $MAX: 'total' },
lowest_order: { $MIN: 'total' },
},
},
});
// Execute
const summary = await ductape.database.execute({
database: 'postgresdb',
action: 'sales-summary',
input: {
startDate: '2024-01-01',
endDate: '2024-12-31',
},
});
console.log('Summary:', summary);
// {
// total_revenue: 125000.50,
// order_count: 850,
// avg_order_value: 147.06,
// highest_order: 2500.00,
// lowest_order: 9.99
// }
ductape.database.action.create(Map.of(
"name", "Sales Summary",
"tag", "postgresdb:sales-summary",
"tableName", "orders",
operation: DatabaseActionTypes.AGGREGATE,
template: Map.of(
where: Map.of(
"status", "completed",
created_at: Map.of(
$"GTE", "Map.of(Map.of(startDate))",
$"LTE", "Map.of(Map.of(endDate))"
)
),
operations: Map.of(
total_revenue: Map.of( $"SUM", "total" ),
order_count: Map.of( $"COUNT", "id" ),
avg_order_value: Map.of( $"AVG", "total" ),
highest_order: Map.of( $"MAX", "total" ),
lowest_order: Map.of( $"MIN", "total" )
)
)
));
// Execute
Map<String, Object> summary = ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "sales-summary",
input: Map.of(
"startDate", "2024-01-01",
"endDate", "2024-12-31"
)
));
System.out.println('Summary:', summary);
// Map.of(
// "total_revenue", 125000.50,
// "order_count", 850,
// "avg_order_value", 147.06,
// "highest_order", 2500.00,
// "lowest_order", 9.99
// )
client.database.action.create({
"name": "Sales Summary",
"tag": "postgresdb:sales-summary",
"tableName": "orders",
operation: DatabaseActionTypes.AGGREGATE,
template: {
where: {
"status": "completed",
created_at: {
$"GTE": "{{startDate}}",
$"LTE": "{{endDate}}",
},
},
operations: {
total_revenue: { $"SUM": "total" },
order_count: { $"COUNT": "id" },
avg_order_value: { $"AVG": "total" },
highest_order: { $"MAX": "total" },
lowest_order: { $"MIN": "total" },
},
},
});
// Execute
summary := client.database.execute({
"database": "postgresdb",
"action": "sales-summary",
input: {
"startDate": "2024-01-01",
"endDate": "2024-12-31",
},
});
fmt.Println('Summary:', summary);
// {
// "total_revenue": 125000.50,
// "order_count": 850,
// "avg_order_value": 147.06,
// "highest_order": 2500.00,
// "lowest_order": 9.99
// }
await ductape.database.action.create({
["name"] = "Sales Summary",
["tag"] = "postgresdb:sales-summary",
["tableName"] = "orders",
operation: DatabaseActionTypes.AGGREGATE,
template: {
where: {
["status"] = "completed",
created_at: {
$["GTE"] = "{{startDate}}",
$["LTE"] = "{{endDate}}",
},
},
operations: {
total_revenue: { $["SUM"] = "total" },
order_count: { $["COUNT"] = "id" },
avg_order_value: { $["AVG"] = "total" },
highest_order: { $["MAX"] = "total" },
lowest_order: { $["MIN"] = "total" },
},
},
});
// Execute
var summary = await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "sales-summary",
input: {
["startDate"] = "2024-01-01",
["endDate"] = "2024-12-31",
},
});
Console.WriteLine('Summary:', summary);
// {
// ["total_revenue"] = 125000.50,
// ["order_count"] = 850,
// ["avg_order_value"] = 147.06,
// ["highest_order"] = 2500.00,
// ["lowest_order"] = 9.99
// }
Best Practices
1. Use Descriptive Names
- TypeScript
- Java
- Go
- .NET
// Good - clear purpose
await ductape.database.action.create({
name: 'Get Active Users Created This Month',
tag: 'postgresdb:get-active-users-this-month',
// ...
});
// ❌ Avoid - vague
await ductape.database.action.create({
name: 'Get Users',
tag: 'postgresdb:users',
// ...
});
// Good - clear purpose
ductape.database.action.create(Map.of(
"name", "Get Active Users Created This Month",
"tag", "postgresdb:get-active-users-this-month",
// ...
));
// ❌ Avoid - vague
ductape.database.action.create(Map.of(
"name", "Get Users",
"tag", "postgresdb:users",
// ...
));
// Good - clear purpose
client.database.action.create({
"name": "Get Active Users Created This Month",
"tag": "postgresdb:get-active-users-this-month",
// ...
});
// ❌ Avoid - vague
client.database.action.create({
"name": "Get Users",
"tag": "postgresdb:users",
// ...
});
// Good - clear purpose
await ductape.database.action.create({
["name"] = "Get Active Users Created This Month",
["tag"] = "postgresdb:get-active-users-this-month",
// ...
});
// ❌ Avoid - vague
await ductape.database.action.create({
["name"] = "Get Users",
["tag"] = "postgresdb:users",
// ...
});
2. Add Descriptions
- TypeScript
- Java
- Go
- .NET
await ductape.database.action.create({
name: 'Get Overdue Invoices',
tag: 'postgresdb:get-overdue-invoices',
description: 'Fetches all unpaid invoices past their due date for reminder emails',
// ...
});
ductape.database.action.create(Map.of(
"name", "Get Overdue Invoices",
"tag", "postgresdb:get-overdue-invoices",
"description", "Fetches all unpaid invoices past their due date for reminder emails",
// ...
));
client.database.action.create({
"name": "Get Overdue Invoices",
"tag": "postgresdb:get-overdue-invoices",
"description": "Fetches all unpaid invoices past their due date for reminder emails",
// ...
});
await ductape.database.action.create({
["name"] = "Get Overdue Invoices",
["tag"] = "postgresdb:get-overdue-invoices",
["description"] = "Fetches all unpaid invoices past their due date for reminder emails",
// ...
});
3. Validate Input
- TypeScript
- Java
- Go
- .NET
function validateInput(input: any) {
if (!input.limit || input.limit > 1000) {
throw new Error('Limit must be between 1 and 1000');
}
if (input.offset < 0) {
throw new Error('Offset must be non-negative');
}
}
const users = await ductape.database.execute({
database: 'postgresdb',
action: 'get-users',
input: validateInput(userInput),
});
function validateInput(input: any) Map.of(
if (!input.limit || input.limit > 1000) Map.of(
throw new Error('Limit must be between 1 and 1000');
)
if (input.offset < 0) Map.of(
throw new Error('Offset must be non-negative');
)
)
Map<String, Object> users = ductape.database.execute(Map.of(
"database", "postgresdb",
"action", "get-users",
input: validateInput(userInput)
));
function validateInput(input: any) {
if (!input.limit || input.limit > 1000) {
throw new Error('Limit must be between 1 and 1000');
}
if (input.offset < 0) {
throw new Error('Offset must be non-negative');
}
}
users := client.database.execute({
"database": "postgresdb",
"action": "get-users",
input: validateInput(userInput),
});
function validateInput(input: any) {
if (!input.limit || input.limit > 1000) {
throw new Error('Limit must be between 1 and 1000');
}
if (input.offset < 0) {
throw new Error('Offset must be non-negative');
}
}
var users = await ductape.database.execute({
["database"] = "postgresdb",
["action"] = "get-users",
input: validateInput(userInput),
});
4. Use Consistent Naming
- TypeScript
- Java
- Go
- .NET
// Good - consistent pattern
'postgresdb:get-users'
'postgresdb:create-user'
'postgresdb:update-user'
'postgresdb:delete-user'
// ❌ Avoid - inconsistent
'postgresdb:getUsers'
'postgresdb:user-create'
'postgresdb:UpdateUser'
'postgresdb:del_user'
// Good - consistent pattern
'postgresdb:get-users'
'postgresdb:create-user'
'postgresdb:update-user'
'postgresdb:delete-user'
// ❌ Avoid - inconsistent
'postgresdb:getUsers'
'postgresdb:user-create'
'postgresdb:UpdateUser'
'postgresdb:del_user'
// Good - consistent pattern
'postgresdb:get-users'
'postgresdb:create-user'
'postgresdb:update-user'
'postgresdb:delete-user'
// ❌ Avoid - inconsistent
'postgresdb:getUsers'
'postgresdb:user-create'
'postgresdb:UpdateUser'
'postgresdb:del_user'
// Good - consistent pattern
'postgresdb:get-users'
'postgresdb:create-user'
'postgresdb:update-user'
'postgresdb:delete-user'
// ❌ Avoid - inconsistent
'postgresdb:getUsers'
'postgresdb:user-create'
'postgresdb:UpdateUser'
'postgresdb:del_user'
5. Keep Actions Focused
- TypeScript
- Java
- Go
- .NET
// Good - single purpose
await ductape.database.action.create({
name: 'Get User Orders',
tag: 'postgresdb:get-user-orders',
operation: DatabaseActionTypes.QUERY,
// ... fetch only orders
});
// ❌ Avoid - too complex
await ductape.database.action.create({
name: 'Get User Complete Profile',
tag: 'postgresdb:get-user-profile',
operation: DatabaseActionTypes.RAW_SQL,
template: {
query: `
SELECT users.*, orders.*, addresses.*, payments.*
FROM users
LEFT JOIN orders ...
LEFT JOIN addresses ...
LEFT JOIN payments ...
-- Too many joins, should be separate actions
`,
},
});
// Good - single purpose
ductape.database.action.create(Map.of(
"name", "Get User Orders",
"tag", "postgresdb:get-user-orders",
operation: DatabaseActionTypes.QUERY,
// ... fetch only orders
));
// ❌ Avoid - too complex
ductape.database.action.create(Map.of(
"name", "Get User Complete Profile",
"tag", "postgresdb:get-user-profile",
operation: DatabaseActionTypes.RAW_SQL,
template: Map.of(
query: `
SELECT users.*, orders.*, addresses.*, payments.*
FROM users
LEFT JOIN orders ...
LEFT JOIN addresses ...
LEFT JOIN payments ...
-- Too many joins, should be separate actions
`
)
));
// Good - single purpose
client.database.action.create({
"name": "Get User Orders",
"tag": "postgresdb:get-user-orders",
operation: DatabaseActionTypes.QUERY,
// ... fetch only orders
});
// ❌ Avoid - too complex
client.database.action.create({
"name": "Get User Complete Profile",
"tag": "postgresdb:get-user-profile",
operation: DatabaseActionTypes.RAW_SQL,
template: {
query: `
SELECT users.*, orders.*, addresses.*, payments.*
FROM users
LEFT JOIN orders ...
LEFT JOIN addresses ...
LEFT JOIN payments ...
-- Too many joins, should be separate actions
`,
},
});
// Good - single purpose
await ductape.database.action.create({
["name"] = "Get User Orders",
["tag"] = "postgresdb:get-user-orders",
operation: DatabaseActionTypes.QUERY,
// ... fetch only orders
});
// ❌ Avoid - too complex
await ductape.database.action.create({
["name"] = "Get User Complete Profile",
["tag"] = "postgresdb:get-user-profile",
operation: DatabaseActionTypes.RAW_SQL,
template: {
query: `
SELECT users.*, orders.*, addresses.*, payments.*
FROM users
LEFT JOIN orders ...
LEFT JOIN addresses ...
LEFT JOIN payments ...
-- Too many joins, should be separate actions
`,
},
});
6. Limit Result Sets
- TypeScript
- Java
- Go
- .NET
// Always include limit
template: {
where: { status: '{{status}}' },
limit: '{{limit}}', // Prevent unbounded queries
}
// Always include limit
template: Map.of(
where: Map.of( "status", "Map.of(Map.of(status))" ),
"limit", "Map.of(Map.of(limit))", // Prevent unbounded queries
)
// Always include limit
template: {
where: { "status": "{{status}}" },
"limit": "{{limit}}", // Prevent unbounded queries
}
// Always include limit
template: {
where: { ["status"] = "{{status}}" },
["limit"] = "{{limit}}", // Prevent unbounded queries
}
7. Test Actions
- TypeScript
- Java
- Go
- .NET
// Test action with various inputs
describe('get-users-paginated', () => {
it('should fetch first page', async () => {
const users = await ductape.database.execute({
database: 'testdb',
action: 'get-users-paginated',
input: { limit: 10, offset: 0 },
});
expect(users).toHaveLength(10);
});
it('should respect offset', async () => {
const page2 = await ductape.database.execute({
database: 'testdb',
action: 'get-users-paginated',
input: { limit: 10, offset: 10 },
});
expect(page2[0].id).toBeGreaterThan(10);
});
});
// Test action with various inputs
describe('get-users-paginated', () => Map.of(
it('should fetch first page', async () => Map.of(
Map<String, Object> users = ductape.database.execute(Map.of(
"database", "testdb",
"action", "get-users-paginated",
input: Map.of( "limit", 10, "offset", 0 )
));
expect(users).toHaveLength(10);
));
it('should respect offset', async () => Map.of(
Map<String, Object> page2 = ductape.database.execute(Map.of(
"database", "testdb",
"action", "get-users-paginated",
input: Map.of( "limit", 10, "offset", 10 )
));
expect(page2[0].id).toBeGreaterThan(10);
));
));
// Test action with various inputs
describe('get-users-paginated', () => {
it('should fetch first page', async () => {
users := client.database.execute({
"database": "testdb",
"action": "get-users-paginated",
input: { "limit": 10, "offset": 0 },
});
expect(users).toHaveLength(10);
});
it('should respect offset', async () => {
page2 := client.database.execute({
"database": "testdb",
"action": "get-users-paginated",
input: { "limit": 10, "offset": 10 },
});
expect(page2[0].id).toBeGreaterThan(10);
});
});
// Test action with various inputs
describe('get-users-paginated', () => {
it('should fetch first page', async () => {
var users = await ductape.database.execute({
["database"] = "testdb",
["action"] = "get-users-paginated",
input: { ["limit"] = 10, ["offset"] = 0 },
});
expect(users).toHaveLength(10);
});
it('should respect offset', async () => {
var page2 = await ductape.database.execute({
["database"] = "testdb",
["action"] = "get-users-paginated",
input: { ["limit"] = 10, ["offset"] = 10 },
});
expect(page2[0].id).toBeGreaterThan(10);
});
});
Next Steps
- Querying Data - Direct database queries
- Writing Data - Insert, update, delete operations
- Transactions - Ensure data consistency
- Best Practices - Database optimization
See Also
- Database Overview - Getting started with databases
- Direct Queries - Execute raw SQL
- Aggregations - Complex data analysis