DEV Community

Alessandro Binda
Alessandro Binda

Posted on

I Replaced whatsapp-web.js With a REST API and My Bot Stopped Crashing

The Problem

If you've built a WhatsApp bot with whatsapp-web.js, you know the pain:

  • Random disconnects every 2-6 hours
  • QR re-authentication breaking at 3am
  • Memory leaks from the embedded Chromium instance
  • Multi-instance? Forget it — each instance eats 500MB+ RAM

I ran this stack in production for 8 months. It worked... until it didn't. The breaking point was when I needed multi-tenant support: one server handling WhatsApp sessions for multiple businesses.

The Migration

I moved from whatsapp-web.js (browser automation) to WAHA (a proper REST API that wraps WhatsApp Web internally). The difference:

whatsapp-web.js WAHA
Architecture Your code spawns Chromium Standalone Docker container
Memory per session ~500MB ~80MB
Multi-session Manual, fragile Native REST endpoint
Session persistence File-based, breaks Built-in
Reconnection You build it Automatic
API style Event-driven JS REST + webhooks

What Changed in the Code

The biggest architectural shift: instead of your bot being the WhatsApp process, your bot becomes a webhook consumer.

Before (whatsapp-web.js):

const client = new Client();
client.on("message", async (msg) => {
  // Your bot IS the WhatsApp process
  // If this crashes, WhatsApp disconnects
  const response = await generateAIResponse(msg.body);
  await msg.reply(response);
});
client.initialize(); // Spawns Chromium
Enter fullscreen mode Exit fullscreen mode

After (WAHA webhook):

// WAHA runs separately as a Docker container
// Your bot receives webhooks
app.post("/webhook/message", async (req, res) => {
  const { from, body, session } = req.body;
  const response = await generateAIResponse(body, session);

  // Send via REST API
  await fetch(`${WAHA_URL}/api/sendText`, {
    method: "POST",
    body: JSON.stringify({
      session,
      chatId: from,
      text: response
    })
  });

  res.sendStatus(200);
});
Enter fullscreen mode Exit fullscreen mode

The separation means:

  • Bot crashes don't kill WhatsApp sessions
  • You can restart your bot without re-authenticating
  • Multiple bots can share the same WAHA instance
  • Scaling is just adding more webhook consumers

The AI Agent Layer

Once the WhatsApp connection was stable, I built the actual agent runtime on top: SARA (github.com/Alessandro114/sara).

SARA handles:

  • Tool-calling loop on Groq/Cerebras/Mistral (free tiers)
  • 20 industry-specific knowledge packs (restaurants, clinics, retail...)
  • Session memory that persists across conversations
  • Multi-tenant isolation — each business gets its own agent persona

The entire stack runs self-hosted via Docker Compose:

services:
  waha:
    image: devlikeapro/waha
    ports:
      - "3000:3000"
  sara:
    build: .
    environment:
      - WAHA_URL=http://waha:3000
      - GROQ_API_KEY=${GROQ_API_KEY}
Enter fullscreen mode Exit fullscreen mode

Results After Migration

  • Uptime: 99.7% over 3 months (vs ~92% with whatsapp-web.js)
  • Memory: 80MB per session (vs 500MB)
  • Deploy time: restart bot in 2s without losing sessions
  • Multi-tenant: 5 businesses on one $10/mo VPS

Try It

SARA is open source (AGPL-3.0): github.com/Alessandro114/sara

If you're stuck on whatsapp-web.js and hitting the same problems, the WAHA migration is worth the effort. The codebase is ~3K lines of TypeScript.


I'm the author of SARA. Questions about the migration or architecture welcome in the comments or on GitHub.

Top comments (0)