2.1 npm: Projekt und Abhängigkeiten
Nach dieser Einheit kannst du:
- ein Node.js-Projekt mit
npm initanlegen und Skripte in derpackage.jsondefinieren und ausführen. - Pakete als
dependenciesoderdevDependenciesinstallieren und die Wahl mit der Prüffrage „Braucht das laufende Programm dieses Paket?“ begründen. - begründen, warum
package.jsonund Sperrdatei ins Repository gehören undnode_modules/nicht.
Wer eine Wohnung einrichtet, baut den Kasten nicht selbst, sondern holt ihn aus dem Möbelhaus - samt Bauanleitung, Schraubenbeutel und der Hoffnung, dass kein Teil fehlt. Mit Software ist es genauso: Kaum jemand schreibt Datumsrechnung oder Eingabeprüfung selbst. Kapitel 2 zeigt in zwei Einheiten, wie man fertige Teile holt, ohne sich dabei Chaos ins Projekt zu tragen. Diese erste Einheit kümmert sich um die Einkaufsliste und darum, was ins Projekt kommt.
JavaScript bringt von Haus aus keine umfangreiche Standardbibliothek mit: kein Werkzeug für Datumsrechnung mit Zeitzonen, keine Validierung, kein Testframework, keinen Datenbankzugriff. Was andere Sprachen mitliefern, kommt hier aus dem Ökosystem. Der Zugang dazu ist npm, der Paketmanager von Node.js.
Das hat zwei Seiten. Auf der einen steht die größte Sammlung wiederverwendbarer Software, die es gibt; kaum ein Problem, für das nicht bereits ein Paket existiert. Auf der anderen steht die Verantwortung für fremden Code, den niemand im Team gelesen hat, der eigene Abhängigkeiten mitbringt und der irgendwann nicht mehr gepflegt wird. Kapitel 2 behandelt beides: in dieser Einheit die Werkzeuge, in Einheit 2.2 Versionen und das Urteilsvermögen im Umgang mit fremdem Code.
Begriffe vorab
Abschnitt betitelt „Begriffe vorab“- Paket (package): ein veröffentlichter, wiederverwendbarer Baustein aus fremdem Code, etwa ein Validierer oder ein Testframework.
- Registry: das zentrale Verzeichnis, aus dem Pakete geladen werden; für JavaScript die npm-Registry auf npmjs.com.
- Paketmanager (package manager): das Kommandozeilenwerkzeug, das Pakete aus der Registry holt, installiert und ihre Versionen verwaltet, hier
npmoderpnpm. - Abhängigkeit (dependency): ein Paket, das dein Projekt braucht. Pakete haben selbst wieder Abhängigkeiten, man spricht dann von transitiven Abhängigkeiten.
- Sperrdatei (lock file): hält fest, welche Version jedes Pakets tatsächlich installiert wurde (
package-lock.jsonbei npm,pnpm-lock.yamlbei pnpm).
Im Bild vom Möbelhaus: Die Registry ist das Lager, der Paketmanager der Lieferdienst, und die package.json aus dem nächsten Abschnitt ist deine Einkaufsliste.
Das Projekt: package.json
Abschnitt betitelt „Das Projekt: package.json“Ein JavaScript-Projekt beginnt mit einem Befehl:
npm init -y # -y accepts all defaultspnpm init # pnpm: no prompts, so no -y neededDas Ergebnis ist eine einzige Datei, package.json. Sie beschreibt das Projekt, und zwar maschinenlesbar. Genau deshalb kann ein fremder Rechner aus ihr die Arbeitsumgebung wiederherstellen.
{ "name": "buffet-app", "version": "1.0.0", "type": "module", "scripts": { "dev": "node --watch src/main.js", "test": "vitest run", "typecheck": "tsc --noEmit", "check": "biome check ." }, "dependencies": { "zod": "^4.4.3" }, "devDependencies": { "@biomejs/biome": "2.5.15", "typescript": "^7.0.2", "vitest": "^4.1.10" }}Beispielprojekt: 02_npm_pakete_und_projekte_examples/ex01_buffet_app
Die Felder im Einzelnen:
| Feld | Bedeutung |
|---|---|
name, version | Kennung des Projekts; Pflicht, sobald ein Paket veröffentlicht wird |
type | "module" legt fest, dass .js-Dateien Module mit import/export sind (siehe Kapitel 1) |
scripts | benannte Befehle, aufrufbar mit npm run <name> (pnpm: pnpm run <name>) |
dependencies | Pakete, die das laufende Programm braucht |
devDependencies | Pakete, die nur beim Entwickeln gebraucht werden |
Ein frisches npm init -y trägt bei type den Wert "commonjs" ein (getestet mit npm 11.10.1). Weil wir mit import und export arbeiten, änderst du ihn als Erstes auf "module"; sonst bricht Node beim ersten import mit Cannot use import statement outside a module ab.
Zurück zum Möbelhaus: Die package.json ist die Einkaufsliste. Sie sagt, was das Projekt braucht, aber nicht, welches Exemplar tatsächlich geliefert wurde. Das hält erst die Sperrdatei fest (Einheit 2.2, Abschnitt “Versionen”).
Skripte: die Befehle des Projekts
Abschnitt betitelt „Skripte: die Befehle des Projekts“scripts ist das am meisten unterschätzte Feld. Es hält fest, wie das Projekt gestartet, getestet und geprüft wird, und zwar so, dass es niemand im Wiki nachschlagen muss:
npm run devnpm run typechecknpm run checknpm test # test and start work without "run"
pnpm run devpnpm typecheck # pnpm: "run" is optional for every script namepnpm test # that does not clash with a built-in pnpm commandEin neues Teammitglied klont das Repository, führt npm install (pnpm: pnpm install) aus und liest in scripts, welche Befehle es gibt. Das ist gelebte Projektdokumentation an der einzigen Stelle, an der sie nicht veraltet: Ein falscher Eintrag fällt beim nächsten Aufruf sofort auf.
Wo begegnet dir das? Bei jedem Projekt, das du ab jetzt klonst, ob von Mitschüler:innen, aus einer Übungsvorlage oder aus einem Open-Source-Repository: Der erste Blick gilt scripts. Dort steht, ob das Projekt mit npm run dev, npm start oder etwas ganz anderem startet. Ab Kapitel 7 legen die Projektgeneratoren von Vite und Next.js diese Skripte für dich an.
Auflösung
npm test und npm run check. Für test (und start) kennt npm eine Abkürzung ohne run. npm check und npm typecheck sind keine npm-Befehle: npm meldet Unknown command und schlägt npm run check beziehungsweise npm run typecheck vor (getestet mit npm 11.10.1). Bei pnpm funktioniert pnpm check dagegen (getestet mit pnpm 12.5.1).
Abhängigkeiten installieren
Abschnitt betitelt „Abhängigkeiten installieren“npm install zod # into dependenciesnpm install --save-dev vitest # into devDependencies (short: -D)npm install # restore everything from package.jsonnpm uninstall zod # remove
pnpm add zod # into dependenciespnpm add --save-dev vitest # into devDependencies (short: -D)pnpm install # restore everything from package.jsonpnpm remove zod # removeJede Installation tut drei Dinge:
- Sie lädt das Paket in den Ordner
node_modules/. - Sie trägt es in die
package.jsonein. - Sie hält die exakt installierte Version in der
package-lock.jsonfest (pnpm:pnpm-lock.yaml).
Warum node_modules/ so groß wird
Abschnitt betitelt „Warum node_modules/ so groß wird“Pakete haben selbst Abhängigkeiten, und diese wieder. Im buffet-app stehen vier Pakete in der package.json. Ein npm install holt daraus 49 Pakete in rund 160 Ordnern mit knapp 150 MB (gemessen unter Windows im Oktober 2026).
Das ist normal und kein Grund zur Sorge. Weil node_modules/ jederzeit aus package.json und Sperrdatei wiederhergestellt werden kann, gehört der Ordner aber nie ins Repository. pnpm legt die Dateien nur einmal in einem zentralen Speicher auf dem Rechner ab und verknüpft sie per Links; das spart Plattenplatz, ändert aber nichts an der Regel.
dependencies gegen devDependencies
Abschnitt betitelt „dependencies gegen devDependencies“Die Unterscheidung ist keine Formsache. Die Prüffrage lautet: Braucht das fertige, laufende Programm dieses Paket? Im Möbelhaus-Bild gehören die Schrauben zum Kasten und bleiben drin, der Akkuschrauber dagegen wird nur zum Aufbauen gebraucht.
dependencies | devDependencies | |
|---|---|---|
| Gebraucht | im Betrieb des Programms | beim Entwickeln, Testen und Bauen |
| Im Möbelhaus-Bild | die Schrauben | der Akkuschrauber |
Beispiele im buffet-app | zod (prüft bei jeder Bestellung die Eingaben) | typescript, vitest, @biomejs/biome |
| Installieren mit | npm install <paket> | npm install --save-dev <paket> |
Auf dem Server läuft am Ende übersetztes JavaScript, kein Compiler und kein Testframework. Wer alles in dependencies wirft, schleppt beim Betrieb ein Vielfaches an Code mit, und jede Zeile davon ist eine mögliche Sicherheitslücke.
Wo begegnet dir das? Auf deinem eigenen Rechner installiert npm install beide Listen, denn zum Entwickeln brauchst du beides. Wird das Projekt später auf einem Server eingespielt, lässt npm ci --omit=dev die devDependencies weg. Genau dort zahlt sich die saubere Trennung aus.
Beispiel: Biome als devDependency
Abschnitt betitelt „Beispiel: Biome als devDependency“Biome kennst du aus Kapitel 1: Linter und Formatierer in einem Werkzeug. Im buffet-app ist es die typische devDependency, denn geprüft und formatiert wird beim Entwickeln, nicht im Betrieb. Eingerichtet wurde es so:
npm install --save-dev --save-exact @biomejs/biome # exact version, no ^npx @biomejs/biome init # creates biome.json
pnpm add --save-dev --save-exact @biomejs/biomepnpm exec biome initDanach steht "@biomejs/biome": "2.5.15" unter devDependencies, ohne ^ davor (warum, zeigt Einheit 2.2 im Abschnitt “Versionen”). Das Skript "check": "biome check ." wurde von Hand ergänzt. npm run check prüft damit das ganze Projekt auf einen Schlag, und alle im Team verwenden dieselbe Biome-Version.
Eine Einstellung musst du kennen: biome init stellt die Einrückung auf Tabs. Das buffet-app rückt mit Leerzeichen ein, deshalb steht in seiner biome.json unter formatter der Eintrag "indentStyle": "space". Ohne ihn meldet npm run check jede Datei als Formatfehler.
Was ins Repository gehört
Abschnitt betitelt „Was ins Repository gehört“-
package.json- ja. Sie ist die Projektbeschreibung. -
package-lock.json(pnpm:pnpm-lock.yaml) - ja. Sie ist der Grund, warum bei allen dasselbe installiert wird. -
node_modules/- nein. Der Ordner ist reproduzierbar, riesig und plattformabhängig; er gehört in die.gitignore.
Damit ist jedes geklonte Projekt mit einem einzigen npm install (pnpm: pnpm install) lauffähig.
Auflösung
react und date-fns laufen mit, wenn jemand die Anwendung benutzt: dependencies. vitest und @biomejs/biome braucht nur, wer am Code arbeitet: devDependencies. Die Prüffrage ist immer dieselbe: Läuft der Code dieses Pakets, wenn ein Benutzer die App verwendet?
Stolperfallen
Abschnitt betitelt „Stolperfallen“node_modules/eingecheckt: bläht das Repository um Zehntausende Dateien auf. In die.gitignore, bevor der erste Commit passiert.- Alles in
dependencies: Testframework und Compiler landen im Betrieb; unnötiger Code und unnötige Angriffsfläche. - Fehlendes
"type": "module": Node 24 führt die Datei trotzdem aus, warnt aber bei jedem Start mitMODULE_TYPELESS_PACKAGE_JSON. Steht dort"type": "commonjs", brichtimportmitCannot use import statement outside a moduleab. - Biome meldet jede Datei als Formatfehler:
biome initstellt die Einrückung auf Tabs, dein Code rückt mit Leerzeichen ein. Entweder in derbiome.json"indentStyle": "space"setzen oder einmalnpx @biomejs/biome check --write .laufen lassen.
Zusatzmaterialien
Abschnitt betitelt „Zusatzmaterialien“- npm Docs - die offizielle Referenz zu allen Befehlen; besonders lesenswert ist die Seite zu
npm install. - npm-Dokumentation: package.json - alle Felder mit Erklärung, auch die, die erst beim Veröffentlichen eigener Pakete wichtig werden.
- npmjs.com - die Paketsuche selbst: Hier siehst du zu jedem Paket Version, Abhängigkeiten und Installationsbefehl.
- pnpm-Dokumentation - Befehlsreferenz und Begründung für pnpm; hilfreich, wenn du statt npm damit arbeitest.
- Biome: Configuration - alle Einstellungen der
biome.json, darunterindentStyle.
Zum Anhören
Abschnitt betitelt „Zum Anhören“Du kannst dir diese Einheit kostenlos mit der Vorlesefunktion deines Browsers oder Betriebssystems vorlesen lassen (etwa „Laut vorlesen” in Edge), ohne Konto und ohne Upload. Freiwillig kannst du mit Google NotebookLM (Google-Konto nötig) aus diesem öffentlichen Text eine gesprochene Zusammenfassung erzeugen, auch auf Deutsch.
Lernergebnisse
Abschnitt betitelt „Lernergebnisse“Du kannst jetzt:
- ein Node.js-Projekt mit
npm init(pnpm:pnpm init) anlegen und Skripte in derpackage.jsondefinieren und ausführen. - Pakete als
dependenciesoderdevDependenciesinstallieren und die Wahl mit der Prüffrage „Braucht das laufende Programm dieses Paket?“ begründen. - begründen, warum
package.jsonund Sperrdatei ins Repository gehören undnode_modules/nicht.
Passende Übungen
Abschnitt betitelt „Passende Übungen“- Aufgabe 01 - TypeScript-Spickzettel, Stufe Wesentlich_überwiegend (Projekt anlegen,
package.json, Skripte,devDependencies); Sperrdatei undnpm cifolgen in Einheit 2.2
Quellen
Abschnitt betitelt „Quellen“package.json: alle Felder → npm-Dokumentation: package.jsonnpm install, Option--omit=dev→ npm-Dokumentation: npm installnpm init -yträgt"type": "commonjs"ein;npm checkist kein npm-Befehl,pnpm checkstartet das Skript → eigener Test mit npm 11.10.1 und pnpm 12.5.1 (Oktober 2026)- Verhalten von Node ohne
"type": "module"und mit"type": "commonjs"→ eigener Test mit Node.js 24.11.1 (Oktober 2026); Node.js-Dokumentation: Syntax detection - npm-Befehle allgemein → npm Docs
- npm-Registry, Paketsuche → npmjs.com
- pnpm-Befehle → pnpm-Dokumentation
- Biome als
devDependency,--save-exact,biome init→ Biome: Getting Started biome.json,indentStyle→ Biome: Configuration- Paketzahl, Ordnerzahl und Größe von
node_modules/sowie der Abhängigkeitsbaum imbuffet-app→ eigene Messung mitnpm installundnpm ls --all(Node.js 24.11.1, Windows, Oktober 2026) - Abb. 2.1 (Möbelhaus), Abb. 2.2 (Was bei
npm installpassiert), Abb. 2.3 (package.json), Abb. 2.4 (Abhängigkeitsbaum), Abb. 2.5 (Rechner und Server) → eigene Darstellung
Autoren
Abschnitt betitelt „Autoren“- Oliver Maurhart (MAO)
- Michael Gabriel (GAM)
CC-BY-SA