DEV Community

Cover image for From Spaghetti Code to Orchestrated Workflows: Building AyurAI with Motia & Mux
Ashita
Ashita

Posted on

From Spaghetti Code to Orchestrated Workflows: Building AyurAI with Motia & Mux

DEV's Worldwide Show and Tell Challenge Submission 🎥

This is a submission for the DEV's Worldwide Show and Tell Challenge Presented by Mux

What I Built

AyurAI is a comprehensive AI-powered Ayurvedic wellness platform that democratizes access to holistic healthcare. The platform seamlessly integrates three core pillars:

1. AI Voice Assistant

Users can engage in natural, conversational exchanges with an intelligent wellness advisor powered by VAPI. Available 24/7, the assistant provides personalized Ayurvedic guidance without the barrier of cost or availability. It's trained to provide authentic Ayurvedic advice while being accessible to modern users.

2. Practitioner Appointment System

A complete booking and management system that connects users with certified Ayurvedic doctors. Users can:

  • View available practitioners and their schedules
  • Book appointments with conflict detection
  • Receive confirmation emails automatically
  • Track their consultation history
  • Manage upcoming appointments

The system handles complex scenarios like preventing double-bookings, checking doctor availability, and automatically notifying both patient and practitioner.

3. Educational Video Library

A curated collection of Ayurvedic wellness videos with professional streaming capabilities. Users can:

  • Browse wellness content
  • Stream videos with adaptive quality
  • Resume from where they left off
  • Autogenerated thumbnail by Mux

This isn't just video hosting—it's a complete learning journey with progress tracking and a personalized experience.

Additional Features

  • User Dashboard - Wellness journey overview, completed visits tracking, upcoming appointments
  • Admin Dashboard - Doctor management, video uploads, appointment analytics, performance metrics
  • Dark Mode UI - Optimized for comfortable viewing

Project Structure

https://github.com/devandop/ayur-ai

ayurai/
├── src/
│   ├── app/                    # Next.js App Router
│   │   ├── dashboard/          # User dashboard + video library
│   │   ├── admin/              # Admin dashboard
│   │   ├── appointments/       # Booking system
│   │   └── voice/              # AI assistant
│   ├── components/
│   │   ├── ui/                 # Radix + shadcn components
│   │   ├── dashboard/          # Dashboard components
│   │   └── admin/              # Admin components
│   ├── hooks/                  # React Query hooks
│   └── lib/actions/            # Server actions
│
├── backend/
│   ├── src/api/                # Motia workflow steps
│   │   ├── create-appointment.step.ts
│   │   ├── admin-add-video.step.ts
│   │   └── ...
│   └── src/events/             # Event handlers
│
└── prisma/schema.prisma        # Database schema
Enter fullscreen mode Exit fullscreen mode

Running Locally

# Frontend
npm install
npm run dev

# Backend (separate terminal)
cd backend
npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

Required environment variables: DATABASE_URL, CLERK_SECRET_KEY, MUX_TOKEN_ID, MUX_TOKEN_SECRET

The Story Behind It

The Inspiration

Ayurveda has guided millions toward wellness for over 5,000 years. It's a sophisticated medical system that views health holistically—not as the absence of disease, but as a state of balance in body, mind, and consciousness.

Yet here we are in 2025, and accessing Ayurvedic wisdom remains surprisingly difficult:

The Pain Points:

  • Scarcity - Qualified Ayurvedic practitioners are rare and concentrated in certain regions
  • Cost - Consultations are expensive, often unaffordable for the average person
  • Inconvenience - Scheduling requires travel, time off work, and planning
  • Fragmentation - Knowledge is scattered across books, blogs, and individual practitioners
  • Impermanence - Guidance comes and goes—there's no continuous journey tracking

I wanted to solve this. Not by replacing traditional Ayurveda, but by making it accessible.

The Realization

I noticed that successful health platforms share a pattern:

  1. Instant Access - Users get help immediately (not days later)
  2. Professional Backup - AI is great, but human expertise matters
  3. Education - Users learn and take ownership of their health
  4. Continuity - Progress is tracked and visible over time

AyurAI combines all four.

Technical Highlights

The Challenge: Building a Robust Backend

Traditional backend architecture would require:

Routes → Controllers → Services → Database
↓
Manual event handling
↓
Complex error recovery
↓
Tightly coupled code
Enter fullscreen mode Exit fullscreen mode

