Error Boundaries ๐ก๏ธ
Keep your workflows resilient with error boundaries - they're like try-catch blocks for your workflow! Protect nodes, handle failures gracefully, and keep your workflow running smoothly.
All error boundaries in Graph Compose represent HTTP endpoints that you own and implement. The error boundary definitions in your workflow specify how Graph Compose should interact with these endpoints when errors occur in protected nodes.
Why Use Error Boundaries? ๐ค
Error boundaries are your workflow's safety net! They help you:
- ๐ก๏ธ Protect critical nodes from failures
- ๐งน Run cleanup operations when things go wrong
- ๐ Implement recovery strategies
- ๐ Track and handle errors elegantly
- ๐ Keep parts of your workflow running even when some nodes fail
๐ก Think of error boundaries as your workflow's guardian angels - they catch problems and help you handle them gracefully!
Basic Usage โจ
Wrap your nodes in an error boundary to handle failures gracefully:
import { GraphCompose } from '@graph-compose/client';
const workflow = new GraphCompose()
// Add payment node
.node('payment')
.post('https://api.example.com/payment')
.end()
// Add error boundary
.errorBoundary('payment_error_handler', ['payment'])
.post('https://api.example.com/payment-cleanup')
.end();
โ ๏ธ Important: Your error boundary endpoint will receive a request where the errorContext
object is automatically added to the request body. This context includes details about the failure. Any results from successfully completed dependency nodes will also be available for use in the error boundary's configuration (e.g., in the URL or body using {{ results.someNode.field }}
).
Example Error Boundary Request Body
If the payment
node fails, the request sent to https://api.example.com/payment-cleanup
would look like this (assuming no other body fields were defined for the error boundary itself):
{
"errorContext": {
"message": "Payment processing failed due to insufficient funds", // Example error message
"failedNodeId": "payment",
"timestamp": 1710000000000 // Example timestamp
}
}
If you defined a body in your error boundary configuration, the errorContext
would be merged into it:
SDK with Body
// ... inside workflow definition
.errorBoundary('payment_error_handler', ['payment'])
.post('https://api.example.com/payment-cleanup')
.withBody({
transactionId: "{{ results.some_previous_node.transactionId }}", // Data from other nodes
reason: "Cleanup requested"
})
.end();
Resulting Request Body
{
"transactionId": "txn_12345", // Resolved from results.some_previous_node
"reason": "Cleanup requested",
"errorContext": {
"message": "Payment processing failed...",
"failedNodeId": "payment",
"timestamp": 1710000000000
}
}
Multiple Boundaries ๐ฏ
You can have multiple boundaries protecting different parts of your workflow. We'll always execute the most specific (closest) error boundary - just like how try-catch blocks work!
import { GraphCompose } from '@graph-compose/client';
const workflow = new GraphCompose()
// Add workflow nodes
.node('fetch_user')
.get('https://api.example.com/user')
.end()
.node('fetch_orders')
.withDependencies(['fetch_user'])
.get('https://api.example.com/orders')
.end()
.node('process_orders')
.withDependencies(['fetch_orders'])
.post('https://api.example.com/process')
.end()
// Add specific error handler
.errorBoundary('specific_error_handler', ['fetch_orders'])
.post('https://api.example.com/order-error')
.end()
// Add general error handler
.errorBoundary('general_error_handler', ['fetch_user', 'fetch_orders', 'process_orders'])
.post('https://api.example.com/general-error')
.end();
๐ก Pro tip: When multiple error boundaries protect the same node, only the most specific one will be executed. This lets you handle errors at the right level of granularity!
How Specificity Works ๐ฏ
When an error occurs, we find the most specific error boundary:
- First, we look for boundaries that directly protect the failed node
- If multiple boundaries protect the node, we use the one that protects the fewest nodes (most specific)
- Only one error boundary will execute - the most specific one
- Other error boundaries are ignored, just like how only one catch block handles an error
Common Use Cases ๐ฏ
Transaction Rollback
Roll back partial transactions when operations fail, such as releasing inventory holds when payments fail.
Notification & Logging
Send alerts and log detailed error information when critical operations fail.
Fallback Logic
Implement fallback mechanisms like trying backup services or providing degraded service.
Cleanup Operations ๐งน
import { GraphCompose } from '@graph-compose/client';
const workflow = new GraphCompose()
.node('create_resources')
.post('https://api.example.com/resources')
.end()
.errorBoundary('cleanup_handler', ['create_resources'])
.post('https://api.example.com/cleanup')
.end();
Retry Logic ๐
import { GraphCompose } from '@graph-compose/client';
const workflow = new GraphCompose()
.node('process_data')
.post('https://api.example.com/process')
.end()
.errorBoundary('retry_handler', ['process_data'])
.post('https://api.example.com/retry')
.end();
Best Practices ๐ก
Make your error boundaries work for you:
- ๐ฏ Protect critical operations
- ๐ Log error details for debugging
- ๐งน Always clean up resources on failure
- ๐ Track error patterns
- ๐ Implement smart recovery strategies
- โก Use specific error boundaries for specific failure modes
- ๐ก๏ธ Layer error boundaries for different levels of protection
Ready to make your workflows more resilient? Let's go! ๐