Edge Computing verändert, wie wir JavaScript deployen. Und mit diesem Wandel verschiebt sich auch, welche Modul-Systeme überhaupt noch funktionieren. CommonJS, jahrelang der Standard in Node.js, stößt an seine Grenzen. ESM übernimmt – nicht aus Prinzip, sondern aus Notwendigkeit.
CommonJS und ESM: Die Kurzfassung
Bevor es um Edge geht, die Grundlagen:
| Aspekt | CommonJS | ESM |
|---|---|---|
| Syntax | require() / module.exports | import / export |
| Laden | Synchron (blockierend) | Asynchron (nicht-blockierend) |
| Tree Shaking | Nein | Ja |
| Browser-Support | Nein (Bundler nötig) | Nativ |
| Node.js | Default bis heute | Ab Node 12+, Default ab Node 20+ |
CommonJS wurde für Server entwickelt – synchrones Laden ist dort kein Problem. ESM wurde für das Web entwickelt – asynchron, optimierbar, zukunftssicher.
ESM in Node.js aktivieren
Für ESM in Node.js gibt es zwei Wege:
Option 1: package.json anpassen:
{
"type": "module"
}
Option 2: Dateiendung .mjs verwenden.
Bei Imports immer die Dateiendung angeben:
import { foo } from './utils.js'; // nicht './utils'
Warum Edge-Plattformen CommonJS ablösen
Edge Computing bedeutet: Code läuft nicht in einem zentralen Rechenzentrum, sondern verteilt an hunderten Standorten weltweit – nah am Nutzer. Die Anforderungen sind radikal anders:
- Kalte Starts in Millisekunden – keine Zeit für synchrones Laden
- Winzige Bundles – jedes Kilobyte zählt
- Isolation – jede Anfrage läuft in einer eigenen Sandbox
CommonJS passt nicht in diese Welt. Es wurde für langlebige Server-Prozesse gebaut, nicht für kurzlebige Edge-Funktionen.
Die großen Edge-Plattformen
| Plattform | ESM-Support | CommonJS | Besonderheiten |
|---|---|---|---|
| Cloudflare Workers | Vollständig | Eingeschränkt | V8 Isolates, unter 50ms Cold Start |
| Vercel Edge Functions | Vollständig | Über Bundler | Next.js-Integration |
| Deno Deploy | Vollständig | Nein | Web-Standards first |
| Netlify Edge Functions | Vollständig | Eingeschränkt | Deno-basiert |
Deno Deploy ist konsequent: Kein CommonJS, nur ESM und Web-APIs. Cloudflare und Vercel bieten Kompatibilitätsschichten, aber die Richtung ist klar.
ESM auf dem Edge: Die technischen Vorteile
Tree Shaking funktioniert
ESM ermöglicht statische Analyse zur Build-Zeit. Der Bundler sieht exakt, welche Exports verwendet werden – und entfernt den Rest.
// utils.js
export function foo() { /* ... */ }
export function bar() { /* ... */ } // wird nie importiert
// app.js
import { foo } from './utils.js';
Bei ESM landet nur foo im Bundle. Bei CommonJS landet alles – weil require() dynamisch ist und der Bundler nicht wissen kann, was zur Laufzeit gebraucht wird.
Asynchrones Laden
ESM lädt Module asynchron und parallel. Das ist entscheidend für Edge-Funktionen, die in Millisekunden starten müssen.
// ESM: parallel laden
const [moduleA, moduleB] = await Promise.all([
import('./a.js'),
import('./b.js')
]);
CommonJS blockiert bei jedem require() – fatal für Cold Starts.
Top-Level Await
ESM erlaubt await auf oberster Ebene:
// ESM
const config = await fetch('/config.json').then(r => r.json());
export { config };
In CommonJS braucht es dafür Workarounds oder IIFEs.
Migration: Von CommonJS zu ESM
Schritt 1: package.json anpassen
{
"type": "module",
"exports": {
".": "./src/index.js"
}
}
Schritt 2: Imports umschreiben
// Vorher (CommonJS)
const express = require('express');
const { readFile } = require('fs/promises');
const config = require('./config.json');
// Nachher (ESM)
import express from 'express';
import { readFile } from 'fs/promises';
// JSON-Import (Node.js 20+)
import config from './config.json' assert { type: 'json' };
Schritt 3: Exports umschreiben
// Vorher
module.exports = { foo, bar };
module.exports.baz = baz;
// Nachher
export { foo, bar };
export { baz };
// oder
export default { foo, bar, baz };
Schritt 4: __dirname und __filename ersetzen
// Vorher (CommonJS)
const path = require('path');
const configPath = path.join(__dirname, 'config.json');
// Nachher (ESM)
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const configPath = join(__dirname, 'config.json');
Schritt 5: Dynamische Imports für Conditional Loading
// Vorher (CommonJS)
let adapter;
if (process.env.DB === 'postgres') {
adapter = require('./adapters/postgres');
} else {
adapter = require('./adapters/sqlite');
}
// Nachher (ESM)
const adapter = await import(
process.env.DB === 'postgres'
? './adapters/postgres.js'
: './adapters/sqlite.js'
);
Edge-Apps bauen: Praxisbeispiele
Cloudflare Workers mit ESM
// worker.js
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname === '/api/hello') {
return Response.json({ message: 'Hello from the Edge!' });
}
return new Response('Not Found', { status: 404 });
}
};
Kein require, kein module.exports – pure ESM mit Web-Standard-APIs.
Vercel Edge Functions
// api/edge.js
export const config = { runtime: 'edge' };
export default async function handler(request) {
const { searchParams } = new URL(request.url);
const name = searchParams.get('name') || 'World';
return new Response(`Hello, ${name}!`, {
headers: { 'content-type': 'text/plain' }
});
}
Deno Deploy
// main.ts
Deno.serve((request: Request) => {
const url = new URL(request.url);
if (url.pathname === '/') {
return new Response('Welcome to Deno Deploy!');
}
return new Response('Not Found', { status: 404 });
});
Deno braucht keine Konfiguration – ESM und TypeScript sind native.
CommonJS auf Edge: Die Einschränkungen
Manche Edge-Plattformen erlauben CommonJS über Bundler-Transformationen. Das funktioniert, hat aber Nachteile:
- Größere Bundles – kein echtes Tree Shaking
- Langsamere Builds – zusätzliche Transformation nötig
- Potenzielle Inkompatibilitäten – nicht alle CJS-Patterns lassen sich transformieren
Für Legacy-Code ist das ein Übergangspfad. Für neue Projekte gibt es keinen Grund, CommonJS zu wählen.
DSGVO und Edge: Ein Vorteil
Edge Computing kann DSGVO-Konformität vereinfachen: Daten werden in der Region verarbeitet, in der sie entstehen. Cloudflare bietet EU-only-Verarbeitung, Vercel hat europäische Edge-Standorte.
Das hat nichts mit ESM vs. CommonJS zu tun – aber es ist ein weiterer Grund, Edge-Deployment ernst zu nehmen.
Performance-Vergleich
| Metrik | CommonJS (Node.js) | ESM (Edge) |
|---|---|---|
| Cold Start | 100-500ms | 5-50ms |
| Bundle Size (typisch) | 500KB-2MB | 50-200KB |
| Time to First Byte | 50-200ms | 5-30ms |
| Globale Latenz | Abhängig vom Server-Standort | unter 50ms weltweit |
Die Zahlen variieren je nach Anwendung, aber die Größenordnung ist konsistent: Edge mit ESM ist schneller.
Wann CommonJS noch Sinn macht
- Legacy-Projekte mit großer Codebasis – Migration braucht Zeit
- Interne Tools ohne Performance-Anforderungen
- Abhängigkeiten die nur als CJS verfügbar sind (werden weniger)
Für neue Projekte, öffentliche APIs und alles mit Nutzer-facing Performance: ESM.
Die Entwicklung ist eindeutig: Edge-Plattformen setzen auf ESM, Web-Standards und asynchrone Patterns. CommonJS bleibt für Legacy-Code nutzbar, aber die Zukunft von JavaScript auf dem Server – und erst recht auf dem Edge – ist ESM.
Wer heute noch ein neues Projekt mit CommonJS startet, baut technische Schulden auf. Die Migration ist nicht schwer, aber sie wird nicht einfacher, je länger man wartet.