The Solution: Event-Driven Workflows with Motia

Isolated Workflow Steps
↓
Declarative Event Emission
↓
Automatic Event Orchestration
↓
Self-Documenting Code
Enter fullscreen mode Exit fullscreen mode

Why Motia Was The Right Choice

1. Declarative, Self-Documenting Workflows

Instead of buried business logic, each workflow is clear and explicit:

export const config: ApiRouteConfig = {
  type: 'api',
  name: 'CreateAppointment',
  path: '/api/appointments',
  method: 'POST',
  emits: ['appointment.created'],        // ← Clear what this produces
  flows: ['appointment-management'],      // ← Declares the flow it belongs to
  middleware: [
    errorHandlerMiddleware,              // ← Built-in error handling
    clerkAuthMiddleware,                 // ← Authentication
    RateLimiters.moderate,               // ← Rate limiting
    SanitizationPresets.medicalNotes,   // ← Input sanitization
  ],
}
Enter fullscreen mode Exit fullscreen mode

Anyone reading this knows exactly what the step does, what security is applied, and what events it produces.

2. Event-Driven Orchestration

When an appointment is created, instead of manually writing callback chains:

// With Motia - it's just one line:
await ctx.emit({
  topic: 'appointment.created',
  data: {
    appointmentId: appointment.id,
    patientEmail: appointment.user.email,
    doctorName: appointment.doctor.name,
    date: appointment.date,
    time: appointment.time,
  }
})
Enter fullscreen mode Exit fullscreen mode

Motia automatically triggers:

  • Email notifications (via separate event handler)
  • Analytics tracking (via separate event handler)
  • Notification caching (via separate event handler)
  • Any future handlers we add (no code changes needed)

Zero manual coordination.

3. Built-in Middleware Stack

Motia provides production-grade middleware out of the box:

Authentication Middleware:

export const clerkAuthMiddleware: ApiMiddleware = async (req, ctx, next) => {
  const clerkUserId = req.headers['x-clerk-user-id']

  const user = await prisma.user.upsert({
    where: { clerkId: clerkUserId },
    update: { email, firstName, lastName },
    create: { clerkId: clerkUserId, email, firstName, lastName }
  })

  ;(ctx as any).user = user
  return await next()
}
Enter fullscreen mode Exit fullscreen mode

Rate Limiting:

const rateLimiter = createRateLimiter({
  maxRequests: 30,
  windowSeconds: 60,
  keyExtractor: (req, ctx) => {
    const user = (ctx as any).user
    return user?.id || 'anonymous'
  }
})
Enter fullscreen mode Exit fullscreen mode

Input Sanitization:

const sanitizeString = (str: string, options = {}) => {
  if (typeof str !== 'string') return str
  let sanitized = removeControlCharacters(str)
  sanitized = trimAndLimit(sanitized, options.maxLength || 1000)
  sanitized = options.stripHTML ? stripHTML(sanitized) : sanitizeHTML(sanitized)
  return sanitized
}
Enter fullscreen mode Exit fullscreen mode

These are applied consistently across all workflows.

4. Type-Safe Event Handling

Events are typed and validated using Zod:

const responseSchema = z.object({
  id: z.string(),
  date: z.string(),
  status: z.enum(['CONFIRMED', 'COMPLETED', 'CANCELLED']),
  notes: z.string().nullable(),
  // ... more fields
})

export const config = {
  responseSchema: {
    200: responseSchema,
    401: z.object({ error: z.string() }),
    500: z.object({ error: z.string() }),
  }
}
Enter fullscreen mode Exit fullscreen mode

If a response doesn't match the schema, it fails loudly. This catches bugs before they hit production.

5. Scalability Through Event Queuing

Using BullMQ under the hood, events are queued and processed reliably:

Event Emission
    ↓
BullMQ Queue
    ↓
Event Processors
    ↓
Database Updates / Emails / Analytics
Enter fullscreen mode Exit fullscreen mode

If an event handler fails, it automatically retries with exponential backoff.

6. Visual Debugging with Motia Workbench

The Motia Workbench provides a visual interface showing:

  • All workflow steps and their connections
  • Real-time event flows
  • Failed workflows with stack traces
  • Performance metrics
  • Request/response logs

