Bhejna Engineering Case Study
Bhejna is a messaging infrastructure platform engineered by CodeNXT Lab for reliable WhatsApp communication. This case study focuses on engineering decisions, architectural trade-offs, and implementation strategies.
The Problem
Integrating third-party business messaging channels introduces architectural friction. Webhook endpoints from upstream providers (such as Meta's WhatsApp API) often experience latency spikes or drop connection during transaction bursts. Rate limits enforced by provider gates cause payload losses if not managed by system buffers. Furthermore, tracking exact delivery statuses across distributed devices requires structured delivery pipelines, which traditional web servers fail to coordinate under concurrency pressure.
Engineering Goals
We designed the backend proxy gateway around six technical parameters:
- 01 / Reliable Delivery: Exponential retry logic for upstream webhook calls.
- 02 / Simple API: OpenAPI compliant paths for client integration.
- 03 / Fast Ingestion: Queue incoming requests instantly, executing writes in the background.
- 04 / Scalable Architecture: Isolated DB limits per tenant schema.
- 05 / Low Overhead: Keeps base resource usage under 50MB RAM.
- 06 / Predictable Behavior: Contract-first development schemas for type safety.
System Architecture
Bhejna is built on Go, using the Chi Router and SQLite for local transaction queuing.
**Concurrency Queue Routing:** Incoming payloads are parsed by the Chi API router. Instead of block-waiting for
upstream WhatsApp responses, Bhejna writes the message payload to a local SQLite database in WAL (Write-Ahead Logging)
mode. The request returns a 202 Accepted status to the client instantly. Background goroutines run queue loops,
executing message delivery, webhook retries, and schema validation scripts asynchronously.
Key Engineering Decisions
Go Compiled Runtime: We built Bhejna in Go instead of Node.js. Go compiles to a static binary with zero external package dependencies. Its concurrent execution runtime maps background jobs to physical CPU cores, using <30MB RAM baseline.
SQLite In WAL Mode: We selected SQLite over PostgreSQL for local queuing. SQLite stores records inside a single local file, removing network TCP roundtrip overhead. Setting SQLite to WAL mode allows parallel read access during serialized database writes.
Contract-First OpenAPI: The REST API routes are generated from an OpenAPI specification schema, ensuring that both client integration parameters and Go structural models are bound to the same type boundaries.
Idempotent Webhooks: Upstream webhooks contain unique message IDs. We enforce database uniqueness keys to ignore duplicate webhook payloads during connection dropouts.
Trade-offs
Building Bhejna required making calculated compromises:
SQLite Writer Limit: SQLite limits transactional write access to a single writer. For high-throughput systems, this can cause transaction lock queues. However, for a messaging gateway, write-locks are managed using in-memory queues (Go channels), avoiding database lock crashes.
Serverless vs Dedicated VPS: Deploys on a VPS (AWS EC2/Hetzner) rather than serverless functions. A persistent VPS compute runtime is required to run long-running background polling loops and manage the local SQLite files reliably.
Webhook Retries: Storing failed webhook delivery payloads for retries increases SQLite file growth. We configure strict retention cleaners that delete historical delivery logs after 48 hours to preserve disk space.
Lessons Learned
Simple Systems Outlive Complex Topologies
Starting with complex message queues (like RabbitMQ) introduced excessive system dependencies. Consolidating the messaging pipeline inside a single Go binary running local SQLite databases reduced operational overhead.
Contract-First Design Restricts Bugs
Building the API specification first prevented interface mismatch issues between our Go gateway and external JavaScript client apps.
Structured Logging is Essential
Tracing event errors through raw text streams is inefficient. Outputting structured JSON logs allowed us to configure automated monitoring dashboards.
Queues Over Frameworks
Using Go's channels to control database transaction pipelines was more reliable than relying on bloated application framework abstractions.
Business Impact
The Bhejna architecture delivered qualitative improvements across production deployments:
- • More reliable message delivery through automatic retry loops.
- • Better operational visibility into webhook failures and provider delays.
- • Faster developer onboarding using OpenAPI compliant API layouts.
- • Lower baseline compute costs through a highly optimized compiled binary.
Architecture Summary
| Layer | Technology | Purpose | Reason Selected |
|---|---|---|---|
| Router API | Go & Chi Router | Ingests payloads, parses webhook endpoints. | Compiles to single binary, minimal RAM overhead. |
| Local Queue | SQLite (WAL Mode) | Caches payloads before delivery attempts. | Bypasses TCP network overhead, sub-1ms reads. |
| Contract | OpenAPI v3 | Enforces payload definitions and path schemas. | Type safety, automatic documentation generation. |
| Jobs | Go Channels | Coordinates background retry loops. | Native concurrency tools without extra server layers. |
Future Evolution
We design systems with long-term adaptability in mind:
API Versioning: Adding semantic version routing to support clients on older contract formats.
Replay Webhooks: Custom admin endpoints to replay historic webhook payloads during client server crashes.
SDK libraries: Native package wrappers for Go, TypeScript, and Python developers.
Frequently Asked Questions
Why Go?
Go compiles to a single static binary with no external runtimes. Its built-in concurrency model (goroutines) allows us to process thousands of queued messages with low CPU and memory footprints.
Why SQLite?
SQLite runs directly in-process, bypassing the TCP networking overhead of external databases (like PostgreSQL). In WAL mode, it handles read queries in sub-1ms, making it ideal for high-speed local payload queue storage.
Why not Kafka?
Kafka is a powerful message broker, but it requires substantial server infrastructure and maintenance overhead. For Bhejna's throughput requirements, Go channels paired with SQLite database files provided stable, low-overhead transaction queuing without extra infrastructure fees.
How are webhooks retried?
Failed deliveries trigger a background retry loop. It retries deliveries using an exponential backoff sequence (5m, 15m, 1h, 6h, 12h) to avoid overwhelming client servers during outages.
How is message ordering handled?
Messages are ordered dynamically by database index sequencing. Background worker loops process message delivery sequentially based on arrival timestamps to prevent message order swaps.
How is multi-tenancy implemented?
We implement multi-tenancy by database partitioning. Each client tenant account is assigned an isolated SQLite database file, preventing cross-client transaction interference or data leaks.
How are API versions managed?
API routes are structured under URL subfolders (e.g. `/v1/...`). OpenAPI specifications define request and response schemas for each version to guarantee backward compatibility.
How are failures handled?
When upstream endpoints reject payloads or timeout, the message status is logged as `failed`. If the error is transient, it is moved to the retry queue; if permanent (e.g., invalid phone number), it is marked as `failed` with the exact status code.
Why contract-first APIs?
Enforcing the OpenAPI schema first prevents validation mismatches between client payloads and our Go backend parser models, reducing production API bugs.
Can Bhejna run on multiple servers?
Yes. Because we partition SQLite databases by tenant, Bhejna can scale horizontally by routing specific tenant traffic to dedicated server instances.
Discuss your architecture before writing code.
Skip generic development agency proposals. Schedule a systems call with our principal architect to review your database schemas and concurrency models.
Connect with the Principal Architect