All articlesField notes

Cloudflare rebuilt Workers' module registry for Node.js compatibility — here's what 64 MiB bundles and URL-based imports mean for edge deployment

Cloudflare shipped a new module registry for Workers that enables Node.js by default, supports 64 MiB bundles, and uses URL-based imports with lazy compilation. Here's what the architecture shift means.

Sep 11, 2026 4 min read
cloudflare-workersedge-deploymentnodejs-compatibilitymodule-systems

Cloudflare shipped a rebuilt module registry for Workers yesterday. Node.js compatibility is now on by default, bundle size caps jumped from 10 MiB to 64 MiB, and the module system switched from internal IDs to URL-based imports with import.meta. The post walks through lazy compilation, shared code caches, and how they handle circular dependencies at the edge.

This matters because it's the first time a major edge runtime has matched V8 Isolates to the Node.js module resolution spec without forcing developers into a custom bundler or compatibility layer. If you've deployed Workers before, you know the pain: wrangle esbuild to inline dependencies, strip out node:fs, hope your imports resolve. That's gone.

What changed

The old registry used numeric IDs for modules. You'd import ./utils.js and the bundler would rewrite it to something like __modules[42]. That worked but broke import.meta.url, made stack traces unreadable, and forced eager evaluation of the entire bundle at startup.

The new system uses URLs as module identifiers. Each module gets a stable worker://worker-name/path/to/module.js URL. When you call import('./utils.js'), Workers resolves it to the full URL, checks a shared code cache (keyed by content hash), and compiles only if there's a cache miss. Lazy compilation means your 64 MiB bundle doesn't pay the parse cost upfront — only the entry module and its synchronous imports get evaluated on the first request.

Shared caches are per-account. If you deploy the same lodash build across 12 Workers, it compiles once. The cache persists across deploys unless the content hash changes. Cloudflare says this cut cold-start p99 latency by 40% in their internal benchmarks.

Node.js compatibility by default

Previously you had to set a nodejs_compat flag and accept a smaller bundle cap. Now it's the default. node:path, node:crypto, node:buffer — all available without config. The 64 MiB cap applies to the compressed bundle; uncompressed it can be larger. Cloudflare is targeting pnpm monorepos and Next.js apps that previously couldn't fit.

The tradeoff: if you use conditional exports ("node" vs "browser" in package.json), Workers now picks the Node.js path. Some packages ship heavier Node builds. Cloudflare recommends auditing your node_modules if bundle size spikes.

URL-based imports and import.meta

import.meta.url now returns the stable worker:// URL. That means you can do new URL('./data.json', import.meta.url) and resolve assets relative to the current module, which is how Vite and Rollup expect it to work. Previously this was undefined or a placeholder.

Circular dependencies are handled with a placeholder object that gets backfilled after both modules finish evaluating. The post includes a 6-step walkthrough of how Workers detects cycles during the parse phase and defers property assignment. It's closer to how Node.js handles it than how the old system did (which threw at runtime).

What this means for VioX deployments

We're evaluating Workers for the VioX OS internal API layer — the part that dispatches requests to Goldie, Jordan, and the CRM write-back jobs. Right now those run on a single t3.medium in us-east-1. Moving them to Workers would let us kill the EC2 instance and distribute the request handlers globally without managing a CDN.

The 64 MiB cap is enough for our current bundle (18 MiB uncompressed, 4.2 MiB gzipped). The Node.js compat is critical because we use node:crypto for HMAC verification on inbound webhooks and node:buffer for parsing multipart form data from ElevenLabs' pronunciation feedback API.

The lazy compilation is interesting for our use case because 80% of requests hit the same 3 endpoints (/voice/inbound, /voice/status, /crm/lead-update). The other 40 routes are admin tooling that fire once a day. Not paying the parse cost for those until they're called would save ~60ms on the first request to a new edge location.

One concern: Cloudflare's post mentions "clearer errors" but doesn't specify what that means. Our current error handling relies on stack traces that include file paths. If the worker:// URLs don't map back to the original source in a predictable way, we'll need to rework our logging middleware. We'll test that before committing.

The other concern is cache invalidation. Shared caches are great until you deploy a bad build and need to purge it across all Workers. Cloudflare doesn't document a manual purge API. If the content hash changes, the cache updates automatically, but if you need to force a recompile without changing code (say, to pick up a patched V8 version), it's unclear how.

Where this leaves Deno Deploy and Vercel Edge

Deno Deploy supports import.meta.url and Node.js compat but caps bundles at 20 MiB. Vercel Edge caps at 4 MiB and doesn't support most node: modules. Workers is now the only edge runtime where you can deploy a full Next.js app without ejecting half your dependencies.

That said, Deno's module resolution is still faster for TypeScript-first codebases because it skips the CJS-to-ESM transform. If you're writing new code in pure ESM and don't need node: APIs, Deno is probably still the better pick. Workers wins if you're lifting an existing Node.js service to the edge.

We'll run a side-by-side deploy of the VioX OS API on Workers and Deno Deploy next week and post the latency breakdown. The lazy compilation story is compelling but we need to see it under load with real traffic before we migrate off EC2.

/ 06 — Start hereOne business day response

Tell us what you'd like built.

Send us a paragraph about the workflow, phone line, or tool you want built. We'll reply within one business day with a one-page plan, a fixed price, and a delivery date you can put on a calendar.

  • 30-min scoping call, free
  • Written proposal within 48 hours
  • Fixed price before we start
  • Most builds delivered in 2–8 weeks