Skip to main content

What is a Flow?

In BuilderBot, a flow represents a conversation path or dialogue structure. The FlowClass manages the flow system, handling flow manipulation, creating callback indices, and enabling keyword-based navigation through conversations.

Creating Flows

Flows are created using the createFlow function, which accepts an array of flow definitions:
import { createFlow, addKeyword } from '@builderbot/bot'

const welcomeFlow = addKeyword(['hi', 'hello'])
  .addAnswer('Welcome to our chatbot!')

const helpFlow = addKeyword('help')
  .addAnswer('How can I help you?')

const adapterFlow = createFlow([welcomeFlow, helpFlow])

FlowClass Architecture

The FlowClass is the core engine that manages all flows in your bot. It provides several key capabilities:

Internal Structure

class FlowClass {
  allCallbacks: Record<string, Function>
  flowSerialize: TContext[]
  flowRaw: TFlow[]
}
Key properties:
  • allCallbacks - Index of all callback functions across flows
  • flowSerialize - Serialized representation of flows for efficient matching
  • flowRaw - Original flow definitions

Flow Matching

The find() method is the heart of flow matching, supporting multiple matching strategies:
find(keyOrWord: string, symbol: boolean = false, overFlow: TContext[] | null = null): TContext[]
1

Keyword Matching

When a user message arrives, the flow system searches for matching keywords using case-insensitive or case-sensitive matching.
2

Symbol Resolution

If symbol is true, it performs direct reference lookup by flow ID rather than text matching.
3

Nested Flow Discovery

The system recursively follows flow references to build the complete conversation path.

Matching Options

The flow system supports sophisticated matching through options: Case Sensitivity:
const flow = addKeyword('HELLO', { sensitive: true })
// Only matches "HELLO" exactly
Regex Patterns:
const flow = addKeyword('/^\d{3,}$/', { regex: true })
// Matches any message with 3+ digits
Multiple Keywords:
const flow = addKeyword(['hi', 'hello', 'hey'])
// Matches any of the keywords

Flow Serialization

When flows are created, they’re serialized into an optimized format for fast lookup:
const mergeToJsonSerialize = _flow.map((flowItem) => flowItem.toJson()).flat(2)
this.flowSerialize = toSerialize(mergeToJsonSerialize)
This serialization:
  • Flattens nested flow structures
  • Creates unique references for each step
  • Builds an index for O(1) callback lookups

Flow Methods

Finding Flows

The FlowClass provides multiple methods to locate flows:
findBySerialize(refSerialize: string, k: number = 0): TContext | undefined
Find a flow by its serialized reference, with optional offset.

Nested Flows

Access child flows within a parent flow:
getFlowsChild(): TContext[]
This method:
  • Extracts all nested flows from the main flow structure
  • Flattens the hierarchy for easy iteration
  • Filters out undefined references

Flow Structure (TFlow)

Every flow object implements the TFlow interface:
interface TFlow<P = any, B = any> {
  ctx: TContext                    // Flow context
  ref: string                      // Unique reference
  addAnswer: (...) => TFlow<P, B>  // Add response
  addAction: (...) => TFlow<P, B>  // Add callback
  toJson: () => TContext[]         // Serialize
}

Practical Example

Here’s a complete flow demonstrating key concepts:
import { createFlow, addKeyword } from '@builderbot/bot'

// Main welcome flow with nested options
const welcomeFlow = addKeyword(['hi', 'hello', 'hola'])
  .addAnswer('Welcome! How can I help you today?')
  .addAnswer(
    'Choose an option:\n1. Documentation\n2. Support\n3. Pricing',
    { capture: true },
    async (ctx, { gotoFlow }) => {
      if (ctx.body.includes('1')) {
        return gotoFlow(docFlow)
      }
      if (ctx.body.includes('2')) {
        return gotoFlow(supportFlow)
      }
      if (ctx.body.includes('3')) {
        return gotoFlow(pricingFlow)
      }
    },
    [docFlow, supportFlow, pricingFlow] // Nested flows
  )

const docFlow = addKeyword('doc')
  .addAnswer('Documentation: https://builderbot.app/docs')

const supportFlow = addKeyword('support')
  .addAnswer('Contact support: [email protected]')

const pricingFlow = addKeyword('pricing')
  .addAnswer('View pricing: https://builderbot.app/pricing')

const adapterFlow = createFlow([
  welcomeFlow,
  docFlow,
  supportFlow,
  pricingFlow
])

Best Practices

Flow Organization: Keep flows focused on specific conversation topics. Use nested flows for sub-conversations.
Array Requirement: The createFlow function requires an array of flows. Passing a single flow without an array wrapper will throw an error.
Performance: The flow system is optimized for fast keyword matching. Even with hundreds of flows, lookups remain efficient through the serialization index.

Error Handling

The FlowClass validates inputs during construction:
if (!Array.isArray(_flow)) throw new Error('Must be an array of flows')
Always wrap flows in an array when creating the flow adapter:
// ✅ Correct
const adapterFlow = createFlow([flow1, flow2])

// ❌ Wrong - will throw error
const adapterFlow = createFlow(flow1)

Build docs developers (and LLMs) love