Technology Mapping & Migration Strategy
This document outlines the architectural shift from the legacy Node.js/Express monolithic API (apps/api) to the serverless Cloudflare Workers architecture (apps/worker).
Core Technology Stack
| Feature / Component | Legacy (apps/api) | Cloudflare Worker (apps/worker) | Rationale & Changes |
|---|---|---|---|
| Runtime Environment | Node.js (Long-running process) | Cloudflare Workers (V8 Isolate) | Stateless, ephemeral execution. No access to OS-level APIs like fs or child_process. |
| Web Framework | Express.js | Hono | Hono is built for edge Runtimes, extremely lightweight, and follows web standard APIs (Request/Response). |
| Database | MongoDB (Mongoose) | Cloudflare D1 (SQLite) | SQL-based relational data. Accessed via Prisma ORM for type safety. |
| Object Storage (Files) | Local Filesystem / multer | Cloudflare R2 | Workers cannot write to disk. Files are streamed directly to R2 object storage. |
| Message Queuing | RabbitMQ (amqplib) | Cloudflare Queues | TCP connections for AMQP are unstable in Workers. Cloudflare Queues offers native integration. |
| Scheduled Tasks | node-cron / PM2 | Cloudflare Cron Triggers | Scheduled events (scheduled() export) trigger the worker at defined intervals. |
Feature-Specific Architectural Changes
1. Real-Time Broadcasts
- Legacy: Used
socket.ioor local EventEmitters. Relied on a single active server instance or Redis adapter for broadcasting. - New Architecture: Durable Objects.
- Why: Workers are ephemeral and distributed. A "broadcast" needs a central point of coordination.
- Implementation: A
WebSocketHubDurable Object maintains a list of active WebSocket sessions. - Flow: Event occurs (e.g. from Queue) → Worker POSTs to
WebSocketHub→ Hub iterates sessions and sends message.
2. PDF Reporting
- Legacy: Used
pdfkitorpuppeteer, wrote temporary files to local disk (/tmp), served via static file route, then deleted via timer. - New Architecture: In-Memory Generation + R2.
- Why: We cannot write to
/tmpreliably or running background cleanup timers. - Implementation:
jsPDFgenerates the PDF into anArrayBufferin memory. This buffer is immediatelyPUTto an R2 bucket. - Flow: Generate Buffer → Upload to R2 → Return Signed URL / Public URL.
- Why: We cannot write to
3. Image Processing
- Legacy:
sharp(native module) orchild_processwith ImageMagick. - New Architecture: Cloudflare Images or WASM-based Sharp.
- Note: Simple resizing can be done with Cloudflare Image Resizing (paid feature) or using pure JS/WASM libraries, avoiding native node modules.
Dependency Mapping
| Legacy Package | Status | Replacement / Mitigation |
|---|---|---|
express | ❌ Incompatible | hono |
mongoose | ❌ Incompatible | @prisma/client + @prisma/adapter-d1 |
amqplib | ❌ Incompatible | env.QUEUE.send() (Native Binding) |
fs (native) | ❌ Incompatible | env.STORAGE (R2 Binding) |
ws / socket.io | ❌ Incompatible | WebSocketPair (Native) + Durable Objects |
jsonwebtoken | ⚠️ Compatible | Compatible, but often replaced by hono/jwt for speed. |
bcrypt | ❌ Incompatible | bcryptjs (Pure JS version) |