๐ Elevator Pitch
A production-ready app built with NestJS that acts as an intelligent bridge to WhatsApp via whatsapp-web.js. It exposes simple HTTP endpoints for triggering WhatsApp messages, while leveraging BullMQ and Redis for reliable message queuing and rate-limiting. A built-in WebSocket gateway provides real-time event publishing (e.g., QR Code generation, authentication status, and message receipts), providing a robust backbone for building enterprise-grade WhatsApp integrations.
๐ Tech Stack
- Backend Framework: NestJS (Node.js) + TypeScript
- Message Broker: BullMQ over Redis
- Real-time Communication: Socket.IO
- WhatsApp Core:
whatsapp-web.js(Puppeteer-driven) - API Documentation: Swagger / OpenAPI
- Logging & Monitoring: Winston +
@nestjs/terminus(Health Checks) - Containerization: Docker & Docker Compose
๐ System Architecture
The application handles incoming REST requests to queue outgoing messages, safely processing them out-of-band via a worker to respect WhatsApp's native rate limits. Meanwhile, clients can subscribe to the WebSocket gateway to receive asynchronous updates.
Key Data Flow Summary:
- Init: Client calls
/api/whatsapp/initializeโ the server provisions a Puppeteer instance, and forwards the QR code payload via WebSocket (whatsapp.qr). - Messaging: Client submits a message payload to
/api/whatsapp/send-message. - Queueing: Message is staged into Redis via BullMQ (
whatsapp-message-queue). - Execution: The
MessageProcessorfetches the job subject to a rate limit constraint (1 message / 5s) and pushes it via Puppeteer to WhatsApp Servers. Real-time updates confirm delivery.
โ๏ธ Environment Variables
Copy .env.example to .env in the root directory before running the app.
| Variable | Description | Example |
|---|---|---|
| `MQ_REDIS_HOST` | Hostname of the Redis server. | `redis` (in Docker) or `localhost` |
| `MQ_REDIS_PORT` | Port of the Redis server. | `6379` |
| `SHOW_QR_ON_TERMINAL` | Render QR code logic directly in console? | `true` or `false` |
_Note: Never expose actual cryptographic secrets or production tokens in your version control._
๐ Prerequisites & Local Setup
1) System Requirements
- Node.js: v22+
- Package Manager:
pnpm(runnpm install -g pnpm) - Redis: Up and running locally (or via Docker Compose)
2) Installation
# Install all dependencies
pnpm install
# Setup environment variables
cp .env.example .env
# Review and edit your .env values if running on local OS (expecting Redis on localhost)
3) Getting Started with Docker
The fastest way to boot the full suite (Redis + API) is via Docker Compose:
# Run in detached mode
docker-compose up -d
# To observe the application logs (helpful for capturing the QR Code!)
docker-compose logs -f api
If you wish to run the app on the host machine but retain the Dockerized Redis DB:
# Start just Redis
docker-compose up -d redis
# Boot the NestJS DEV server
pnpm run start:dev
๐ก API Documentation
REST API
By default, the global API prefix is set to /api. The Swagger documentation UI is available if swagger.yaml is bootstrapped.
- Swagger Base URL:
http://localhost:3000/docs
GET /api/health
POST /api/whatsapp/initialize
POST /api/whatsapp/send-message
WebSocket Gateway
Connect to http://localhost:3000 via your Socket.io client.
- Handshake/Query parameter setup: Supply
?user_id=YOUR_IDwhen initializing the socket. - Subscribable Events:
๐งช Testing Strategy
The project leverages Jest for both unit and End-to-End coverage.
# Run the entire unit test suite
pnpm run test
# Run tests in watch mode
pnpm run test:watch
# Generate an execution coverage report
pnpm run test:cov
# Run End-to-End integration tests
pnpm run test:e2e
E2E testing is defined in the test/ directory, while unit logic acts primarily on .spec.ts files adjacent to module business layers.
๐ฅ Deployment Guide & Troubleshooting
Building for Production
The application features a structured Dockerfile utilizing a minimal node:22-slim image to reduce footprint. It has a multistage build:
builderstage: Compiles NestJS typescript to/dist.productionstage: Copies output, installs Chromium binary, skips node internal Puppeteer download, and locks down execution privileges viaappuser.
Linux / Container Troubleshooting:
- Crash on Boot: If Puppeteer fails to run
browser.launch, verify that your container operates without an aggressive AppArmor profile or simply bind Chromium to run with--no-sandboxparams in yourwhatsapp.client.tsinitialization. - Dangling instances: In some host environments, orphaned
chromiumprocesses linger. Ensure you have--initenabled in Docker (e.g.,init: truein your compose configuration) pointing to a zombie reaper like dumb-init, or simply usedocker-compose downgracefully. - Cache Volumes: Note that WhatsApp persistent accounts are mapped into the
whatsapp_sessionsandwhatsapp_cachevolumes. If QR scanning requires a loop reset, run:docker volume rm whatsapp-integration_whatsapp_sessions.