Transactions
Execute multiple database operations atomically using WebSocket-based transactions. All operations within a transaction either succeed together or fail together.
Overview
Transactions allow you to:
- Execute multiple operations as a single atomic unit
- Read your own uncommitted changes within the transaction
- Rollback all changes if any operation fails
- Choose isolation levels for concurrent access control
Message Protocol
All messages are JSON objects with a type field and optional messageId for request/response correlation.
Begin Transaction
Start a new transaction:
{
"type": "begin",
"messageId": "msg-1",
"isolationLevel": "ReadCommitted",
"autoCommitOnDisconnect": false
}
Options:
| Field | Type | Default | Description |
|---|---|---|---|
isolationLevel | string | ReadCommitted | Transaction isolation level |
autoCommitOnDisconnect | boolean | false | Auto-commit when connection closes |
Isolation Levels:
| Level | Description |
|---|---|
ReadCommitted | See only committed data from other transactions |
Serializable | Strictest isolation, transactions execute as if serial |
Response:
{
"type": "success",
"messageId": "msg-1",
"data": {
"isolationLevel": "ReadCommitted",
"autoCommitOnDisconnect": false
}
}
Execute Operations
Execute HTTP operations within the transaction:
{
"type": "execute",
"messageId": "msg-2",
"method": "POST",
"path": "products",
"body": {
"name": "Widget",
"price": 29.99
}
}
Fields:
| Field | Type | Description |
|---|---|---|
method | string | HTTP method: GET, POST, PUT, PATCH, DELETE |
path | string | Collection path (e.g., products or products/{id}) |
body | object | Request body for POST, PUT, PATCH |
Response:
{
"type": "success",
"messageId": "msg-2",
"statusCode": 200,
"data": {
"data": ["f38fa478-842e-4599-8cbc-918a34b3b789"]
}
}
Commit Transaction
Persist all changes:
{
"type": "commit",
"messageId": "msg-3"
}
Response:
{
"type": "success",
"messageId": "msg-3",
"data": {
"status": "committed"
}
}
Rollback Transaction
Discard all changes:
{
"type": "rollback",
"messageId": "msg-4"
}
Response:
{
"type": "success",
"messageId": "msg-4",
"data": {
"status": "rolled back"
}
}
Error Handling
Errors return an error type response:
{
"type": "error",
"messageId": "msg-2",
"error": "No active transaction"
}
Common Errors:
| Error | Cause |
|---|---|
No active transaction | Execute/commit/rollback without begin |
Invalid isolation level | Unrecognized isolation level |
Transaction already active | Begin when transaction exists |
Example Flow
Creating an order with line items atomically:
// 1. Begin transaction
send({ type: 'begin', messageId: '1' });
// Response: { type: 'success', messageId: '1', data: { isolationLevel: 'ReadCommitted' } }
// 2. Create order
send({
type: 'execute',
messageId: '2',
method: 'POST',
path: 'orders',
body: { customerId: 'c123', total: 99.99 }
});
// Response: { type: 'success', messageId: '2', statusCode: 200, data: { data: ['order-id-123'] } }
// 3. Create order items
send({
type: 'execute',
messageId: '3',
method: 'POST',
path: 'order-items',
body: { orderId: 'order-id-123', productId: 'p456', quantity: 2 }
});
// Response: { type: 'success', messageId: '3', statusCode: 200, data: { data: ['item-id-456'] } }
// 4. Commit transaction
send({ type: 'commit', messageId: '4' });
// Response: { type: 'success', messageId: '4', data: { status: 'committed' } }
If any operation fails, send a rollback to discard all changes:
send({ type: 'rollback', messageId: '5' });
// Response: { type: 'success', messageId: '5', data: { status: 'rolled back' } }
Auto-Commit on Disconnect
When autoCommitOnDisconnect is true, the transaction automatically commits if the WebSocket connection closes without an explicit commit or rollback.
When false (default), disconnection triggers an automatic rollback, ensuring no partial changes are persisted.
Use Cases
| Scenario | Description |
|---|---|
| Order processing | Create order and line items atomically |
| Inventory updates | Deduct stock and create order together |
| Account transfers | Debit one account and credit another |
| Bulk operations | Insert multiple related records |
| Data migrations | Update related records consistently |
Best Practices
- Keep transactions short to avoid blocking other operations
- Use
ReadCommittedunless you specifically needSerializable - Always handle errors and implement retry logic
- Set
autoCommitOnDisconnect: truefor fire-and-forget operations - Close the WebSocket connection after commit/rollback