Building Stateful Workflows in JavaScript: A Guide to Codehooks Workflow API
· 4 min read
At Codehooks, our mission is to simplify the development of automations, integrations, and backend APIs. As your app logic grows more complex — onboarding flows, async jobs, conditional steps — it becomes clear: You need a better way to organize and execute business logic.
That's why we built the Codehooks Workflow API — a lightweight, serverless-friendly way to run stateful workflows in JavaScript, without the overhead of bulky orchestrators or external tools.
✨ Why Workflows Matter
- Step-by-step Logic: Break down complex tasks into simple, ordered steps. Each step works independently, making it easy to find and fix issues.
- Retries and Conditional Branches: Automatically retry failed steps and choose different paths based on conditions. Like an if/else statement, but for your whole workflow.
- Pause/Wait States and Resume Triggers: Pause workflows until something happens, like getting an API response or user approval. Continues automatically when ready.
- Persistent State Management: Safely saves all your workflow data between steps. Keeps track of everything, even if there are errors or delays.
- Cloud-scale Execution: Runs smoothly in the cloud with multiple workflows at once. Each workflow runs separately without interfering with others.
- Code-First & LLM-Ready: Define workflows in plain JavaScript code, making them perfect for LLM-assisted development. AI can help write, review, and improve your workflows just like any other code.
Getting Started: Your First Workflow
index.js
import { app } from 'codehooks-js'
const workflow = app.createWorkflow('parityCheck', 'Check if a number is even or odd', {
begin: async (state, goto) => {
state.number = Math.floor(Math.random() * 100)
goto('check', state)
},
check: async (state, goto) => {
const step = (state.number % 2 === 0) ? 'even' : 'odd'
goto(step, state) // branch
},
even: async (state, goto) => {
console.log(`${state.number} is even`)
goto('end', state)
},
odd: async (state, goto) => {
console.log(`${state.number} is odd`)
goto('end', state)
},
end: async (state, goto) => {
console.log('Workflow finished', state)
goto(null, state) // null complete the workflow
}
})
// REST API to start a new workflow instance
app.post('/start', async (req, res) => {
const result = await workflow.start('parityCheck', {"Initial": "state"});
res.json(result);
});
// export app interface to serverless runtime
export default app.init();
The above JavaScript workflow code can also be visualized as a diagram like the Mermaid diagram shown below:
Designed for Developers
- JavaScript-native: Write workflows in plain JavaScript using async/await. No special syntax to learn - just write normal JavaScript code.
- Composable: Break workflows into reusable modules that you can import and combine. Add middleware for common functionality like logging or error handling.
- Serverless: Built for serverless from the ground up. Stores workflow state separately from functions to keep memory usage low.
- Distributed-safe: Handles concurrent updates safely across multiple workers. Uses atomic operations to prevent data conflicts.
🤖 LLM-Friendly Development
- Code-First Approach: Since workflows are defined in plain JavaScript, LLMs can easily understand, generate, and modify workflow code. No need to translate between visual tools and code.
- Self-Documenting: Each step is a named function with clear inputs and outputs, making it easy for LLMs to analyze and explain workflow logic.
- Pattern Recognition: Common workflow patterns are expressed as standard JavaScript patterns, allowing LLMs to suggest improvements and best practices.
- Error Prevention: LLMs can help catch common workflow issues like missing state transitions or infinite loops before runtime.
Common Use Cases
- User Onboarding: Implement multi-step registration flows with validation, OAuth integration, and role-based access control. Handle edge cases like partial completions and timeouts.
- Approval Flows: Create approval workflows where multiple people need to review and approve something. Track who approved what and when, and send reminders for pending approvals.
- Data Sync and Async Jobs: Process large datasets with checkpointing and resumable execution. Includes built-in support for rate limiting, batching, and error handling.
- External Event Triggers: Listen for events from outside sources like webhooks and message queues. Handles duplicate events and ensures reliable processing.
🚀 Ready to Build Smarter Backends?