Instead of blindly searching through logs, you can see exactly what happened.

Real-World Example: Appointment Booking Flow

Here's how AyurAI handles a complete appointment booking:

1. User submits appointment form
   ↓
2. POST /api/appointments hits CreateAppointment step
   ↓
3. Validation layer (Zod schema)
   ↓
4. Auth middleware (verify user)
   ↓
5. Rate limiting (max 30 bookings/minute)
   ↓
6. Sanitization (clean user input)
   ↓
7. Database checks:
   - Doctor exists and is active?
   - Time slot available?
   - User doesn't have conflict?
   ↓
8. Create appointment record
   ↓
9. Emit 'appointment.created' event
   ↓
10. Motia automatically triggers:
    ├─ Email to user (confirmation)
    ├─ Email to doctor (new appointment)
    ├─ Analytics tracking (appointment created)
    └─ Cache invalidation (user appointments cache)
   ↓
11. Return success to frontend
    ↓
12. Frontend optimistically updates UI
    ↓
13. TanStack Query refetches and syncs
Enter fullscreen mode Exit fullscreen mode

Not a single line of manual coordination code.

Comparison: Motia vs Traditional Express

Aspect Express Motia
Boilerplate Manual routes, controllers, services Declarative steps
Events Manual pub/sub setup Built-in, automatic orchestration
Middleware Write custom, apply manually Built-in, composable
Error Handling Write catch/finally blocks Automatic retry logic
Scaling Manage queues manually BullMQ integrated
Documentation Separate OpenAPI specs Self-documenting config
Testing Spy on internals Test steps independently
Debugging Log diving Visual workflow tracing

Use of Mux (Additional Prize Category Participants Only)

Most platforms use Mux only for video hosting. AyurAI goes much deeper.

1. Direct Upload Infrastructure

Instead of uploading videos to my server (which is slow and storage-intensive), admins upload directly to Mux:

// Step 1: Create upload URL
const upload = await mux.video.uploads.create({
  new_asset_settings: {
    playback_policy: ['public'],
  },
})

// Returns: { id, url }
// Admin uploads to this URL directly
// Video never touches my servers
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Server bandwidth savings
  • Faster uploads
  • No storage costs
  • Secure direct transfer

2. Automatic Asset Processing

Once uploaded, Mux automatically:

Encoding:

  • Converts to VP9, H.264, H.265
  • Multiple quality levels (720p, 1080p, 4K)
  • Generates HLS and DASH streams

Thumbnail Generation:

  • Extracts key frames
  • Generates optimized thumbnails
  • Available at: https://image.mux.com/{playbackId}/thumbnail.jpg
// AyurAI automatically stores this URL
const video = await prisma.video.create({
  data: {
    title,
    description,
    muxAssetId: asset.id,
    muxPlaybackId: asset.playback_ids[0].id,
    thumbnailUrl: `https://image.mux.com/${asset.playback_ids[0].id}/thumbnail.jpg`,
    duration: asset.duration,
  }
})
Enter fullscreen mode Exit fullscreen mode

3. Adaptive Bitrate Streaming

Mux serves the appropriate quality based on user's connection:

// All video URLs are HLS streams
<video
  src={`https://stream.mux.com/${muxPlaybackId}.m3u8`}
  controls
/>

// Mux automatically:
// - Detects user's bandwidth
// - Serves optimal quality
// - Switches mid-stream if connection changes
// - Provides smooth buffering
Enter fullscreen mode Exit fullscreen mode

4. Progress Tracking Integration

I built a custom system on top of Mux playback:

// VideoPlayer component
const VideoPlayer = ({ videoId, muxPlaybackId, initialPosition }) => {
  const videoRef = useRef()
  const [currentTime, setCurrentTime] = useState(initialPosition)

  // Track time updates
  const handleTimeUpdate = () => {
    setCurrentTime(videoRef.current.currentTime)
  }

  // Auto-save every 15 seconds (debounced to reduce API calls)
  useEffect(() => {
    if (!videoId || !duration) return

    const trackingTimer = setTimeout(async () => {
      await videoApi.updateProgress(videoId, currentTime, duration)
    }, 15000)

    return () => clearTimeout(trackingTimer)
  }, [currentTime])

  // Resume from where user left off
  useEffect(() => {
    if (videoRef.current && initialPosition > 0) {
      videoRef.current.currentTime = initialPosition
    }
  }, [initialPosition])

  return (
    <video
      ref={videoRef}
      src={`https://stream.mux.com/${muxPlaybackId}.m3u8`}
      onTimeUpdate={handleTimeUpdate}
      controls
    />
  )
}
Enter fullscreen mode Exit fullscreen mode

