Data Request
How to request data through The Butler's Data API. Butlers request data through The Butler instead of storing it themselves. The shell brokers Data API calls, enforces permissions, and returns only what each Butler is allowed to see.
Overview
Butlers are stateless and do not store user data directly. All data operations go through The Butler's Data API, which handles storage, permissions, and security. This ensures data consistency, security, and enables cross-Butler data linking.
Query Data
Query data from the unified data layer using the Data API.
Basic Queries
// Query entities
const expenses = await window.lifeButler.data.query('Transaction', {
where: {
category: 'groceries',
date: { $gte: '2024-01-01' }
},
include: ['contact', 'receipt'],
orderBy: { date: 'desc' },
limit: 50
});
// Query single entity
const expense = await window.lifeButler.data.get('Transaction', 'expense-123', {
include: ['contact', 'receipt']
});Advanced Queries
// Complex queries with filters
const results = await window.lifeButler.data.query('Transaction', {
where: {
$and: [
{ amount: { $gte: 50 } },
{ category: { $in: ['groceries', 'dining'] } },
{ date: { $between: ['2024-01-01', '2024-01-31'] } }
]
},
include: ['contact'],
orderBy: { amount: 'desc' },
limit: 20,
offset: 0
});
// Count queries
const count = await window.lifeButler.data.count('Transaction', {
where: { category: 'groceries' }
});Create & Update Data
Create and update data through the Data API. All operations are validated and permission-checked by The Butler.
Create Entities
// Create new entity
const expense = await window.lifeButler.data.create('Transaction', {
amount: 25.50,
category: 'groceries',
date: '2024-01-15',
merchant: 'Grocery Store',
notes: 'Weekly groceries'
});
// Create with linked entities
const expense = await window.lifeButler.data.create('Transaction', {
amount: 25.50,
category: 'groceries',
date: '2024-01-15',
links: {
contact: 'contact-123',
receipt: 'receipt-456'
}
});Update Entities
// Update entity
const updated = await window.lifeButler.data.update('Transaction', 'expense-123', {
amount: 30.00,
notes: 'Updated amount'
});
// Partial update (only specified fields)
const updated = await window.lifeButler.data.patch('Transaction', 'expense-123', {
notes: 'Added note'
});Delete Entities
// Delete entity
await window.lifeButler.data.delete('Transaction', 'expense-123');
// Soft delete (mark as deleted but keep data)
await window.lifeButler.data.softDelete('Transaction', 'expense-123');Data Subscriptions
Subscribe to data changes to keep your UI in sync with the data layer.
// Subscribe to data changes
const unsubscribe = await window.lifeButler.data.subscribe('Transaction', {
where: { category: 'groceries' }
}, (changes) => {
// Handle changes
changes.forEach(change => {
if (change.type === 'create') {
addExpenseToList(change.entity);
} else if (change.type === 'update') {
updateExpenseInList(change.entity);
} else if (change.type === 'delete') {
removeExpenseFromList(change.entityId);
}
});
});
// Unsubscribe when done
unsubscribe();Permissions
The Butler enforces permissions for all data operations. Your Butler must declare the data access it needs in the manifest.
Permission Declaration
// In your Butler manifest
{
"permissions": {
"data": {
"read": ["Transaction", "Contact"],
"write": ["Transaction"],
"explanation": "Need to read and create transactions for expense tracking"
}
}
}Permission Errors
If your Butler tries to access data without permission, The Butler will return an error. Handle these gracefully:
try {
const expenses = await window.lifeButler.data.query('Transaction', { ... });
} catch (error) {
if (error.code === 'PERMISSION_DENIED') {
// Request permission or show message
await window.lifeButler.requestPermission({
type: 'data',
action: 'read',
resource: 'Transaction'
});
}
}Requirements
- Use Data API: All data operations must go through The Butler's Data API. Do not store data directly.
- Declare permissions: Declare all data access permissions in your Butler manifest.
- Handle errors: Handle permission errors and other data API errors gracefully.
- Subscribe to changes: Use data subscriptions to keep your UI in sync with the data layer.
- Respect permissions: Only request data access that your Butler actually needs.