Was npm wirklich ist
npm ist eigentlich drei Dinge in einem. Zum einen eine Registry – eine riesige öffentliche Datenbank mit JavaScript-Paketen auf npmjs.com. Zum anderen ein Kommandozeilen-Tool, das zusammen mit Node.js ausgeliefert wird und mit dem du diese Pakete installierst und verwaltest. Und drittens eine Spezifikation (das Format der package.json), die beschreibt, was ein Projekt braucht.
Wenn du npm install express ausführst, spricht das CLI mit der Registry, lädt express samt aller Abhängigkeiten herunter, legt die Dateien in einem Ordner namens node_modules ab und trägt das Paket mit seiner Version in deine package.json ein. Das ist der komplette Ablauf.
Wenn Node.js installiert ist, hast du npm automatisch dabei. Kurz prüfen:
node --version
npm --version
Wenn bei beiden eine Version angezeigt wird, kann es losgehen.
Projekt anlegen mit npm init
Jedes npm-Projekt braucht eine package.json. Diese Datei ist quasi das Manifest des Projekts: Darin stehen Name, Version, Skripte und Abhängigkeiten. Am schnellsten legst du sie mit npm init -y an – damit werden alle Standardwerte übernommen:
mkdir my-app
cd my-app
npm init -y
Das schreibt etwa Folgendes:
{
"name": "my-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Wenn du das -y weglässt, fragt npm jedes Feld einzeln ab. So oder so landest du am Ende bei einer package.json – der Datei, an der alles andere hängt. Die einzelnen Felder sehen wir uns auf der nächsten Seite genauer an.
Pakete installieren mit npm install
Sobald eine package.json existiert, installierst du ein Paket mit npm install (oder kurz npm i):
npm install lodash
Dabei passieren drei Dinge:
- npm lädt
lodashsamt Abhängigkeiten in den Ordnernode_modules/herunter. - Im Abschnitt
dependenciesderpackage.jsonwird"lodash": "^4.17.21"(oder die jeweils aktuelle Version) ergänzt. - Es entsteht eine
package-lock.json, in der die exakten Versionen aller Pakete im Abhängigkeitsbaum festgehalten werden.
Jetzt kannst du das Paket verwenden:
Der require-Aufruf (bzw. import in einem ESM-Projekt) findet das Paket, indem er in node_modules nachschaut. Du musst keinen Pfad angeben — darum kümmert sich Nodes Module Resolver.
dependencies vs devDependencies
Nicht jedes Paket wird gebraucht, wenn deine App in Produktion läuft. Test-Frameworks, Linter und Bundler sind nur während der Entwicklung relevant. Solche Pakete installierst du mit --save-dev (oder -D):
npm install --save-dev jest
npm install -D eslint prettier
Diese Pakete landen dann in devDependencies statt in dependencies:
{
"dependencies": {
"lodash": "^4.17.21"
},
"devDependencies": {
"jest": "^29.7.0",
"eslint": "^8.57.0",
"prettier": "^3.2.5"
}
}
Auf einem Production-Server überspringt npm install --omit=dev den Dev-Bereich komplett – das Installationsergebnis bleibt kleiner und geht schneller. Diese Trennung sauber hinzubekommen, ist wichtiger, als es auf den ersten Blick wirkt: Ein webpack, das versehentlich in den dependencies landet, bläht jedes Production-Deployment unnötig auf.
Alle Pakete auf einmal installieren
Wenn du ein Repo klonst, das bereits eine package.json mitbringt, musst du die Pakete nicht einzeln auflisten. Es reicht ein Befehl:
npm install
Ohne Argumente liest npm install die package.json aus (und hält sich dabei strikt an die in der package-lock.json festgehaltenen Versionen) und installiert den kompletten Abhängigkeitsbaum in node_modules. Das ist der erste Befehl, den du nach einem frischen Checkout ausführst.
Genau deshalb gehört node_modules auch in die .gitignore. Der Ordner lässt sich jederzeit aus dem Lockfile neu erzeugen, ist riesig und ändert sich bei jedem npm install. Einchecken solltest du nur package.json und package-lock.json – alle anderen generieren sich ihr node_modules lokal selbst.
Pakete aktualisieren
Mit npm outdated siehst du, was veraltet ist:
npm outdated
Die Ausgabe zeigt dir eine Tabelle mit den Spalten Current, Wanted und Latest. Wanted ist die neueste Version, die innerhalb des in package.json definierten Bereichs erlaubt ist (bei ^4.17.21 also alles unterhalb von 5.0.0). Latest hingegen ist die aktuellste veröffentlichte Version überhaupt – das kann durchaus ein Major-Release sein, auf den du dich noch nicht eingelassen hast.
So aktualisierst du innerhalb des erlaubten Versionsbereichs:
npm update
Um wirklich auf die allerneueste Version zu springen – inklusive Major-Versionen – installierst du das Paket einfach erneut mit @latest:
npm install lodash@latest
Major-Releases können deinen Code brechen – genau davor soll dich die Versionsnummer ja warnen. Bevor du so einen Sprung mitgehst, wirf unbedingt einen Blick ins Changelog.
npm Pakete deinstallieren
Ein Paket zu entfernen funktioniert genauso unkompliziert wie das Installieren:
npm uninstall lodash
Damit verschwindet es aus node_modules und der Eintrag in der package.json wird ebenfalls entfernt. Bei einer Dev-Dependency hängst du -D an — npm erkennt das zwar von selbst, aber explizit zu sein erspart dir böse Überraschungen in Scripts.
npm global vs. lokal installieren
In der Regel installierst du Pakete lokal, also fest an ein Projekt gebunden und im jeweiligen node_modules abgelegt. Eine Ausnahme bilden Kommandozeilen-Tools, die du überall verfügbar haben willst:
npm install -g typescript
npm install -g http-server
Ein globales Install legt das Tool an einem systemweiten Ort ab und hängt dessen bin-Eintrag an deinen PATH, sodass du tsc oder http-server aus jedem beliebigen Verzeichnis heraus aufrufen kannst. Der Haken: Global installierte Pakete werden nicht pro Projekt festgehalten und können zwischen verschiedenen Rechnern leicht auseinanderlaufen.
Für einmalige Befehle ist npx meist der bessere Kompromiss – es ist ohnehin bei npm mit dabei:
npx create-react-app my-app
npx prettier --write .
npx führt ein Paket aus, ohne es global zu installieren – es wird bei Bedarf heruntergeladen, ausgeführt, und fertig. Für Tools, die du nur einmal brauchst, ist das sauberer als eine dauerhafte globale Installation.
Die wichtigsten Befehle auf einen Blick
Die Befehle, die du im Alltag tatsächlich brauchst:
npm init -y # package.json erstellen
npm install # alles aus package.json installieren
npm install <pkg> # eine Laufzeit-Abhängigkeit hinzufügen
npm install -D <pkg> # eine Entwicklungs-Abhängigkeit hinzufügen
npm install -g <pkg> # ein CLI-Tool global installieren
npm uninstall <pkg> # eine Abhängigkeit entfernen
npm outdated # sehen, was veraltet ist
npm update # innerhalb der erlaubten Bereiche aktualisieren
npm install <pkg>@latest # auf die neueste Version springen
npm run <script> # ein Skript aus package.json ausführen
npx <pkg> # ein Paket ausführen, ohne es zu installieren
Das war's im Großen und Ganzen zu npm. Den Rest — Publishing, Workspaces, Scoped Packages — kannst du dir holen, sobald du ihn wirklich brauchst.
Was steckt eigentlich in node_modules?
Noch ein Denkmodell zum Mitnehmen: node_modules ist ein weitgehend flacher Ordner, der jedes Paket enthält, von dem dein Projekt abhängt — plus alles, wovon diese Pakete wiederum abhängen, also die gesamte transitive Kette. Ein einziges Paket zu installieren kann schnell hundert weitere nach sich ziehen, und das ist völlig normal. npm dedupliziert, wo es geht: Hängen zwei Pakete an derselben Version von lodash, teilen sie sich eine Kopie.
Die Lockdatei (package-lock.json) hält die exakt aufgelöste Version jedes dieser Pakete fest. Genau das macht Builds reproduzierbar: Zwei Entwickler, die mit derselben Lockdatei npm install ausführen, bekommen byteweise identische Abhängigkeitsbäume — auch noch Monate später.
Betrachte node_modules als generiertes Artefakt. Fass da drin nie Dateien an — deine Änderungen sind beim nächsten Install von irgendwem wieder weg. Deshalb gehört node_modules übrigens immer in die .gitignore.
Weiter geht's mit package.json
package.json ist die Datei, die npm die ganze Zeit im Hintergrund liest und umschreibt. Sobald du ihre Felder kennst — scripts, main, type, Versionsbereiche, engines — wird npm vom Blackbox-Tool zu etwas, das du wirklich unter Kontrolle hast. Genau darum geht's im nächsten Teil.
Häufig gestellte Fragen
Was ist npm eigentlich?
npm ist der Standard-Paketmanager für Node.js. Es wird zusammen mit Node installiert, betreibt eine riesige öffentliche Registry für JavaScript-Pakete und bringt ein Kommandozeilen-Tool mit, um Pakete zu installieren, zu aktualisieren und zu veröffentlichen. Wenn du npm install lodash ausführst, lädt npm lodash aus der Registry nach node_modules und trägt es in deine package.json ein.
Was ist der Unterschied zwischen dependencies und devDependencies?
dependencies sind die Pakete, die deine App zur Laufzeit in Produktion braucht – also z. B. express oder react. devDependencies brauchst du nur während der Entwicklung oder beim Build: Test-Runner, Bundler, Linter. Letztere installierst du mit npm install --save-dev <paket> (oder kurz -D). In Produktion lässt du sie mit npm install --omit=dev einfach weg.
Sollte ich node_modules in Git einchecken?
Nein, auf keinen Fall. node_modules kann schnell mehrere hundert MB groß werden und lässt sich aus package.json und package-lock.json jederzeit exakt wiederherstellen. Pack den Ordner in die .gitignore und committe stattdessen die Lockfile. Wer dein Repo klont, bekommt mit einem npm install denselben Dependency-Tree wie du.
Was heißt global vs. lokal bei npm install?
Eine lokale Installation (npm install <paket>) legt das Paket im node_modules-Ordner deines Projekts ab und trägt es in die package.json ein. Eine globale Installation (npm install -g <paket>) installiert das Paket systemweit – typischerweise für CLI-Tools, die du überall verfügbar haben willst. Für Projektabhängigkeiten solltest du lokal installieren, damit die Versionen pro Projekt sauber gepinnt bleiben.