This creates a YouTube-like experience where users always resume from where they left off.

5. Metadata Extraction

Mux provides metadata that AyurAI uses for the UI:

const asset = await mux.video.assets.retrieve(assetId)

// Extract key metadata
const metadata = {
  duration: asset.duration,           // ← Total video length
  width: asset.width,                 // ← Video dimensions
  height: asset.height,
  fps: asset.frame_rate,              // ← For quality indicators
  createdAt: asset.created_at,
}

// AyurAI displays duration on video cards:
// "45:30" (45 minutes, 30 seconds)
Enter fullscreen mode Exit fullscreen mode

6. Playback ID Strategy

Mux assigns each video a unique playback ID, separate from the asset ID. This is powerful:

// Asset ID (unique, server-side)
muxAssetId: "abc123def456"

// Playback ID (unique, client-safe)
muxPlaybackId: "ji1XhtoywRhdk402DJ9FT3IcQ4Q01HCneqqDm2OwPBdrQ"

// Users only see playback ID in URLs:
// https://stream.mux.com/ji1XhtoywRhdk402DJ9FT3IcQ4Q01HCneqqDm2OwPBdrQ.m3u8

// This means:
// - Asset IDs never exposed to clients
// - Can revoke playback IDs without deleting video
// - Fine-grained access control possible
Enter fullscreen mode Exit fullscreen mode

7. Global CDN Delivery

Mux's infrastructure ensures:

User in India
  ↓
Closest Mux edge node
  ↓
Video streams with <100ms latency
  ↓
Adaptive quality based on connection

User in USA
  ↓
Different edge node
  ↓
Same experience, optimized for region
Enter fullscreen mode Exit fullscreen mode

AyurAI doesn't worry about CDN setup—Mux handles it.

8. Real Example: Video Upload Workflow

Here's the complete flow when an admin uploads a wellness video:

// Step 1: Admin clicks upload
// Frontend calls: POST /api/admin/videos/upload

// Step 2: Backend creates Mux upload session
const upload = await mux.video.uploads.create({
  new_asset_settings: {
    playback_policy: ['public'],
  },
})
// Returns upload URL

// Step 3: Admin uploads directly to Mux
// (File never touches AyurAI servers)

// Step 4: Backend polls for completion
const upload = await mux.video.uploads.retrieve(uploadId)
if (upload.status === 'ready') {
  const asset = await mux.video.assets.retrieve(upload.asset_id)

  // Step 5: Save metadata to database
  const video = await prisma.video.create({
    data: {
      title: "Ayurvedic Morning Routine",
      description: "...",
      muxAssetId: asset.id,
      muxPlaybackId: asset.playback_ids[0].id,
      thumbnailUrl: `https://image.mux.com/${asset.playback_ids[0].id}/thumbnail.jpg`,
      duration: asset.duration,
      isPublished: true,
    }
  })

  // Emit event
  await ctx.emit({
    topic: 'video.published',
    data: { videoId: video.id }
  })
}

// Step 6: Motia triggers downstream events
// - Update search index
// - Notify admin of completion
// - Generate recommended videos
Enter fullscreen mode Exit fullscreen mode

Result: Netflix-quality video appears in the app within minutes of upload. Zero server infrastructure concerns.

Why This Matters

By using Mux beyond just hosting, AyurAI:

Saves bandwidth - No video data on my servers
Saves storage - Mux stores, I just reference
Saves complexity - No encoding pipelines to manage
Saves costs - Pay per video delivered, not per storage
Provides reliability - Netflix-grade infrastructure
Enables features - Progress tracking, adaptive streaming, global delivery
Scales automatically - Handle 1 user or 1M users with same infrastructure

Top comments (2)

Collapse
 
rohitg00 profile image
Rohit Ghumare

Nice project! The best thing is, Everything works conveniently with Motia.

Collapse
 
anthony_motia profile image
Anthony Lusardi

Hey Ashita, Would love to walk through what you're building. Feel free to reach out any time!