The Problem

Sending a WhatsApp message programmatically normally means running an unofficial client, managing a session, and polling for delivery state. This API wraps whatsapp-web.js behind a REST interface so a caller can enqueue a message and let a background worker deliver it through a logged-in WhatsApp session.

What This Does

whatsapp-api is a TypeScript/Express service with two processes: a REST API (src/app.ts, src/server.ts) and a worker (src/whatsapp/index.ts) that polls a database for queued messages and sends them. The API manages users, auth tokens, and message queues via TypeORM entities (src/models/Message.ts, src/models/Token.ts, src/models/User.ts) and repositories (src/repositories/MessagesRepository.ts, src/repositories/TokensRepository.ts).

The WhatsApp integration lives in src/whatsapp/client/index.ts, which initializes a whatsapp-web.js client, handles authentication (QR code or saved session), and exposes sendMessage, getContacts, and registerNewToken. The repo includes a Procfile and docker-compose.yml, suggesting deployment targets Heroku or Docker.

How It Is Wired

Execution starts at two entry points. src/server.ts boots the Express app, which routes through src/routes/index.ts into controllers like MessagesController.ts and TokensController.ts. The worker entry point is checkMessages in src/whatsapp/index.ts, which reaches 15 functions—the widest reach in the repo.

The critical path is short: MessagesController.create calls CreateMessageService.execute, which calls findByPhone on TokensRepository and create on MessagesRepository. The worker polls findMessagesToSend, then calls sendMessage in src/whatsapp/client/index.ts, which leaves the process via the whatsapp-web.js library. The session lifecycle is centralized in finalizeClient, called from 5 places—changing it affects every session teardown path.

The module graph shows no circular dependencies, but src/errors/AppError is imported by 11 modules, making it a low-instability hub. src/routes/index has instability 0.89 (imports 8 modules, imported by 1), meaning it's a thin router that's easy to swap. The highest-risk file is src/whatsapp/client/index.ts: it owns session init, teardown, and message sending, and is called from 3 other files.

How To Use It

git clone https://github.com/moses-y/whatsapp-api.git && cd whatsapp-api
yarn
docker run --name "whatsapp" -e MYSQL_ROOT_PASSWORD="mysql_password" -p 3306:3306 -d mysql:5.7.30
cp .env.example .env
yarn typeorm migration:run
yarn dev:server

Configuration lives in .env (copied from .env.example) and ormconfig.js. The README documents these commands verbatim. Auth uses JWT (src/config/auth.ts), and the API exposes routes for users, sessions, messages, contacts, tokens, and screenshots.

Real-World Use

A customer-support bot that receives a webhook, calls POST /messages with a recipient phone and text, and lets the worker deliver it through a persistent WhatsApp session. The TokensController issues scoped tokens so external services can enqueue messages without full user credentials.

Code Health & Issues

Static analysis found 1 medium finding: duplicated 6-line blocks across 5 files (ScreenshotController.ts, TokensController.ts, Message.ts, Token.ts)—extract shared helpers. The audit also flags 4 high-severity issues: no test suite (44 source files, zero tests), no CI workflow, no build gate for the Procfile deployment artifact, and a wildcard CORS origin in src/app.ts that pairs with credentials—browsers reject this configuration. Dependency updates are not automated (no Dependabot/Renovate). The repo has a license and no committed secrets.

The Bottom Line

This is a functional prototype for study or internal use, not production-grade. The architecture is clean (no circular deps, clear separation of concerns), but the missing tests and CI make it risky to change. Use it to learn how whatsapp-web.js integrates with Express, but add tests and a build gate before deploying it anywhere real.