billing hacks
Cloudflare Deep DiveRuntime Limitations

Runtime Limitations

Node.js APIs that break on Cloudflare Workers and how to work around each one.

Compatibility Warning

Workers run V8 isolates, not Node.js. fs, native modules, and many npm packages won't work. Audit your dependencies before migrating.

Cloudflare Workers run V8 isolates, not Node.js. Most Node.js APIs don't exist. Here's what breaks and how to fix it.

The Big Three

1. File System (fs)

// ❌ BREAKS: No filesystem in Workers
import fs from 'fs';
const config = fs.readFileSync('./config.json');

// ✅ WORKS: Import at build time
import config from './config.json';

// ✅ WORKS: Use KV or R2 for runtime data
const config = await env.CONFIG_KV.get('settings', 'json');

Any package that reads files at runtime will fail. This includes most config loaders, template engines, and file-based databases.

2. Native Modules

// ❌ BREAKS: Native bindings don't work
import bcrypt from 'bcrypt';
import sharp from 'sharp';

// ✅ WORKS: Pure JS alternatives
import bcryptjs from 'bcryptjs';

// ✅ WORKS: Use Cloudflare's image service
// Or offload to a separate image optimization worker

Common replacements:

Native ModulePure JS Alternative
bcryptbcryptjs
sharpCloudflare Images / external service
canvas@napi-rs/canvas (limited)
better-sqlite3D1 Database
pg (native)@neondatabase/serverless

3. Node.js Crypto Specifics

// ❌ BREAKS: Node-specific crypto
import crypto from 'crypto';
const hash = crypto.createHash('sha256').update(data).digest('hex');

// ✅ WORKS: Web Crypto API
const hashBuffer = await crypto.subtle.digest('SHA-256',
  new TextEncoder().encode(data)
);
const hash = [...new Uint8Array(hashBuffer)]
  .map(b => b.toString(16).padStart(2, '0'))
  .join('');

Streams Differences

Node.js streams and Web streams are different:

// ❌ BREAKS: Node.js streams
import { Readable } from 'stream';
const nodeStream = new Readable();

// ✅ WORKS: Web streams
const webStream = new ReadableStream({
  start(controller) {
    controller.enqueue('data');
    controller.close();
  }
});

Buffer vs Uint8Array

// ❌ BREAKS: Node.js Buffer
const buf = Buffer.from('hello');

// ✅ WORKS: TextEncoder/Uint8Array
const buf = new TextEncoder().encode('hello');

// ✅ WORKS: Buffer polyfill (if you must)
// Cloudflare provides a Buffer polyfill, but prefer native Web APIs

Environment Variables

// ❌ BREAKS: process.env at runtime
const apiKey = process.env.API_KEY;

// ✅ WORKS: Accessed via env binding
export default {
  async fetch(request, env) {
    const apiKey = env.API_KEY;
  }
};

// ✅ WORKS: In Next.js pages/API routes
// env is passed through the adapter

Database Connections

Traditional connection pooling doesn't work:

// ❌ BREAKS: Persistent connections
import { Pool } from 'pg';
const pool = new Pool(); // Connection dies between requests

// ✅ WORKS: HTTP-based database clients
import { neon } from '@neondatabase/serverless';
const sql = neon(env.DATABASE_URL);

// ✅ WORKS: Cloudflare D1
const result = await env.DB.prepare('SELECT * FROM users').all();

// ✅ WORKS: PlanetScale serverless driver
import { connect } from '@planetscale/database';

Time and Timers

// ⚠️ CAREFUL: setTimeout has limits
setTimeout(() => {}, 30000); // Max ~30 seconds

// ❌ BREAKS: setInterval in request context
setInterval(() => {}, 1000); // Doesn't persist

// ✅ WORKS: Use Cron Triggers for scheduled work
// wrangler.toml
// [triggers]
// crons = ["*/5 * * * *"]

Request/Response Differences

// ❌ BREAKS: Express-style req/res
app.get('/api', (req, res) => {
  res.json({ data: true });
});

// ✅ WORKS: Web standard Request/Response
export async function GET(request: Request) {
  return Response.json({ data: true });
}

The Compatibility Matrix

Check your dependencies against Cloudflare's compatibility:

npx wrangler dev
Error MessageLikely CauseFix
X is not definedMissing polyfillAdd polyfill or replace with Web API
Cannot find moduleNative Node.js moduleReplace with edge-compatible alternative
TypeError: X is not a functionAPI differenceCheck workerd compatibility docs

Quick Audit Checklist

Before migrating, grep your codebase:

# Find Node.js API usage
grep -r "require('fs')" src/
grep -r "from 'fs'" src/
grep -r "process.env" src/
grep -r "Buffer.from" src/
grep -r "require('crypto')" src/

Next: Caching on Cloudflare - How caching works differently and ISR alternatives.

On this page