Skip to content

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 / ComponentLegacy (apps/api)Cloudflare Worker (apps/worker)Rationale & Changes
Runtime EnvironmentNode.js (Long-running process)Cloudflare Workers (V8 Isolate)Stateless, ephemeral execution. No access to OS-level APIs like fs or child_process.
Web FrameworkExpress.jsHonoHono is built for edge Runtimes, extremely lightweight, and follows web standard APIs (Request/Response).
DatabaseMongoDB (Mongoose)Cloudflare D1 (SQLite)SQL-based relational data. Accessed via Prisma ORM for type safety.
Object Storage (Files)Local Filesystem / multerCloudflare R2Workers cannot write to disk. Files are streamed directly to R2 object storage.
Message QueuingRabbitMQ (amqplib)Cloudflare QueuesTCP connections for AMQP are unstable in Workers. Cloudflare Queues offers native integration.
Scheduled Tasksnode-cron / PM2Cloudflare Cron TriggersScheduled events (scheduled() export) trigger the worker at defined intervals.

Feature-Specific Architectural Changes

1. Real-Time Broadcasts

  • Legacy: Used socket.io or 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 WebSocketHub Durable 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 pdfkit or puppeteer, 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 /tmp reliably or running background cleanup timers.
    • Implementation: jsPDF generates the PDF into an ArrayBuffer in memory. This buffer is immediately PUT to an R2 bucket.
    • Flow: Generate Buffer → Upload to R2 → Return Signed URL / Public URL.

3. Image Processing

  • Legacy: sharp (native module) or child_process with 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 PackageStatusReplacement / Mitigation
express❌ Incompatiblehono
mongoose❌ Incompatible@prisma/client + @prisma/adapter-d1
amqplib❌ Incompatibleenv.QUEUE.send() (Native Binding)
fs (native)❌ Incompatibleenv.STORAGE (R2 Binding)
ws / socket.io❌ IncompatibleWebSocketPair (Native) + Durable Objects
jsonwebtoken⚠️ CompatibleCompatible, but often replaced by hono/jwt for speed.
bcrypt❌ Incompatiblebcryptjs (Pure JS version)

SCS Smart City - Traffic, Gateway, Camera, and NVR Platform