Modules vs Scripts
Understanding execution models in code mode
The do tool embraces code mode - AI models write actual TypeScript code against a complete API rather than constrained tool-calling formats.
Core Philosophy
"LLMs have seen a lot of code. They have not seen a lot of 'tool calls'."
AI models are fundamentally trained on code. They understand APIs, type signatures, and programming patterns deeply. Code mode leverages this native capability instead of forcing models into synthetic JSON tool schemas.
This document explains the two execution models available in the do tool: scripts and modules. Understanding when to use each is essential for building effective Business-as-Code applications.
Execution Models Overview
The do tool supports two distinct execution models:
| Aspect | Scripts | Modules |
|---|---|---|
| Execution Model | Imperative, sequential | Declarative, persistent |
| Return Value | Returns final expression | No return value |
| Lifecycle | Execute once, then complete | Register handlers, run indefinitely |
| Primary Use | Queries, transformations, one-time operations | Event handling, scheduled tasks, workflows |
| State | Ephemeral execution context | Persistent subscriptions |
| Composition | Via return values and chaining | Via event patterns and queues |
Deep Dive: Scripts
Scripts are imperative programs that execute sequentially and return a result. They represent the traditional programming model that AI models understand deeply.
Imperative Execution Model
Scripts execute statements in order, top to bottom. Each statement builds on previous results, creating a clear data flow:
// Step 1: Query database
const customer = await db.get('Person', 'per_123')
// Step 2: Transform data
const orderCount = await db
.list('Order', {
where: { customerId: customer.id },
})
.then((orders) => orders.length)
// Step 3: Calculate derived value
const segment = orderCount > 10 ? 'VIP' : orderCount > 5 ? 'Regular' : 'New'
// Step 4: Return result
{
customer: (customer.name, orderCount, segment)
}Key Characteristics:
- Sequential Execution: Each line executes in order
- Explicit Control Flow: Clear if/else, loops, try/catch
- Local Variables: Build up state during execution
- Single Result: Returns one final value
Return Value Semantics
The last expression in a script becomes its return value. This value is sent back to the AI model for further processing.
// Implicit return - last expression
await db.list('Business')
// Explicit return - use return statement
const businesses = await db.list('Business')
return businesses.filter((b) => b.revenue > 1000000)
// Complex return - computed object
const orders = await db.list('Order')
const revenue = orders.reduce(
(sum, o) => sum + o.total,
0
)({
totalOrders: orders.length,
totalRevenue: revenue,
avgOrderValue: revenue / orders.length,
})Return Value Best Practices:
- Return structured data that AI models can interpret
- Include context (labels, metadata) not just raw values
- Use objects with descriptive keys over bare arrays
- Return errors as structured objects, not thrown exceptions
Script Composition Patterns
Scripts compose through return values and sequential chaining:
Pattern 1: Data Pipeline
// Fetch → Transform → Aggregate → Return
const orders = await db.list('Order', {
where: { status: 'completed' },
})
const enriched = await Promise.all(
orders.map(async (order) => ({
...order,
customer: await db.get('Person', order.customerId),
itemCount: order.items.length,
}))
)
const summary = {
totalOrders: enriched.length,
totalRevenue: enriched.reduce((sum, o) => sum + o.total, 0),
uniqueCustomers: new Set(enriched.map((o) => o.customerId)).size,
avgItemsPerOrder: enriched.reduce((sum, o) => sum + o.itemCount, 0) / enriched.length,
}
summaryPattern 2: Conditional Logic
// Check condition → Branch → Execute → Return
const user = user.current()
if (!user) {
return { error: 'Authentication required' }
}
if (!user.can('read', 'Order')) {
return { error: 'Permission denied' }
}
const orders = await db.list('Order', {
where: { customerId: user.id }
})
{ orders, count: orders.length }Pattern 3: Error Handling
// Try operation → Catch errors → Return status
try {
const customer = await db.create('Person', {
name: 'Alice',
email: '[email protected]',
})
const order = await db.create('Order', {
customer,
items: [$.Product({ name: 'Widget', price: 29.99 })],
total: 29.99,
})
await send($.Email.send, {
to: customer.email,
subject: 'Order Confirmation',
body: `Your order #${order.id} has been confirmed`,
})
return {
success: true,
orderId: order.id,
customerId: customer.id,
}
} catch (error) {
return {
success: false,
error: error.message,
step: error.context?.step || 'unknown',
}
}When Scripts Are The Right Choice
Use scripts when you need:
- Immediate Results: Return data to the AI model for processing
- One-Time Operations: Execute once and complete
- Data Queries: Fetch and transform data
- Analysis: Calculate metrics and generate reports
- Testing: Run quick tests of API functionality
- Debugging: Inspect state and verify behavior
Script Use Cases:
- Database queries and filtering
- Data transformations and aggregations
- Report generation
- API integrations
- One-time data migrations
- Testing and verification
- Ad-hoc administrative tasks
Script Example 1: Customer Analysis
// Analyze customer purchase patterns
const customers = await db.list('Person')
const analysis = await Promise.all(
customers.map(async (customer) => {
const orders = await db.list('Order', {
where: { customerId: customer.id },
})
if (orders.length === 0) {
return null // Skip customers with no orders
}
const totalSpent = orders.reduce((sum, o) => sum + (o.total || 0), 0)
const avgOrderValue = totalSpent / orders.length
const daysSinceLastOrder = orders.length > 0 ? Math.floor((Date.now() - new Date(orders[0].createdAt).getTime()) / (1000 * 60 * 60 * 24)) : null
return {
customerId: customer.id,
name: customer.name,
email: customer.email,
orderCount: orders.length,
totalSpent,
avgOrderValue,
daysSinceLastOrder,
segment: totalSpent > 1000 ? 'VIP' : totalSpent > 500 ? 'Premium' : 'Regular',
}
})
)
// Filter out nulls and sort by total spent
analysis
.filter((a) => a !== null)
.sort((a, b) => b.totalSpent - a.totalSpent)
.slice(0, 20) // Top 20 customersScript Example 2: Inventory Report
// Generate low stock inventory report
const products = await db.list('Product', {
where: {
stock: { lt: 20 }, // Less than 20 units
},
sort: { stock: 'asc' },
})
const report = {
generatedAt: new Date().toISOString(),
totalProducts: products.length,
criticalStock: products.filter((p) => p.stock < 5).length,
lowStock: products.filter((p) => p.stock >= 5 && p.stock < 10).length,
moderateStock: products.filter((p) => p.stock >= 10 && p.stock < 20).length,
products: products.map((p) => ({
id: p.id,
name: p.name,
sku: p.sku,
currentStock: p.stock,
reorderLevel: 50,
reorderQuantity: 50 - p.stock,
status: p.stock < 5 ? 'CRITICAL' : p.stock < 10 ? 'LOW' : 'MODERATE',
estimatedValue: p.stock * p.price,
})),
totalValue: products.reduce((sum, p) => sum + p.stock * p.price, 0),
reorderCost: products.reduce((sum, p) => sum + (50 - p.stock) * p.price, 0),
}
// Send report via email
await send($.Email.send, {
to: '[email protected]',
subject: `Low Stock Alert - ${report.totalProducts} products`,
body: JSON.stringify(report, null, 2),
})
reportScript Example 3: Revenue Analysis
// Calculate monthly revenue trends
const startDate = new Date()
startDate.setMonth(startDate.getMonth() - 12) // Last 12 months
const orders = await db.list('Order', {
where: {
createdAt: { gte: startDate.toISOString() },
status: 'completed'
}
})
// Group by month
const monthlyRevenue = orders.reduce((acc, order) => {
const month = new Date(order.createdAt).toISOString().substring(0, 7) // YYYY-MM
if (!acc[month]) {
acc[month] = {
month,
orders: 0,
revenue: 0,
customers: new Set()
}
}
acc[month].orders++
acc[month].revenue += order.total || 0
acc[month].customers.add(order.customerId)
return acc
}, {})
// Convert to array and calculate trends
const months = Object.values(monthlyRevenue).map((m: any) => ({
month: m.month,
orders: m.orders,
revenue: m.revenue,
customers: m.customers.size,
avgOrderValue: m.revenue / m.orders
}))
// Sort by month
months.sort((a, b) => a.month.localeCompare(b.month))
// Calculate growth rates
const withGrowth = months.map((m, i) => {
if (i === 0) return { ...m, revenueGrowth: 0, orderGrowth: 0 }
const prev = months[i - 1]
return {
...m,
revenueGrowth: ((m.revenue - prev.revenue) / prev.revenue * 100).toFixed(2) + '%',
orderGrowth: ((m.orders - prev.orders) / prev.orders * 100).toFixed(2) + '%'
}
})
{
summary: {
totalMonths: withGrowth.length,
totalRevenue: withGrowth.reduce((sum, m) => sum + m.revenue, 0),
totalOrders: withGrowth.reduce((sum, m) => sum + m.orders, 0),
avgMonthlyRevenue: withGrowth.reduce((sum, m) => sum + m.revenue, 0) / withGrowth.length,
trend: withGrowth[withGrowth.length - 1].revenue > withGrowth[0].revenue ? 'growing' : 'declining'
},
months: withGrowth
}Script Example 4: Data Migration
// One-time migration: Add missing email domains
const persons = await db.list('Person', {
where: { emailDomain: null }
})
const results = []
const errors = []
for (const person of persons) {
try {
if (!person.email) {
errors.push({ id: person.id, reason: 'Missing email' })
continue
}
const domain = person.email.split('@')[1]
if (!domain) {
errors.push({ id: person.id, reason: 'Invalid email format' })
continue
}
await db.update('Person', person.id, {
emailDomain: domain
})
results.push({ id: person.id, domain })
} catch (error) {
errors.push({ id: person.id, reason: error.message })
}
}
{
processed: results.length,
failed: errors.length,
successRate: (results.length / persons.length * 100).toFixed(2) + '%',
results: results.slice(0, 10),
errors: errors.slice(0, 10)
}Script Example 5: API Integration
// Fetch data from external API and sync to database
const response = await api.fetch('https://api.example.com/products', {
headers: { 'Authorization': 'Bearer token123' }
})
const externalProducts = await response.json()
const syncResults = await Promise.all(
externalProducts.map(async (extProduct) => {
// Check if product exists
const existing = await db.list('Product', {
where: { externalId: extProduct.id }
})
if (existing.length > 0) {
// Update existing
const updated = await db.update('Product', existing[0].id, {
name: extProduct.name,
price: extProduct.price,
stock: extProduct.inventory,
lastSyncedAt: new Date().toISOString()
})
return { action: 'updated', id: updated.id }
} else {
// Create new
const created = await db.create('Product', {
externalId: extProduct.id,
name: extProduct.name,
price: extProduct.price,
stock: extProduct.inventory,
lastSyncedAt: new Date().toISOString()
})
return { action: 'created', id: created.id }
}
})
)
{
synced: syncResults.length,
created: syncResults.filter(r => r.action === 'created').length,
updated: syncResults.filter(r => r.action === 'updated').length,
timestamp: new Date().toISOString()
}Script Example 6: Order Fulfillment Check
// Check orders ready for fulfillment
const paidOrders = await db.list('Order', {
where: {
status: 'paid',
fulfilledAt: null
}
})
const readyToFulfill = await Promise.all(
paidOrders.map(async (order) => {
// Check inventory for all items
const inventoryChecks = await Promise.all(
order.items.map(async (item) => {
const product = await db.get('Product', item.productId)
return {
productId: item.productId,
productName: product.name,
required: item.quantity,
available: product.stock,
sufficient: product.stock >= item.quantity
}
})
)
const allAvailable = inventoryChecks.every(check => check.sufficient)
return {
orderId: order.id,
orderNumber: order.orderNumber,
customerId: order.customerId,
total: order.total,
readyToFulfill: allAvailable,
inventoryChecks
}
})
)
{
totalOrders: paidOrders.length,
readyToFulfill: readyToFulfill.filter(o => o.readyToFulfill).length,
blockedByInventory: readyToFulfill.filter(o => !o.readyToFulfill).length,
orders: readyToFulfill
}Script Example 7: Customer Churn Analysis
// Identify customers at risk of churning
const allCustomers = await db.list('Person')
const churnAnalysis = await Promise.all(
allCustomers.map(async (customer) => {
const orders = await db.list('Order', {
where: { customerId: customer.id },
sort: { createdAt: 'desc' }
})
if (orders.length === 0) {
return null
}
const lastOrderDate = new Date(orders[0].createdAt)
const daysSinceLastOrder = Math.floor((Date.now() - lastOrderDate.getTime()) / (1000 * 60 * 60 * 24))
// Calculate average days between orders
const orderDates = orders.map(o => new Date(o.createdAt).getTime()).sort((a, b) => a - b)
const intervals = []
for (let i = 1; i < orderDates.length; i++) {
intervals.push((orderDates[i] - orderDates[i - 1]) / (1000 * 60 * 60 * 24))
}
const avgDaysBetweenOrders = intervals.length > 0
? intervals.reduce((sum, i) => sum + i, 0) / intervals.length
: null
// Churn risk scoring
let churnRisk = 'low'
if (avgDaysBetweenOrders && daysSinceLastOrder > avgDaysBetweenOrders * 2) {
churnRisk = 'high'
} else if (avgDaysBetweenOrders && daysSinceLastOrder > avgDaysBetweenOrders * 1.5) {
churnRisk = 'medium'
}
return {
customerId: customer.id,
name: customer.name,
email: customer.email,
orderCount: orders.length,
lastOrderDate: lastOrderDate.toISOString(),
daysSinceLastOrder,
avgDaysBetweenOrders: avgDaysBetweenOrders?.toFixed(1),
churnRisk,
needsAttention: churnRisk !== 'low'
}
})
)
// Filter and sort by risk
const atRisk = churnAnalysis
.filter(a => a !== null && a.needsAttention)
.sort((a, b) => {
if (a.churnRisk === 'high' && b.churnRisk !== 'high') return -1
if (a.churnRisk !== 'high' && b.churnRisk === 'high') return 1
return b.daysSinceLastOrder - a.daysSinceLastOrder
})
{
totalCustomers: allCustomers.length,
analyzed: churnAnalysis.filter(a => a !== null).length,
atRisk: atRisk.length,
highRisk: atRisk.filter(a => a.churnRisk === 'high').length,
mediumRisk: atRisk.filter(a => a.churnRisk === 'medium').length,
customers: atRisk.slice(0, 20) // Top 20 at-risk customers
}Script Example 8: Product Performance Report
// Analyze product performance metrics
const products = await db.list('Product')
const performance = await Promise.all(
products.map(async (product) => {
// Get all orders containing this product
const allOrders = await db.list('Order', {
where: { status: 'completed' }
})
const ordersWithProduct = allOrders.filter(order =>
order.items?.some(item => item.productId === product.id)
)
const totalQuantitySold = ordersWithProduct.reduce((sum, order) => {
const item = order.items.find(i => i.productId === product.id)
return sum + (item?.quantity || 0)
}, 0)
const totalRevenue = ordersWithProduct.reduce((sum, order) => {
const item = order.items.find(i => i.productId === product.id)
return sum + ((item?.quantity || 0) * (item?.price || product.price))
}, 0)
return {
productId: product.id,
name: product.name,
sku: product.sku,
currentStock: product.stock,
price: product.price,
orderCount: ordersWithProduct.length,
quantitySold: totalQuantitySold,
revenue: totalRevenue,
avgQuantityPerOrder: ordersWithProduct.length > 0
? totalQuantitySold / ordersWithProduct.length
: 0,
turnoverRate: product.stock > 0
? (totalQuantitySold / product.stock * 100).toFixed(2) + '%'
: 'N/A'
}
})
)
// Sort by revenue
const topPerformers = performance
.sort((a, b) => b.revenue - a.revenue)
.slice(0, 20)
const bottomPerformers = performance
.sort((a, b) => a.revenue - b.revenue)
.slice(0, 10)
{
totalProducts: products.length,
totalRevenue: performance.reduce((sum, p) => sum + p.revenue, 0),
totalQuantitySold: performance.reduce((sum, p) => sum + p.quantitySold, 0),
topPerformers,
bottomPerformers,
needsAttention: performance.filter(p => p.quantitySold === 0)
}Script Example 9: Email Campaign Preparation
// Prepare targeted email campaign data
const segment = 'VIP' // Target VIP customers
const customers = await db.list('Person', {
where: { segment }
})
const campaignData = await Promise.all(
customers.map(async (customer) => {
// Get recent orders
const recentOrders = await db.list('Order', {
where: { customerId: customer.id },
sort: { createdAt: 'desc' },
limit: 5
})
// Get favorite products (most purchased)
const productCounts = {}
recentOrders.forEach(order => {
order.items?.forEach(item => {
productCounts[item.productId] = (productCounts[item.productId] || 0) + item.quantity
})
})
const topProductIds = Object.entries(productCounts)
.sort(([, a], [, b]) => (b as number) - (a as number))
.slice(0, 3)
.map(([id]) => id)
const favoriteProducts = await Promise.all(
topProductIds.map(id => db.get('Product', id))
)
return {
email: customer.email,
name: customer.name,
segment: customer.segment,
orderCount: recentOrders.length,
totalSpent: recentOrders.reduce((sum, o) => sum + o.total, 0),
lastOrderDate: recentOrders[0]?.createdAt,
favoriteProducts: favoriteProducts.map(p => p.name),
personalization: {
greeting: `Hi ${customer.name}`,
loyaltyMessage: `You've placed ${recentOrders.length} orders with us!`,
recommendation: `Based on your purchases, you might like our new ${favoriteProducts[0]?.name} collection`
}
}
})
)
{
campaignName: 'VIP Exclusive - Fall Collection',
targetSegment: segment,
recipientCount: campaignData.length,
estimatedReach: campaignData.length,
recipients: campaignData.slice(0, 10), // Preview first 10
fullDataAvailable: true
}Script Example 10: Database Health Check
// Perform database integrity and health checks
const checks = {
timestamp: new Date().toISOString(),
checks: []
}
// Check 1: Orders without customers
const ordersWithoutCustomers = await db.list('Order')
const orphanedOrders = []
for (const order of ordersWithoutCustomers) {
const customer = await db.get('Person', order.customerId)
if (!customer) {
orphanedOrders.push(order.id)
}
}
checks.checks.push({
name: 'Orders without customers',
status: orphanedOrders.length === 0 ? 'PASS' : 'FAIL',
count: orphanedOrders.length,
samples: orphanedOrders.slice(0, 5)
})
// Check 2: Products with negative stock
const negativeStock = await db.list('Product', {
where: { stock: { lt: 0 } }
})
checks.checks.push({
name: 'Products with negative stock',
status: negativeStock.length === 0 ? 'PASS' : 'FAIL',
count: negativeStock.length,
samples: negativeStock.slice(0, 5).map(p => ({ id: p.id, name: p.name, stock: p.stock }))
})
// Check 3: Duplicate emails
const persons = await db.list('Person')
const emailCounts = {}
persons.forEach(p => {
if (p.email) {
emailCounts[p.email] = (emailCounts[p.email] || 0) + 1
}
})
const duplicateEmails = Object.entries(emailCounts)
.filter(([, count]) => (count as number) > 1)
.map(([email]) => email)
checks.checks.push({
name: 'Duplicate email addresses',
status: duplicateEmails.length === 0 ? 'PASS' : 'WARN',
count: duplicateEmails.length,
samples: duplicateEmails.slice(0, 5)
})
// Check 4: Orders with mismatched totals
const allOrders = await db.list('Order')
const mismatchedTotals = []
for (const order of allOrders) {
const calculatedTotal = order.items?.reduce((sum, item) =>
sum + (item.quantity * item.price), 0) || 0
if (Math.abs(calculatedTotal - order.total) > 0.01) {
mismatchedTotals.push({
orderId: order.id,
storedTotal: order.total,
calculatedTotal
})
}
}
checks.checks.push({
name: 'Orders with mismatched totals',
status: mismatchedTotals.length === 0 ? 'PASS' : 'WARN',
count: mismatchedTotals.length,
samples: mismatchedTotals.slice(0, 5)
})
// Summary
const failedChecks = checks.checks.filter(c => c.status === 'FAIL').length
const warnings = checks.checks.filter(c => c.status === 'WARN').length
const passed = checks.checks.filter(c => c.status === 'PASS').length
{
...checks,
summary: {
total: checks.checks.length,
passed,
warnings,
failed: failedChecks,
overallStatus: failedChecks > 0 ? 'FAIL' : warnings > 0 ? 'WARN' : 'PASS'
}
}Deep Dive: Modules
Modules are declarative definitions that register persistent behaviors. They don't return values - instead, they create subscriptions that respond to events and schedules.
Declarative Definition Model
Modules use declarative statements to define what should happen rather than imperatively executing how to do it:
// Declarative: Define what happens when an event occurs
on($.Order.created, async (order) => {
// This handler persists and runs every time an order is created
await send($.Email.send, {
to: order.customer.email,
subject: 'Order Confirmation',
})
})
// Declarative: Define what happens on a schedule
every('0 9 * * *', async () => {
// This task persists and runs every day at 9 AM
const report = await generateDailyReport()
await send($.Email.send, { to: '[email protected]', body: report })
})Key Characteristics:
- Persistent Subscriptions: Handlers remain active indefinitely
- Event-Driven: React to domain events as they occur
- Scheduled Execution: Run on cron patterns
- No Return Value: Side effects only, no data returned
- Composability via Events: Chain handlers through event publishing
Persistent Subscription Semantics
When you register a handler with on() or every(), it creates a persistent subscription in the system:
// This creates a subscription that persists beyond script execution
on($.Payment.succeeded, async (payment) => {
await db.update('Order', payment.orderId, {
status: 'paid',
paidAt: new Date(),
})
})
// The subscription remains active even after the script completes
// It will trigger every time a Payment.succeeded event is publishedSubscription Lifecycle:
- Registration: Module defines handler → subscription created
- Activation: Subscription becomes active in the system
- Persistence: Subscription persists across restarts
- Triggering: Event matches pattern → handler executes
- Cleanup: Manual deletion or system maintenance
Export Patterns and Reusability
Modules can export reusable handler definitions:
// Define reusable handlers
export const orderHandlers = {
onCreated: on($.Order.created, async (order) => {
await send($.Email.send, {
to: order.customer.email,
subject: 'Order Confirmation',
})
}),
onPaid: on($.Payment.succeeded, async (payment) => {
await db.update('Order', payment.orderId, {
status: 'paid',
})
}),
onShipped: on($.Fulfillment.shipped, async (fulfillment) => {
await send($.Email.send, {
to: fulfillment.customer.email,
subject: 'Order Shipped',
})
}),
}
// Define reusable scheduled tasks
export const maintenanceTasks = {
daily: every('0 2 * * *', async () => {
await cleanupExpiredSessions()
await archiveOldOrders()
}),
hourly: every('0 * * * *', async () => {
await syncInventory()
}),
}Workflow Orchestration
Modules excel at orchestrating multi-step workflows through event chains:
// Step 1: Order created
on($.Order.created, async (order) => {
// Validate order
const valid = await validateOrder(order)
if (valid) {
// Trigger payment processing
await send($.Payment.process, {
orderId: order.id,
amount: order.total,
})
} else {
// Trigger failure handling
await send($.Order.failed, {
orderId: order.id,
reason: 'Validation failed',
})
}
})
// Step 2: Payment processed
on($.Payment.succeeded, async (payment) => {
// Update order status
await db.update('Order', payment.orderId, {
status: 'paid',
paidAt: new Date(),
})
// Trigger fulfillment
await send($.Fulfillment.start, {
orderId: payment.orderId,
})
})
// Step 3: Fulfillment started
on($.Fulfillment.start, async (fulfillment) => {
// Reserve inventory
await reserveInventory(fulfillment.orderId)
// Trigger shipping
await send($.Shipping.create, {
orderId: fulfillment.orderId,
})
})
// Step 4: Shipped
on($.Shipping.shipped, async (shipping) => {
// Notify customer
await send($.Email.send, {
to: shipping.customer.email,
subject: 'Order Shipped',
body: `Tracking: ${shipping.trackingNumber}`,
})
// Update order
await db.update('Order', shipping.orderId, {
status: 'shipped',
trackingNumber: shipping.trackingNumber,
})
})When Modules Are The Right Choice
Use modules when you need:
- Event Handling: React to business events automatically
- Scheduled Tasks: Run recurring operations
- Workflow Orchestration: Multi-step processes
- Background Jobs: Long-running async operations
- Side Effects: Operations that don't return values
- Persistent Behavior: Handlers that outlive script execution
Module Use Cases:
- Order processing pipelines
- Email notification systems
- Scheduled reports and cleanup tasks
- Inventory management workflows
- Customer lifecycle automation
- Integration webhooks
- Monitoring and alerting
- Data synchronization
Module Example 1: Order Processing Pipeline
// Complete order processing workflow
// Handler 1: Order created
on($.Order.created, async (order) => {
console.log(`New order received: ${order.id}`)
// Send confirmation email
await send($.Email.send, {
to: order.customer.email,
from: '[email protected]',
subject: `Order Confirmation #${order.orderNumber}`,
body: `Thank you for your order! Total: $${order.total}`,
})
// Update inventory
for (const item of order.items || []) {
const product = await db.get('Product', item.productId)
await db.update('Product', item.productId, {
stock: product.stock - item.quantity,
})
// Check reorder threshold
if (product.stock - item.quantity < 10) {
await send($.Inventory.lowStock, {
productId: item.productId,
currentStock: product.stock - item.quantity,
})
}
}
// Trigger payment processing
await send($.Payment.process, {
orderId: order.id,
amount: order.total,
})
})
// Handler 2: Payment succeeded
on($.Payment.succeeded, async (payment) => {
console.log(`Payment succeeded: ${payment.id}`)
// Update order
await db.update('Order', payment.orderId, {
status: 'paid',
paidAt: new Date(),
paymentMethod: payment.method,
})
// Trigger fulfillment
await send($.Fulfillment.start, {
orderId: payment.orderId,
priority: payment.amount > 500 ? 'high' : 'normal',
})
// Track revenue
await send($.Analytics.track, {
event: 'purchase',
orderId: payment.orderId,
revenue: payment.amount,
})
})
// Handler 3: Payment failed
on($.Payment.failed, async (payment) => {
console.log(`Payment failed: ${payment.id}`)
const order = await db.get('Order', payment.orderId)
const customer = await db.get('Person', order.customerId)
// Update order status
await db.update('Order', payment.orderId, {
status: 'payment_failed',
failureReason: payment.reason,
})
// Notify customer
await send($.Email.send, {
to: customer.email,
from: '[email protected]',
subject: 'Payment Issue with Your Order',
body: `We couldn't process your payment. Please update your payment method.`,
})
// Return inventory
for (const item of order.items || []) {
const product = await db.get('Product', item.productId)
await db.update('Product', item.productId, {
stock: product.stock + item.quantity,
})
}
})
// Handler 4: Fulfillment started
on($.Fulfillment.start, async (fulfillment) => {
console.log(`Starting fulfillment: ${fulfillment.orderId}`)
const order = await db.get('Order', fulfillment.orderId)
// Update order status
await db.update('Order', order.id, {
status: 'processing',
processingStartedAt: new Date(),
})
// Notify warehouse
await send($.Warehouse.pickOrder, {
orderId: order.id,
priority: fulfillment.priority,
items: order.items,
})
})
// Handler 5: Order shipped
on($.Order.shipped, async (event) => {
console.log(`Order shipped: ${event.orderId}`)
const order = await db.get('Order', event.orderId)
const customer = await db.get('Person', order.customerId)
// Update order
await db.update('Order', event.orderId, {
status: 'shipped',
shippedAt: new Date(),
trackingNumber: event.trackingNumber,
carrier: event.carrier,
})
// Send shipping notification
await send($.Email.send, {
to: customer.email,
from: '[email protected]',
subject: 'Your Order Has Shipped!',
body: `
Your order #${order.orderNumber} is on its way!
Tracking Number: ${event.trackingNumber}
Carrier: ${event.carrier}
Expected delivery: ${event.estimatedDelivery}
`,
})
})
// Handler 6: Order delivered
on($.Order.delivered, async (event) => {
console.log(`Order delivered: ${event.orderId}`)
const order = await db.get('Order', event.orderId)
const customer = await db.get('Person', order.customerId)
// Update order
await db.update('Order', event.orderId, {
status: 'delivered',
deliveredAt: new Date(),
})
// Request review
await send($.Email.send, {
to: customer.email,
from: '[email protected]',
subject: 'How was your order?',
body: `We'd love to hear your feedback on order #${order.orderNumber}!`,
})
// Schedule follow-up
await send($.Schedule.create, {
type: 'followup-email',
recipientId: customer.id,
orderId: order.id,
sendAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
})
})Module Example 2: Customer Lifecycle Automation
// Customer onboarding and engagement workflows
// New customer welcome flow
on($.Person.created, async (person) => {
console.log(`New customer: ${person.email}`)
// Generate personalized welcome email with AI
const welcomeEmail = await ai.generate({
prompt: `Write a warm welcome email for new customer ${person.name}.
Include:
- Thank them for joining
- Highlight our key benefits
- Offer discount code: WELCOME10
Keep it friendly and concise.`,
schema: $.EmailMessage,
model: 'claude-sonnet-4.5',
})
// Send welcome email
await send($.Email.send, {
to: person.email,
from: '[email protected]',
subject: welcomeEmail.subject,
body: welcomeEmail.body,
})
// Tag customer
await db.update('Person', person.id, {
tags: ['new-customer'],
welcomeEmailSent: true,
welcomeEmailSentAt: new Date(),
})
// Schedule follow-up emails
await send($.Schedule.create, {
type: 'email',
recipientId: person.id,
template: 'welcome-day-3',
sendAt: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000),
})
await send($.Schedule.create, {
type: 'email',
recipientId: person.id,
template: 'welcome-day-7',
sendAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
})
})
// First purchase celebration
on($.Order.firstPurchase, async (order) => {
console.log(`First purchase for customer: ${order.customerId}`)
const customer = await db.get('Person', order.customerId)
// Update segment
await db.update('Person', customer.id, {
segment: 'Active',
firstPurchaseAt: new Date(),
tags: [...(customer.tags || []), 'converted'],
})
// Send thank you email
await send($.Email.send, {
to: customer.email,
from: '[email protected]',
subject: 'Thank You for Your First Order!',
body: `
We're so excited you made your first purchase!
As a thank you, here's a referral code: ${customer.id.substring(0, 8).toUpperCase()}
Share it with friends - they get 15% off, you get $10 credit!
`,
})
// Trigger loyalty program enrollment
await send($.Loyalty.enroll, {
customerId: customer.id,
})
})
// Repeat purchase - loyalty growth
on($.Order.created, async (order) => {
const customer = await db.get('Person', order.customerId)
// Get order count
const allOrders = await db.list('Order', {
where: { customerId: customer.id },
})
// Milestone rewards
if (allOrders.length === 5) {
await send($.Email.send, {
to: customer.email,
from: '[email protected]',
subject: "You're on fire! 🔥",
body: "Congrats on your 5th order! Here's $20 credit: LOYAL5",
})
await db.update('Person', customer.id, {
segment: 'Loyal',
loyaltyCredits: (customer.loyaltyCredits || 0) + 20,
})
} else if (allOrders.length === 10) {
await send($.Email.send, {
to: customer.email,
from: '[email protected]',
subject: 'Welcome to VIP Status! 💎',
body: "You're now a VIP! Enjoy exclusive perks and early access.",
})
await db.update('Person', customer.id, {
segment: 'VIP',
vipEnrolledAt: new Date(),
loyaltyCredits: (customer.loyaltyCredits || 0) + 50,
})
}
})
// Inactivity detection
on($.Customer.inactive, async (event) => {
console.log(`Customer inactive: ${event.customerId}`)
const customer = await db.get('Person', event.customerId)
// Generate personalized win-back email
const winbackEmail = await ai.generate({
prompt: `Write a win-back email for inactive customer ${customer.name}.
They haven't ordered in ${event.daysSinceLastOrder} days.
Offer 20% discount code: COMEBACK20
Be warm, not pushy.`,
schema: $.EmailMessage,
model: 'claude-sonnet-4.5',
})
await send($.Email.send, {
to: customer.email,
from: '[email protected]',
subject: winbackEmail.subject,
body: winbackEmail.body,
})
// Update segment
await db.update('Person', customer.id, {
segment: 'At-Risk',
winbackEmailSentAt: new Date(),
})
})Module Example 3: Inventory Management
// Automated inventory management workflows
// Low stock alerts
on($.Inventory.lowStock, async (event) => {
console.log(`Low stock alert: ${event.productId}`)
const product = await db.get('Product', event.productId)
// Send alert to purchasing
await send($.Email.send, {
to: '[email protected]',
from: '[email protected]',
subject: `Low Stock Alert: ${product.name}`,
body: `
Product: ${product.name}
SKU: ${product.sku}
Current Stock: ${event.currentStock}
Reorder Level: ${product.reorderLevel || 10}
Action Required: Place reorder
`,
})
// Update product flags
await db.update('Product', product.id, {
lowStockAlertSent: true,
lowStockAlertSentAt: new Date(),
})
// Create reorder task
await send($.Task.create, {
type: 'reorder-product',
productId: product.id,
priority: event.currentStock < 5 ? 'high' : 'normal',
assignee: 'purchasing-team',
})
})
// Out of stock handling
on($.Inventory.outOfStock, async (event) => {
console.log(`Out of stock: ${event.productId}`)
const product = await db.get('Product', event.productId)
// Update product status
await db.update('Product', product.id, {
status: 'out-of-stock',
outOfStockAt: new Date(),
})
// Notify interested customers
const waitlist = await db.list('ProductWaitlist', {
where: { productId: product.id },
})
for (const entry of waitlist) {
await send($.Email.send, {
to: entry.customerEmail,
from: '[email protected]',
subject: `${product.name} - Back in Stock Soon`,
body: `We'll notify you when ${product.name} is back in stock!`,
})
}
// Urgent alert
await send($.Notification.send, {
to: 'inventory-team',
priority: 'urgent',
message: `${product.name} is OUT OF STOCK`,
})
})
// Restock notifications
on($.Inventory.restocked, async (event) => {
console.log(`Product restocked: ${event.productId}`)
const product = await db.get('Product', event.productId)
// Update product status
await db.update('Product', product.id, {
status: 'in-stock',
stock: event.newStock,
restockedAt: new Date(),
lowStockAlertSent: false,
})
// Notify waitlist
const waitlist = await db.list('ProductWaitlist', {
where: { productId: product.id },
})
for (const entry of waitlist) {
await send($.Email.send, {
to: entry.customerEmail,
from: '[email protected]',
subject: `${product.name} is Back in Stock!`,
body: `
Great news! ${product.name} is back in stock.
Order now: https://acme.com/products/${product.id}
Limited quantity available!
`,
})
// Remove from waitlist
await db.delete('ProductWaitlist', entry.id)
}
})
// Scheduled inventory audit
every('0 2 * * *', async () => {
console.log('Running nightly inventory audit...')
const products = await db.list('Product')
const issues = []
for (const product of products) {
// Check for anomalies
if (product.stock < 0) {
issues.push({
type: 'negative-stock',
productId: product.id,
productName: product.name,
stock: product.stock,
})
}
if (product.stock === 0 && product.status !== 'out-of-stock') {
issues.push({
type: 'status-mismatch',
productId: product.id,
productName: product.name,
})
// Auto-fix
await db.update('Product', product.id, {
status: 'out-of-stock',
})
}
}
if (issues.length > 0) {
await send($.Email.send, {
to: '[email protected]',
from: '[email protected]',
subject: `Inventory Audit: ${issues.length} Issues Found`,
body: JSON.stringify(issues, null, 2),
})
}
})
// Scheduled stock level report
every('0 9 * * MON', async () => {
console.log('Generating weekly stock level report...')
const products = await db.list('Product')
const lowStock = products.filter((p) => p.stock < (p.reorderLevel || 10))
const outOfStock = products.filter((p) => p.stock === 0)
const overstocked = products.filter((p) => p.stock > (p.maxStock || 100))
const report = {
date: new Date().toISOString(),
totalProducts: products.length,
lowStock: lowStock.length,
outOfStock: outOfStock.length,
overstocked: overstocked.length,
totalValue: products.reduce((sum, p) => sum + p.stock * p.price, 0),
details: {
lowStock: lowStock.map((p) => ({ id: p.id, name: p.name, stock: p.stock })),
outOfStock: outOfStock.map((p) => ({ id: p.id, name: p.name })),
overstocked: overstocked.map((p) => ({ id: p.id, name: p.name, stock: p.stock })),
},
}
await send($.Email.send, {
to: '[email protected]',
from: '[email protected]',
subject: 'Weekly Inventory Report',
body: JSON.stringify(report, null, 2),
})
})Module Example 4: Notification System
// Multi-channel notification system
// Email notifications
on($.Notification.email, async (notification) => {
await send($.Email.send, {
to: notification.recipient,
from: notification.from || '[email protected]',
subject: notification.subject,
body: notification.body,
})
// Log notification
await db.create('NotificationLog', {
type: 'email',
recipient: notification.recipient,
sentAt: new Date(),
status: 'sent',
})
})
// SMS notifications (via API integration)
on($.Notification.sms, async (notification) => {
const result = await api.proxy('twilio', '/messages', {
method: 'POST',
body: {
to: notification.phoneNumber,
from: '+1-555-0100',
body: notification.message,
},
})
await db.create('NotificationLog', {
type: 'sms',
recipient: notification.phoneNumber,
sentAt: new Date(),
status: result.status,
messageId: result.sid,
})
})
// Push notifications
on($.Notification.push, async (notification) => {
await api.fetch('https://fcm.googleapis.com/fcm/send', {
method: 'POST',
headers: {
Authorization: 'key=SERVER_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: notification.deviceToken,
notification: {
title: notification.title,
body: notification.body,
},
}),
})
await db.create('NotificationLog', {
type: 'push',
recipient: notification.userId,
sentAt: new Date(),
status: 'sent',
})
})
// Notification preferences handling
on($.Notification.send, async (notification) => {
// Get user preferences
const user = await db.get('Person', notification.userId)
if (!user || !user.notificationPreferences) {
// Default: email only
await send($.Notification.email, {
recipient: user.email,
subject: notification.subject,
body: notification.body,
})
return
}
const prefs = user.notificationPreferences
// Send via preferred channels
if (prefs.email && user.email) {
await send($.Notification.email, {
recipient: user.email,
subject: notification.subject,
body: notification.body,
})
}
if (prefs.sms && user.phoneNumber) {
await send($.Notification.sms, {
phoneNumber: user.phoneNumber,
message: notification.body,
})
}
if (prefs.push && user.deviceToken) {
await send($.Notification.push, {
userId: user.id,
deviceToken: user.deviceToken,
title: notification.subject,
body: notification.body,
})
}
})
// Scheduled reminder system
every('0 * * * *', async () => {
console.log('Processing scheduled notifications...')
const now = new Date()
const pending = await db.list('ScheduledNotification', {
where: {
sendAt: { lte: now.toISOString() },
status: 'pending',
},
})
for (const scheduled of pending) {
await send($.Notification.send, {
userId: scheduled.userId,
subject: scheduled.subject,
body: scheduled.body,
})
await db.update('ScheduledNotification', scheduled.id, {
status: 'sent',
sentAt: new Date(),
})
}
console.log(`Sent ${pending.length} scheduled notifications`)
})Module Example 5: Analytics and Reporting
// Automated analytics and reporting system
// Track key business events
on($.Order.created, async (order) => {
await send($.Analytics.track, {
event: 'order_created',
properties: {
orderId: order.id,
customerId: order.customerId,
amount: order.total,
itemCount: order.items?.length || 0,
},
timestamp: new Date(),
})
})
on($.Order.completed, async (order) => {
await send($.Analytics.track, {
event: 'order_completed',
properties: {
orderId: order.id,
revenue: order.total,
processingTime: new Date().getTime() - new Date(order.createdAt).getTime(),
},
timestamp: new Date(),
})
})
// Daily metrics report
every('0 9 * * *', async () => {
console.log('Generating daily metrics report...')
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000)
// Fetch yesterday's data
const orders = await db.list('Order', {
where: { createdAt: { gte: yesterday.toISOString() } },
})
const newCustomers = await db.list('Person', {
where: { createdAt: { gte: yesterday.toISOString() } },
})
// Calculate metrics
const metrics = {
date: yesterday.toISOString().split('T')[0],
orders: {
total: orders.length,
completed: orders.filter((o) => o.status === 'completed').length,
cancelled: orders.filter((o) => o.status === 'cancelled').length,
revenue: orders.reduce((sum, o) => sum + (o.total || 0), 0),
},
customers: {
new: newCustomers.length,
active: new Set(orders.map((o) => o.customerId)).size,
},
avgOrderValue: orders.length > 0 ? orders.reduce((sum, o) => sum + (o.total || 0), 0) / orders.length : 0,
}
// Generate insights with AI
const insights = await ai.generate({
prompt: `Analyze these daily e-commerce metrics and provide 3 key insights:
${JSON.stringify(metrics, null, 2)}`,
schema: $.Article,
model: 'gpt-5',
})
// Send report
await send($.Email.send, {
to: '[email protected]',
from: '[email protected]',
subject: `Daily Metrics - ${metrics.date}`,
body: `
Metrics Summary:
Orders: ${metrics.orders.total} (${metrics.orders.completed} completed)
Revenue: $${metrics.revenue.toFixed(2)}
New Customers: ${metrics.customers.new}
Avg Order Value: $${metrics.avgOrderValue.toFixed(2)}
AI Insights:
${insights.articleBody}
`,
})
// Store metrics
await db.create('DailyMetrics', metrics)
})
// Weekly executive summary
every('0 10 * * MON', async () => {
console.log('Generating weekly executive summary...')
const lastWeek = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
// Fetch week's data
const orders = await db.list('Order', {
where: { createdAt: { gte: lastWeek.toISOString() } },
})
const weekSummary = {
weekOf: lastWeek.toISOString().split('T')[0],
totalOrders: orders.length,
totalRevenue: orders.reduce((sum, o) => sum + (o.total || 0), 0),
topProducts: await getTopProducts(orders),
topCustomers: await getTopCustomers(orders),
}
// Generate executive summary with AI
const summary = await ai.generate({
prompt: `Write an executive summary for this week's e-commerce performance:
${JSON.stringify(weekSummary, null, 2)}
Include:
- Overall performance assessment
- Key trends
- Recommendations for next week
Keep it concise and actionable.`,
schema: $.Article,
model: 'claude-sonnet-4.5',
})
// Send to leadership
await send($.Email.send, {
to: '[email protected]',
from: '[email protected]',
subject: `Weekly Executive Summary - Week of ${weekSummary.weekOf}`,
body: summary.articleBody,
})
})
// Real-time anomaly detection
on($.Analytics.track, async (event) => {
// Detect unusual patterns
if (event.event === 'order_created') {
const recentOrders = await db.list('Order', {
where: {
createdAt: { gte: new Date(Date.now() - 60 * 60 * 1000).toISOString() },
},
})
// Check for unusual spike
if (recentOrders.length > 50) {
await send($.Alert.send, {
type: 'anomaly',
severity: 'high',
message: `Unusual order spike detected: ${recentOrders.length} orders in last hour`,
timestamp: new Date(),
})
}
// Check for large order
if (event.properties.amount > 10000) {
await send($.Alert.send, {
type: 'large-order',
severity: 'info',
message: `Large order detected: $${event.properties.amount}`,
orderId: event.properties.orderId,
})
}
}
})
// Helper functions
async function getTopProducts(orders) {
const productCounts = {}
orders.forEach((order) => {
order.items?.forEach((item) => {
productCounts[item.productId] = (productCounts[item.productId] || 0) + item.quantity
})
})
const topIds = Object.entries(productCounts)
.sort(([, a], [, b]) => (b as number) - (a as number))
.slice(0, 5)
.map(([id]) => id)
return Promise.all(
topIds.map(async (id) => {
const product = await db.get('Product', id)
return { name: product.name, quantity: productCounts[id] }
})
)
}
async function getTopCustomers(orders) {
const customerSpend = {}
orders.forEach((order) => {
customerSpend[order.customerId] = (customerSpend[order.customerId] || 0) + (order.total || 0)
})
const topIds = Object.entries(customerSpend)
.sort(([, a], [, b]) => (b as number) - (a as number))
.slice(0, 5)
.map(([id]) => id)
return Promise.all(
topIds.map(async (id) => {
const customer = await db.get('Person', id)
return { name: customer.name, spent: customerSpend[id] }
})
)
}Module Example 6: Data Synchronization
// Sync data with external systems
// Sync new orders to external fulfillment system
on($.Order.paid, async (order) => {
console.log(`Syncing order to fulfillment system: ${order.id}`)
try {
const result = await api.fetch('https://fulfillment.example.com/api/orders', {
method: 'POST',
headers: {
Authorization: 'Bearer API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
externalOrderId: order.id,
customer: {
name: order.customer.name,
email: order.customer.email,
address: order.shippingAddress,
},
items: order.items.map((item) => ({
sku: item.sku,
quantity: item.quantity,
price: item.price,
})),
total: order.total,
}),
})
const syncData = await result.json()
// Store external reference
await db.update('Order', order.id, {
externalFulfillmentId: syncData.fulfillmentId,
syncedAt: new Date(),
})
console.log(`Order synced successfully: ${syncData.fulfillmentId}`)
} catch (error) {
console.error(`Failed to sync order: ${error.message}`)
// Log sync failure
await db.create('SyncError', {
entityType: 'Order',
entityId: order.id,
errorMessage: error.message,
timestamp: new Date(),
})
// Retry later
await send($.Sync.retry, {
entityType: 'Order',
entityId: order.id,
attempt: 1,
})
}
})
// Sync customer data to CRM
on($.Person.created, async (person) => {
console.log(`Syncing customer to CRM: ${person.email}`)
try {
const result = await api.proxy('salesforce', '/sobjects/Contact', {
method: 'POST',
body: {
FirstName: person.name.split(' ')[0],
LastName: person.name.split(' ').slice(1).join(' '),
Email: person.email,
Phone: person.phoneNumber,
Source__c: 'E-commerce',
},
})
await db.update('Person', person.id, {
crmId: result.id,
crmSyncedAt: new Date(),
})
console.log(`Customer synced to CRM: ${result.id}`)
} catch (error) {
console.error(`Failed to sync customer: ${error.message}`)
}
})
// Scheduled bulk sync
every('0 3 * * *', async () => {
console.log('Running nightly bulk sync...')
// Find entities that need syncing
const unsyncedOrders = await db.list('Order', {
where: {
status: 'completed',
externalFulfillmentId: null,
},
})
console.log(`Found ${unsyncedOrders.length} unsynced orders`)
for (const order of unsyncedOrders) {
await send($.Order.paid, order)
// Rate limiting
await new Promise((resolve) => setTimeout(resolve, 100))
}
// Find sync errors to retry
const syncErrors = await db.list('SyncError', {
where: {
retryCount: { lt: 3 },
lastRetryAt: { lt: new Date(Date.now() - 60 * 60 * 1000).toISOString() },
},
})
console.log(`Retrying ${syncErrors.length} failed syncs`)
for (const error of syncErrors) {
await send($.Sync.retry, {
entityType: error.entityType,
entityId: error.entityId,
attempt: error.retryCount + 1,
})
}
})
// Handle sync retries
on($.Sync.retry, async (retry) => {
console.log(`Retry sync: ${retry.entityType} ${retry.entityId} (attempt ${retry.attempt})`)
if (retry.attempt > 3) {
console.log('Max retries exceeded, manual intervention required')
await send($.Alert.send, {
type: 'sync-failure',
message: `Failed to sync ${retry.entityType} ${retry.entityId} after 3 attempts`,
severity: 'high',
})
return
}
// Get entity and retry sync
const entity = await db.get(retry.entityType, retry.entityId)
if (retry.entityType === 'Order') {
await send($.Order.paid, entity)
} else if (retry.entityType === 'Person') {
await send($.Person.created, entity)
}
// Update retry count
await db.update('SyncError', retry.id, {
retryCount: retry.attempt,
lastRetryAt: new Date(),
})
})Module Example 7: Scheduled Maintenance
// Automated maintenance tasks
// Daily cleanup at 2 AM
every('0 2 * * *', async () => {
console.log('Running daily cleanup tasks...')
// Clean up expired sessions
const expiredSessions = await db.list('Session', {
where: {
expiresAt: { lt: new Date().toISOString() },
},
})
for (const session of expiredSessions) {
await db.delete('Session', session.id)
}
console.log(`Deleted ${expiredSessions.length} expired sessions`)
// Clean up abandoned carts (30+ days old)
const cutoff = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
const abandonedCarts = await db.list('Cart', {
where: {
status: 'active',
updatedAt: { lt: cutoff.toISOString() },
},
})
for (const cart of abandonedCarts) {
await db.delete('Cart', cart.id)
}
console.log(`Deleted ${abandonedCarts.length} abandoned carts`)
// Clean up old logs (90+ days)
const logCutoff = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000)
const oldLogs = await db.list('Log', {
where: {
createdAt: { lt: logCutoff.toISOString() },
},
})
for (const log of oldLogs) {
await db.delete('Log', log.id)
}
console.log(`Deleted ${oldLogs.length} old logs`)
})
// Monthly archival at 3 AM on 1st of month
every('0 3 1 * *', async () => {
console.log('Running monthly archival...')
const cutoff = new Date(Date.now() - 365 * 24 * 60 * 60 * 1000) // 1 year ago
// Archive old completed orders
const oldOrders = await db.list('Order', {
where: {
status: 'completed',
completedAt: { lt: cutoff.toISOString() },
},
})
for (const order of oldOrders) {
// Create archive record
await db.create('ArchivedOrder', {
...order,
archivedAt: new Date().toISOString(),
})
// Delete original
await db.delete('Order', order.id)
}
console.log(`Archived ${oldOrders.length} old orders`)
// Send summary
await send($.Email.send, {
to: '[email protected]',
from: '[email protected]',
subject: 'Monthly Archival Complete',
body: `Archived ${oldOrders.length} orders from ${cutoff.toISOString()}`,
})
})
// Database optimization weekly
every('0 4 * * SUN', async () => {
console.log('Running database optimization...')
// Rebuild indexes
await api.fetch('https://api.platform.do/admin/optimize', {
method: 'POST',
headers: { Authorization: 'Bearer ADMIN_TOKEN' },
body: JSON.stringify({
action: 'rebuild-indexes',
}),
})
// Vacuum database
await api.fetch('https://api.platform.do/admin/optimize', {
method: 'POST',
headers: { Authorization: 'Bearer ADMIN_TOKEN' },
body: JSON.stringify({
action: 'vacuum',
}),
})
console.log('Database optimization complete')
await send($.Alert.send, {
type: 'maintenance',
message: 'Weekly database optimization completed',
severity: 'info',
})
})
// Health check every 5 minutes
every('*/5 * * * *', async () => {
try {
// Check database connectivity
const testQuery = await db.list('Product', { limit: 1 })
// Check API connectivity
const testApi = await api.fetch('https://api.platform.do/health')
if (!testApi.ok) {
throw new Error('API health check failed')
}
// All checks passed
} catch (error) {
console.error('Health check failed:', error.message)
await send($.Alert.send, {
type: 'health-check-failed',
message: error.message,
severity: 'critical',
timestamp: new Date(),
})
}
})Module Example 8: Webhook Handler
// Handle incoming webhooks from external services
// Stripe payment webhook
on($.Webhook.stripe, async (webhook) => {
console.log(`Stripe webhook: ${webhook.type}`)
switch (webhook.type) {
case 'payment_intent.succeeded':
const paymentIntent = webhook.data.object
// Find order by payment intent ID
const orders = await db.list('Order', {
where: { stripePaymentIntentId: paymentIntent.id },
})
if (orders.length === 0) {
console.error(`No order found for payment intent: ${paymentIntent.id}`)
return
}
const order = orders[0]
// Trigger payment succeeded event
await send($.Payment.succeeded, {
orderId: order.id,
amount: paymentIntent.amount / 100,
currency: paymentIntent.currency,
method: 'stripe',
})
break
case 'payment_intent.payment_failed':
const failedIntent = webhook.data.object
const failedOrders = await db.list('Order', {
where: { stripePaymentIntentId: failedIntent.id },
})
if (failedOrders.length > 0) {
await send($.Payment.failed, {
orderId: failedOrders[0].id,
reason: failedIntent.last_payment_error?.message || 'Unknown error',
})
}
break
default:
console.log(`Unhandled Stripe event: ${webhook.type}`)
}
})
// Shippo shipping webhook
on($.Webhook.shippo, async (webhook) => {
console.log(`Shippo webhook: ${webhook.event}`)
if (webhook.event === 'track_updated') {
const tracking = webhook.data
// Find order by tracking number
const orders = await db.list('Order', {
where: { trackingNumber: tracking.tracking_number },
})
if (orders.length === 0) {
console.error(`No order found for tracking: ${tracking.tracking_number}`)
return
}
const order = orders[0]
// Update tracking status
await db.update('Order', order.id, {
trackingStatus: tracking.tracking_status.status,
trackingStatusDetail: tracking.tracking_status.status_details,
lastTrackingUpdate: new Date(),
})
// Trigger events based on status
if (tracking.tracking_status.status === 'DELIVERED') {
await send($.Order.delivered, {
orderId: order.id,
deliveredAt: tracking.tracking_status.status_date,
})
}
}
})
// Webhook error handling
on($.Webhook.error, async (error) => {
console.error(`Webhook error: ${error.message}`)
await db.create('WebhookError', {
source: error.source,
type: error.type,
payload: error.payload,
errorMessage: error.message,
timestamp: new Date(),
})
// Alert on repeated errors
const recentErrors = await db.list('WebhookError', {
where: {
source: error.source,
timestamp: { gte: new Date(Date.now() - 60 * 60 * 1000).toISOString() },
},
})
if (recentErrors.length > 10) {
await send($.Alert.send, {
type: 'webhook-errors',
message: `${recentErrors.length} webhook errors from ${error.source} in last hour`,
severity: 'high',
})
}
})Module Example 9: A/B Testing System
// Automated A/B testing and optimization
// Assign visitors to experiments
on($.Visitor.created, async (visitor) => {
// Get active experiments
const activeExperiments = await db.list('Experiment', {
where: {
status: 'active',
endDate: { gte: new Date().toISOString() },
},
})
for (const experiment of activeExperiments) {
// Randomly assign variant
const variant = Math.random() < 0.5 ? 'A' : 'B'
await db.create('ExperimentAssignment', {
experimentId: experiment.id,
visitorId: visitor.id,
variant,
assignedAt: new Date(),
})
}
})
// Track conversion events
on($.Order.created, async (order) => {
const customer = await db.get('Person', order.customerId)
// Find experiment assignments for this customer
const assignments = await db.list('ExperimentAssignment', {
where: { visitorId: customer.visitorId },
})
for (const assignment of assignments) {
// Record conversion
await db.create('ExperimentConversion', {
experimentId: assignment.experimentId,
assignmentId: assignment.id,
variant: assignment.variant,
orderId: order.id,
value: order.total,
convertedAt: new Date(),
})
}
})
// Daily experiment analysis
every('0 10 * * *', async () => {
console.log('Analyzing active experiments...')
const activeExperiments = await db.list('Experiment', {
where: { status: 'active' },
})
for (const experiment of activeExperiments) {
// Get assignments
const assignments = await db.list('ExperimentAssignment', {
where: { experimentId: experiment.id },
})
const variantA = assignments.filter((a) => a.variant === 'A')
const variantB = assignments.filter((a) => a.variant === 'B')
// Get conversions
const conversions = await db.list('ExperimentConversion', {
where: { experimentId: experiment.id },
})
const conversionsA = conversions.filter((c) => c.variant === 'A')
const conversionsB = conversions.filter((c) => c.variant === 'B')
// Calculate metrics
const metrics = {
experimentId: experiment.id,
experimentName: experiment.name,
date: new Date().toISOString().split('T')[0],
variantA: {
visitors: variantA.length,
conversions: conversionsA.length,
conversionRate: variantA.length > 0 ? conversionsA.length / variantA.length : 0,
revenue: conversionsA.reduce((sum, c) => sum + c.value, 0),
},
variantB: {
visitors: variantB.length,
conversions: conversionsB.length,
conversionRate: variantB.length > 0 ? conversionsB.length / variantB.length : 0,
revenue: conversionsB.reduce((sum, c) => sum + c.value, 0),
},
}
// Store daily metrics
await db.create('ExperimentMetrics', metrics)
// Check for statistical significance
const sampleSize = Math.min(variantA.length, variantB.length)
if (sampleSize >= 100) {
const lift = ((metrics.variantB.conversionRate - metrics.variantA.conversionRate) / metrics.variantA.conversionRate) * 100
if (Math.abs(lift) > 10) {
// Significant difference detected
const winner = lift > 0 ? 'B' : 'A'
await send($.Email.send, {
to: '[email protected]',
from: '[email protected]',
subject: `Experiment Result: ${experiment.name}`,
body: `
Experiment: ${experiment.name}
Sample Size: ${sampleSize}
Lift: ${lift.toFixed(2)}%
Winner: Variant ${winner}
Variant A: ${(metrics.variantA.conversionRate * 100).toFixed(2)}% conversion
Variant B: ${(metrics.variantB.conversionRate * 100).toFixed(2)}% conversion
Consider deploying variant ${winner} to all users.
`,
})
}
}
}
})Module Example 10: Fraud Detection
// Real-time fraud detection system
// Screen orders for fraud
on($.Order.created, async (order) => {
console.log(`Screening order for fraud: ${order.id}`)
const riskScore = 0
const riskFactors = []
// Factor 1: High order value
if (order.total > 1000) {
riskScore += 20
riskFactors.push('High order value')
}
// Factor 2: New customer
const customer = await db.get('Person', order.customerId)
const customerOrders = await db.list('Order', {
where: { customerId: customer.id },
})
if (customerOrders.length === 1) {
riskScore += 15
riskFactors.push('First-time customer')
}
// Factor 3: Shipping vs billing address mismatch
if (order.shippingAddress?.country !== order.billingAddress?.country) {
riskScore += 30
riskFactors.push('International shipping')
}
// Factor 4: Rapid orders
const recentOrders = await db.list('Order', {
where: {
customerId: customer.id,
createdAt: { gte: new Date(Date.now() - 60 * 60 * 1000).toISOString() },
},
})
if (recentOrders.length > 3) {
riskScore += 25
riskFactors.push('Multiple orders in short time')
}
// Factor 5: Email domain check
const emailDomain = customer.email.split('@')[1]
const suspiciousDomains = ['tempmail.com', 'guerrillamail.com', '10minutemail.com']
if (suspiciousDomains.includes(emailDomain)) {
riskScore += 40
riskFactors.push('Suspicious email domain')
}
// Store fraud check
await db.create('FraudCheck', {
orderId: order.id,
riskScore,
riskFactors,
checkedAt: new Date(),
})
// Take action based on risk
if (riskScore >= 50) {
// High risk - flag for manual review
await db.update('Order', order.id, {
status: 'under_review',
reviewReason: 'Fraud risk detected',
})
await send($.Alert.send, {
type: 'fraud-alert',
severity: 'high',
message: `High-risk order detected: ${order.id}`,
riskScore,
riskFactors,
})
// Notify team
await send($.Email.send, {
to: '[email protected]',
from: '[email protected]',
subject: `Fraud Alert: Order ${order.orderNumber}`,
body: `
Order: ${order.orderNumber}
Customer: ${customer.name} (${customer.email})
Amount: $${order.total}
Risk Score: ${riskScore}
Risk Factors:
${riskFactors.map((f) => `- ${f}`).join('\n')}
Review required: https://acme.com/admin/orders/${order.id}
`,
})
} else if (riskScore >= 30) {
// Medium risk - enhanced verification
await send($.Email.send, {
to: customer.email,
from: '[email protected]',
subject: 'Order Verification Required',
body: `
We need to verify your order #${order.orderNumber}.
Please confirm:
1. Did you place this order?
2. Is the shipping address correct?
Reply to this email or call us at 1-555-0100.
`,
})
}
// Log event
console.log(`Fraud check complete: ${order.id} - Risk Score: ${riskScore}`)
})
// Monitor fraud patterns
every('0 */6 * * *', async () => {
console.log('Analyzing fraud patterns...')
const recentChecks = await db.list('FraudCheck', {
where: {
checkedAt: { gte: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString() },
},
})
const highRisk = recentChecks.filter((c) => c.riskScore >= 50)
const mediumRisk = recentChecks.filter((c) => c.riskScore >= 30 && c.riskScore < 50)
if (highRisk.length > 10) {
await send($.Alert.send, {
type: 'fraud-spike',
severity: 'critical',
message: `${highRisk.length} high-risk orders detected in last 24 hours`,
})
}
// Generate report
const report = {
period: 'Last 24 hours',
totalChecks: recentChecks.length,
highRisk: highRisk.length,
mediumRisk: mediumRisk.length,
lowRisk: recentChecks.length - highRisk.length - mediumRisk.length,
commonRiskFactors: getTopRiskFactors(recentChecks),
}
await send($.Email.send, {
to: '[email protected]',
from: '[email protected]',
subject: 'Fraud Detection Report',
body: JSON.stringify(report, null, 2),
})
})
function getTopRiskFactors(checks) {
const factorCounts = {}
checks.forEach((check) => {
check.riskFactors?.forEach((factor) => {
factorCounts[factor] = (factorCounts[factor] || 0) + 1
})
})
return Object.entries(factorCounts)
.sort(([, a], [, b]) => (b as number) - (a as number))
.slice(0, 5)
.map(([factor, count]) => ({ factor, count }))
}Module + Script Combined
Sometimes you need both execution models in the same code. This section shows patterns for combining scripts and modules effectively.
Pattern 1: Initialize + Return Status
Use a module to set up persistent handlers, then return status information:
// Register handlers
on($.Order.created, async (order) => {
await send($.Email.send, {
to: order.customer.email,
subject: 'Order Confirmation'
})
})
on($.Payment.succeeded, async (payment) => {
await db.update('Order', payment.orderId, {
status: 'paid'
})
})
// Return status (script behavior)
{
handlersRegistered: 2,
handlers: [
'Order.created → Email.send',
'Payment.succeeded → Update order status'
],
status: 'active'
}Pattern 2: Register + Query State
Register scheduled tasks and return current system state:
// Register scheduled tasks
every('0 * * * *', async () => {
const pending = await db.list('Order', {
where: { status: 'pending' }
})
for (const order of pending) {
await send($.Order.reminder, { orderId: order.id })
}
})
every('0 2 * * *', async () => {
await cleanupExpiredData()
})
// Query and return current state
const currentPending = await db.list('Order', {
where: { status: 'pending' }
})
const expiredData = await db.list('Cache', {
where: {
expiresAt: { lt: new Date().toISOString() }
}
})
{
scheduledTasks: 2,
currentState: {
pendingOrders: currentPending.length,
expiredCacheEntries: expiredData.length
},
nextRun: {
hourly: 'Every hour',
daily: '2:00 AM'
}
}Pattern 3: Setup Workflow + Test Run
Set up a complete workflow, then execute a test:
// Set up workflow handlers
on($.Order.created, async (order) => {
await send($.Payment.process, { orderId: order.id })
})
on($.Payment.succeeded, async (payment) => {
await db.update('Order', payment.orderId, { status: 'paid' })
await send($.Fulfillment.start, { orderId: payment.orderId })
})
on($.Fulfillment.start, async (fulfillment) => {
await send($.Email.send, {
to: fulfillment.customer.email,
subject: 'Order Processing'
})
})
// Execute test order flow
const testCustomer = await db.create('Person', {
name: 'Test Customer',
email: '[email protected]'
})
const testOrder = await db.create('Order', {
customer: testCustomer,
items: [$.Product({ name: 'Test Widget', price: 10 })],
total: 10,
status: 'test'
})
// Trigger workflow
await send($.Order.created, testOrder)
// Return test results
{
workflowSetup: 'complete',
handlersRegistered: 3,
testOrder: {
id: testOrder.id,
status: 'test',
workflowTriggered: true
},
note: 'Workflow is active and tested'
}Pattern 4: Monitor Setup + Current Metrics
Set up monitoring, then return current metrics:
// Set up monitoring handlers
on($.Error.occurred, async (error) => {
await send($.Alert.send, {
type: 'error',
severity: error.severity,
message: error.message
})
await db.create('ErrorLog', {
type: error.type,
message: error.message,
stack: error.stack,
timestamp: new Date()
})
})
on($.Alert.send, async (alert) => {
if (alert.severity === 'critical') {
await send($.Notification.sms, {
phoneNumber: '+1-555-ON-CALL',
message: `CRITICAL: ${alert.message}`
})
}
})
// Check for recent errors
const recentErrors = await db.list('ErrorLog', {
where: {
timestamp: { gte: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString() }
}
})
const criticalErrors = recentErrors.filter(e => e.severity === 'critical')
{
monitoringActive: true,
handlersRegistered: 2,
last24Hours: {
totalErrors: recentErrors.length,
criticalErrors: criticalErrors.length,
errorRate: (recentErrors.length / 24).toFixed(2) + ' per hour'
},
status: criticalErrors.length > 0 ? 'ALERT' : 'OK'
}Pattern 5: Integration Setup + Validation
Set up external integrations, then validate they work:
// Set up webhook handlers
on($.Webhook.stripe, async (webhook) => {
console.log(`Stripe webhook: ${webhook.type}`)
if (webhook.type === 'payment_intent.succeeded') {
await send($.Payment.succeeded, {
orderId: webhook.data.object.metadata.orderId,
amount: webhook.data.object.amount / 100
})
}
})
on($.Webhook.shippo, async (webhook) => {
console.log(`Shippo webhook: ${webhook.event}`)
if (webhook.event === 'track_updated' && webhook.data.tracking_status.status === 'DELIVERED') {
await send($.Order.delivered, {
orderId: webhook.data.metadata.orderId
})
}
})
// Test webhook connectivity
const stripeTest = await api.fetch('https://api.stripe.com/v1/webhooks/test', {
method: 'POST',
headers: { 'Authorization': 'Bearer sk_test_...' }
})
const shippoTest = await api.fetch('https://api.goshippo.com/webhooks/test', {
method: 'POST',
headers: { 'Authorization': 'ShippoToken shippo_test_...' }
})
{
webhooksConfigured: 2,
handlers: ['Stripe payments', 'Shippo tracking'],
validation: {
stripe: stripeTest.ok ? 'Connected' : 'Failed',
shippo: shippoTest.ok ? 'Connected' : 'Failed'
},
status: stripeTest.ok && shippoTest.ok ? 'Ready' : 'Configuration needed'
}Decision Matrix
Use this matrix to quickly determine which execution model to use:
| Scenario | Use Script | Use Module | Use Both | Reason |
|---|---|---|---|---|
| Query database | ✅ | ❌ | ❌ | Need immediate result |
| One-time data migration | ✅ | ❌ | ❌ | Execute once and complete |
| Generate report | ✅ | ❌ | ❌ | Return report data |
| Test API functionality | ✅ | ❌ | ❌ | Return test results |
| React to order creation | ❌ | ✅ | ❌ | Persistent event handling |
| Send email notifications | ❌ | ✅ | ❌ | Triggered by events |
| Run hourly cleanup | ❌ | ✅ | ❌ | Scheduled recurring task |
| Process payment workflow | ❌ | ✅ | ❌ | Multi-step orchestration |
| Setup workflow + test | ❌ | ❌ | ✅ | Register + validate |
| Register handlers + status | ❌ | ❌ | ✅ | Setup + report state |
| Monitor setup + metrics | ❌ | ❌ | ✅ | Create handlers + query |
| Daily scheduled report | ❌ | ✅ | ❌ | Recurring with side effects |
| Analyze customer data | ✅ | ❌ | ❌ | Need computed result |
| Sync to external system | ❌ | ✅ | ❌ | Event-driven integration |
| Debug database state | ✅ | ❌ | ❌ | Inspect and return |
Decision Tree
Do you need to return data to the AI model?
├─ YES → Use Script
│ └─ Examples: queries, reports, analysis
│
└─ NO → Do you need persistent behavior?
├─ YES → Use Module
│ └─ Examples: event handlers, scheduled tasks
│
└─ NO → Use Script (for side effects only)
└─ Examples: one-time updates, migrations
Do you need both setup AND results?
└─ YES → Use Both (Module + Script)
└─ Examples: register handlers + return statusCommon Mistakes
Mistake 1: Using module for one-time query
// ❌ Bad: Module for query (handler persists unnecessarily)
on($.Query.business, async () => {
return await db.list('Business')
})
// ✅ Good: Script for query
await db.list('Business')Mistake 2: Using script for event handling
// ❌ Bad: Script (handler doesn't persist)
on($.Order.created, async (order) => {
await send($.Email.send, { to: order.customer.email })
})
// Handler is registered but script completes, behavior unclear
// ✅ Good: Module (makes persistence explicit)
// Use as module - handler persists and processes all future Order.created eventsMistake 3: Not returning from script
// ❌ Bad: Script with no return value
const orders = await db.list('Order')
const total = orders.reduce((sum, o) => sum + o.total, 0)
// Missing return - AI model gets undefined
// ✅ Good: Return result
const orders = await db.list('Order')
const total = orders.reduce((sum, o) => sum + o.total, 0)
{ orderCount: orders.length, totalRevenue: total }Mistake 4: Over-complex modules
// ❌ Bad: Too much logic in one handler
on($.Order.created, async (order) => {
// 200 lines of complex logic
// Hard to maintain, test, debug
})
// ✅ Good: Break into smaller handlers
on($.Order.created, async (order) => {
await send($.Email.send, { to: order.customer.email })
await send($.Inventory.reserve, { orderId: order.id })
await send($.Analytics.track, { event: 'order_created' })
})
on($.Inventory.reserve, async (event) => {
// Handle inventory reservation
})
on($.Analytics.track, async (event) => {
// Handle analytics
})Mistake 5: Missing error handling
// ❌ Bad: No error handling
on($.Payment.process, async (payment) => {
const result = await api.proxy('stripe', '/charges', {
method: 'POST',
body: { amount: payment.amount },
})
// If this fails, the event is lost
})
// ✅ Good: Proper error handling
on($.Payment.process, async (payment) => {
try {
const result = await api.proxy('stripe', '/charges', {
method: 'POST',
body: { amount: payment.amount },
})
await send($.Payment.succeeded, {
orderId: payment.orderId,
chargeId: result.id,
})
} catch (error) {
await send($.Payment.failed, {
orderId: payment.orderId,
error: error.message,
})
}
})Advanced Patterns
Pattern: Module Composition and Reusability
Create reusable handler factories:
// Create generic notification handler factory
function createNotificationHandler(eventPattern, emailConfig) {
on(eventPattern, async (data) => {
await send($.Email.send, {
to: emailConfig.recipient,
subject: emailConfig.subject(data),
body: emailConfig.body(data),
})
})
}
// Use the factory to create specific handlers
createNotificationHandler($.Order.created, {
recipient: (data) => data.customer.email,
subject: (data) => `Order Confirmation #${data.orderNumber}`,
body: (data) => `Your order for $${data.total} has been confirmed.`,
})
createNotificationHandler($.Order.shipped, {
recipient: (data) => data.customer.email,
subject: (data) => `Order Shipped #${data.orderNumber}`,
body: (data) => `Tracking: ${data.trackingNumber}`,
})Pattern: Error Handling Strategies
Implement retry logic and error recovery:
// Retry helper
async function withRetry(fn, maxRetries = 3, delayMs = 1000) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn()
} catch (error) {
if (attempt === maxRetries) throw error
await new Promise((resolve) => setTimeout(resolve, delayMs * attempt))
}
}
}
// Use in handlers
on($.Payment.process, async (payment) => {
try {
const result = await withRetry(async () => {
return await api.proxy('stripe', '/charges', {
method: 'POST',
body: { amount: payment.amount },
})
})
await send($.Payment.succeeded, {
orderId: payment.orderId,
chargeId: result.id,
})
} catch (error) {
// Log error
await db.create('PaymentError', {
orderId: payment.orderId,
error: error.message,
attempts: 3,
timestamp: new Date(),
})
// Trigger failure handler
await send($.Payment.failed, {
orderId: payment.orderId,
reason: error.message,
})
}
})Pattern: Testing Strategies
Test scripts with immediate feedback:
// Test script with assertions
const result = await (async () => {
// Setup test data
const testCustomer = await db.create('Person', {
name: 'Test',
email: '[email protected]',
})
// Execute operation
const order = await db.create('Order', {
customer: testCustomer,
total: 100,
})
// Assertions
const assertions = {
customerCreated: testCustomer.id !== undefined,
orderCreated: order.id !== undefined,
orderHasCustomer: order.customerId === testCustomer.id,
totalCorrect: order.total === 100,
}
// Cleanup
await db.delete('Order', order.id)
await db.delete('Person', testCustomer.id)
return {
test: 'Order creation flow',
passed: Object.values(assertions).every((v) => v),
assertions,
}
})()
resultTest modules by triggering events:
// Register handler
on($.Order.created, async (order) => {
await send($.Email.send, {
to: order.customer.email,
subject: 'Order Confirmation'
})
})
// Test by sending event
await send($.Order.created, {
id: 'test_123',
orderNumber: 'TEST-001',
customer: {
email: '[email protected]'
},
total: 100
})
// Check email was sent (query email service or logs)
const emailLogs = await db.list('EmailLog', {
where: { recipient: '[email protected]' },
limit: 1,
sort: { createdAt: 'desc' }
})
{
test: 'Order created handler',
emailSent: emailLogs.length > 0,
recipient: emailLogs[0]?.recipient,
subject: emailLogs[0]?.subject
}Pattern: Performance Optimization
Batch operations for better performance:
// Script: Batch process products
const products = await db.list('Product')
// ❌ Bad: Sequential processing
for (const product of products) {
const description = await ai.generate({
prompt: `Describe ${product.name}`,
schema: $.Text,
})
await db.update('Product', product.id, { description: description.text })
}
// ✅ Good: Parallel batching
const batchSize = 10
for (let i = 0; i < products.length; i += batchSize) {
const batch = products.slice(i, i + batchSize)
await Promise.all(
batch.map(async (product) => {
const description = await ai.generate({
prompt: `Describe ${product.name}`,
schema: $.Text,
})
await db.update('Product', product.id, {
description: description.text,
})
})
)
}Pattern: State Management
Maintain state across handler invocations:
// Use database for state
on($.Order.created, async (order) => {
// Get or create state
let state = await db.list('WorkflowState', {
where: { orderId: order.id },
})
if (state.length === 0) {
state = await db.create('WorkflowState', {
orderId: order.id,
step: 'created',
data: {},
})
} else {
state = state[0]
}
// Update state
await db.update('WorkflowState', state.id, {
step: 'processing',
data: { ...state.data, processedAt: new Date() },
})
// Continue workflow
await send($.Payment.process, { orderId: order.id })
})Pattern: Dependency Injection
Make handlers testable with dependency injection:
// Injectable services
const services = {
email: {
send: async (to, subject, body) => {
return await send($.Email.send, { to, subject, body })
},
},
db: {
updateOrder: async (id, data) => {
return await db.update('Order', id, data)
},
},
}
// Handler uses injected services
function createOrderHandler(deps = services) {
return on($.Order.created, async (order) => {
await deps.email.send(order.customer.email, 'Order Confirmation', `Order ${order.id} confirmed`)
await deps.db.updateOrder(order.id, {
emailSent: true,
})
})
}
// Production use
createOrderHandler()
// Test use (with mocks)
// createOrderHandler({ email: mockEmail, db: mockDb })Anti-Patterns
Anti-Pattern 1: Module for One-Time Operations
Problem: Using persistent handlers for operations that should run once.
// ❌ Bad: Module for one-time query
on($.Business.list, async () => {
return await db.list('Business')
})
// Handler persists but is designed for one-time use
// ✅ Good: Script for one-time query
await db.list('Business')Why it's bad: Creates unnecessary persistent subscriptions, wastes resources, confuses intent.
Anti-Pattern 2: Script for Persistent Behavior
Problem: Trying to set up event handlers in a script without making persistence explicit.
// ❌ Bad: Unclear if handlers persist
on($.Order.created, handler)
on($.Payment.succeeded, handler)
// Script completes - do handlers persist?
// ✅ Good: Make it explicit (use module or document behavior)
// As a module: handlers persist
on($.Order.created, handler)
on($.Payment.succeeded, handler)
// Or return status in combined pattern
on($.Order.created, handler)
{ status: 'handlers registered', persistent: true }Anti-Pattern 3: Not Returning Values from Scripts
Problem: Script does work but doesn't return results.
// ❌ Bad: No return value
const orders = await db.list('Order')
const total = orders.reduce((sum, o) => sum + o.total, 0)
// AI model receives undefined
// ✅ Good: Return structured result
const orders = await db.list('Order')
const total = orders.reduce((sum, o) => sum + o.total, 0)
{
orderCount: orders.length,
totalRevenue: total,
avgOrderValue: total / orders.length
}Anti-Pattern 4: Over-Complex Modules
Problem: Putting too much logic in a single handler.
// ❌ Bad: Monolithic handler
on($.Order.created, async (order) => {
// Send email
await send($.Email.send, { to: order.customer.email, ... })
// Update inventory (50 lines)
// Process payment (50 lines)
// Generate invoice (50 lines)
// Send to fulfillment (50 lines)
// Update analytics (50 lines)
// etc. (300+ lines total)
})
// ✅ Good: Composed handlers
on($.Order.created, async (order) => {
await send($.Email.send, { to: order.customer.email })
await send($.Inventory.update, { orderId: order.id })
await send($.Payment.process, { orderId: order.id })
})
on($.Inventory.update, async (event) => {
// Handle inventory logic
})
on($.Payment.process, async (event) => {
// Handle payment logic
})Anti-Pattern 5: Missing Error Handling
Problem: Not handling errors in async operations.
// ❌ Bad: No error handling
on($.Payment.process, async (payment) => {
const result = await api.proxy('stripe', '/charges', {
method: 'POST',
body: { amount: payment.amount },
})
// If this fails, event is lost
})
// ✅ Good: Comprehensive error handling
on($.Payment.process, async (payment) => {
try {
const result = await api.proxy('stripe', '/charges', {
method: 'POST',
body: { amount: payment.amount },
})
await send($.Payment.succeeded, {
orderId: payment.orderId,
chargeId: result.id,
})
} catch (error) {
console.error(`Payment processing failed: ${error.message}`)
await db.create('PaymentError', {
orderId: payment.orderId,
error: error.message,
timestamp: new Date(),
})
await send($.Payment.failed, {
orderId: payment.orderId,
reason: error.message,
})
}
})Anti-Pattern 6: Polling Instead of Events
Problem: Using scheduled tasks to poll for changes instead of event-driven handlers.
// ❌ Bad: Poll for new orders every minute
every('* * * * *', async () => {
const newOrders = await db.list('Order', {
where: {
status: 'new',
processed: false,
},
})
for (const order of newOrders) {
await processOrder(order)
await db.update('Order', order.id, { processed: true })
}
})
// ✅ Good: Event-driven
on($.Order.created, async (order) => {
await processOrder(order)
})Anti-Pattern 7: Ignoring Idempotency
Problem: Not making handlers idempotent, causing duplicate processing.
// ❌ Bad: Not idempotent
on($.Order.created, async (order) => {
// Always sends email, even if already sent
await send($.Email.send, {
to: order.customer.email,
subject: 'Order Confirmation',
})
})
// ✅ Good: Idempotent
on($.Order.created, async (order) => {
// Check if already processed
const existing = await db.list('EmailLog', {
where: {
orderId: order.id,
type: 'order-confirmation',
},
})
if (existing.length > 0) {
console.log(`Email already sent for order ${order.id}`)
return
}
// Send email
await send($.Email.send, {
to: order.customer.email,
subject: 'Order Confirmation',
})
// Log
await db.create('EmailLog', {
orderId: order.id,
type: 'order-confirmation',
sentAt: new Date(),
})
})Anti-Pattern 8: Tight Coupling
Problem: Handlers that depend on specific implementation details.
// ❌ Bad: Tightly coupled
on($.Order.created, async (order) => {
// Assumes specific Stripe implementation
const result = await api.proxy('stripe', '/v1/charges', {
method: 'POST',
body: {
amount: order.total * 100,
currency: 'usd',
source: order.stripeToken,
},
})
})
// ✅ Good: Loosely coupled via events
on($.Order.created, async (order) => {
// Trigger payment processing (implementation-agnostic)
await send($.Payment.process, {
orderId: order.id,
amount: order.total,
})
})
// Separate payment handler
on($.Payment.process, async (payment) => {
// Payment implementation can change without affecting order handler
const result = await api.proxy('stripe', '/v1/charges', {
method: 'POST',
body: {
amount: payment.amount * 100,
currency: 'usd',
},
})
})Next Steps
- The
doTool - Complete tool reference - Core Primitives - SDK modules guide
- Examples - Real-world patterns
- Authentication - Security & permissions