DEV Community

Sandeep Dara
Sandeep Dara

Posted on

Building Flow-Aware Validation in JavaScript - Behind the Scenes of sd-is@1.0.7

In many real-world applications, especially form-heavy or wizard-style interfaces, validation isn’t just about individual fields — it’s about what happens across steps, when and how data should be validated, and how users move through a flow.

This is where most validation libraries fall short. They’re either too rigid, too form-specific, or not aware of user progression.

So I decided to solve it.

🎯 The Goal: Flow-Aware Validation Engine

The idea was simple but ambitious:

Build a schema-driven, flow-aware, and lifecycle-sensitive validation system — one that not only validates data, but understands where the user is in a flow, and what needs to be validated when.

This led to the creation of the following core building blocks in sd-is v1.0.7:


🔧 Core Concepts Introduced

1. defineFlowSchema()

A declarative way to define a multi-step flow, where each step has:

  • Its own schema
  • Optional onEnter / onExit hooks for lifecycle-based validation
  • Execution order control
const flow = defineFlowSchema({
  steps: {
    account: {
      schema: defineSchema({ email: { type: 'string' }, password: { type: 'string' } }),
      onEnter: (data) => { /* validate access */ },
      onExit: (data) => { /* check password strength */ }
    },
    profile: {
      schema: defineSchema({ name: { type: 'string' } })
    }
  },
  order: ['account', 'profile']
});
Enter fullscreen mode Exit fullscreen mode

2. validateStep() & validateStepAsync()

Validate a specific step in the flow — respecting its schema, onEnter/onExit hooks, and returning a clean verdict.

const result = validateStep(flow, 'account', data); // sync
const result = await validateStepAsync(flow, 'account', data); // async
Enter fullscreen mode Exit fullscreen mode

Async mode is particularly helpful for:

  • API checks
  • Debounced validations
  • Async business logic (e.g., blocked email detection)

3. createFlow()

A full-blown flow controller built on top of defineFlowSchema, managing:

  • Step progression (.proceed())
  • Navigation (.back(), .restart())
  • Debug logs
  • State tracking
const flowEngine = createFlow(flow, { debug: true });

await flowEngine.proceed({ email: 'user@example.com', password: 'pass123' });
await flowEngine.proceed({ name: 'Tony Stark' });

flowEngine.back();      // go back a step
flowEngine.restart();   // restart the flow
console.log(flowEngine.getDebugLog());
Enter fullscreen mode Exit fullscreen mode

🧪 What Makes It Different?

  • ✅ Declarative: No spaghetti conditionals, no imperative mess
  • ✅ Lifecycle-aware: onEnter and onExit give precise control over transitions
  • ✅ Extendable: Schema validation can be synchronous or asynchronous
  • ✅ Developer-first: Works great with logging, tracing, and testability
  • ✅ Dependency-free: Zero external dependencies — just clean JS

💡 Use Cases

  • Multi-step forms (onboarding, checkout, profile setup)
  • Workflow engines
  • Survey builders
  • Any staged UI with dynamic rules

📦 Now on npm

npm install sd-is
Enter fullscreen mode Exit fullscreen mode
  • 📘 Docs & README
  • 🧪 Fully tested — with test.js showcasing all capabilities

🛠️ What's Next?

The roadmap is exciting:

  • ⚙️ Composable sub-flows
  • 💡 Dynamic fix suggestions
  • 🧩 Flow tracing with context
  • 🔄 createFlowV – a versioned, extensible flow runner for enterprise logic

🧠 Closing Thoughts

Validation shouldn't be dumb. It should understand context, user progression, and logic over time.

This evolution of sd-is aims to make validation smarter, modular, and built for real-life flows, not just single-form fields.

If you're building form-heavy apps or complex flows — I’d love for you to give it a spin and share feedback!

→ GitHub: github.com/sandeepdara-sd/sd-is

→ NPM: sd-is

Let’s build better flows, together.

Top comments (0)