Skip to main content

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:

FieldTypeDefaultDescription
isolationLevelstringReadCommittedTransaction isolation level
autoCommitOnDisconnectbooleanfalseAuto-commit when connection closes

Isolation Levels:

LevelDescription
ReadCommittedSee only committed data from other transactions
SerializableStrictest 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:

FieldTypeDescription
methodstringHTTP method: GET, POST, PUT, PATCH, DELETE
pathstringCollection path (e.g., products or products/{id})
bodyobjectRequest 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:

ErrorCause
No active transactionExecute/commit/rollback without begin
Invalid isolation levelUnrecognized isolation level
Transaction already activeBegin 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

ScenarioDescription
Order processingCreate order and line items atomically
Inventory updatesDeduct stock and create order together
Account transfersDebit one account and credit another
Bulk operationsInsert multiple related records
Data migrationsUpdate related records consistently

Best Practices

  • Keep transactions short to avoid blocking other operations
  • Use ReadCommitted unless you specifically need Serializable
  • Always handle errors and implement retry logic
  • Set autoCommitOnDisconnect: true for fire-and-forget operations
  • Close the WebSocket connection after commit/rollback