npm
Node.js に同梱される標準のパッケージマネージャーで、世界最大のソフトウェアレジストリを通じた JavaScript パッケージの公開 / インストール / 管理を行う
npm とは
npm は、Node.js に同梱される標準のパッケージマネージャーである。公式ドキュメントでは、公開レジストリ・コマンドラインクライアント (npm コマンド)・Web サイト (npmjs.com) の 3 つを合わせて npm と呼んでおり、レジストリは世界最大のソフトウェアレジストリと位置づけられている。2010 年に Isaac Z. Schlueter が開発し、Node.js のエコシステムの成長を支えた。
公開されているパッケージは数百万規模にのぼる。数の多さは利点だが、依存を 1 つ足すとその依存が引き連れてくるパッケージまで入るため、実際に取り込む数は宣言した数よりはるかに多くなる。npm の使い方で問題になるのは、たいていこの「宣言していないのに入っているもの」の扱いである。
package.json と package-lock.json
2 つのファイルは役割が違う。package.json は許容するバージョンの範囲を人が宣言するファイルで、package-lock.json はその範囲を解決した結果を機械が記録するファイルである。同じ package.json でも解決した時期が違えば別のバージョンが入り得るため、結果の記録が別ファイルとして必要になる。
{
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.0",
"zod": "^3.22.0"
},
"devDependencies": {
"typescript": "^5.3.0",
"vitest": "^1.0.0"
},
"scripts": {
"build": "tsc",
"test": "vitest run",
"start": "node dist/index.js"
}
}
package.json: 依存関係とバージョン範囲を宣言package-lock.json: 実際にインストールされた正確なバージョンを記録 (再現性の保証)
package-lock.json は必ず Git にコミットする。これがないと、開発者ごとに異なるバージョンがインストールされ、「自分の環境では動くのに CI では動かない」問題が発生する。
主要コマンド
よく使うコマンドは次の通りである。依存の解決結果を書き換えるもの (install 系) と、書き換えないもの (ci・run・audit・outdated) を区別して覚えると事故が減る。
npm install # package.json の依存をインストール
npm install express # パッケージを追加 (dependencies)
npm install -D vitest # 開発依存に追加 (devDependencies)
npm ci # lock ファイルから厳密にインストール (CI 向け)
npm run build # scripts のコマンドを実行
npm audit # 脆弱性のチェック
npm outdated # 更新可能なパッケージの一覧
npx create-next-app # パッケージを一時的にダウンロードして実行
この 2 つの違いは用途の好みではなく、動作そのものが別である。
| コマンド | 動作 | 用途 |
|---|---|---|
npm install | package.json のバージョン範囲を解決してインストールし、解決結果が lock と食い違えば lock を書き換える。個別パッケージの追加もできる | 開発時 |
npm ci | lock ファイルが必須。既存の node_modules を丸ごと削除してから lock の内容だけを再現し、package.json と lock には一切書き込まない。両者が不整合な場合は書き換えずにエラーで終了する | CI/CD |
CI で npm ci を選ぶ理由は速さではなく、インストール結果が lock から一歩も動かないことが保証される点にある。個別パッケージの追加はできないので、依存を増やす操作は手元の npm install で行い、書き換わった lock をコミットに含める運用になる。なお --legacy-peer-deps のように依存ツリーの形を変えるフラグは、npm install のときと npm ci のときで同じものを渡さないと結果が一致しない (2026 年 8 月時点の npm 11 系の仕様)。
セマンティックバージョニング
npm はセマンティックバージョニング (SemVer) に基づいてバージョンを管理する。
^4.18.0 → 4.x.x の最新 (メジャーバージョン固定)
~4.18.0 → 4.18.x の最新 (マイナーバージョン固定)
4.18.0 → 完全一致
^ (キャレット) が npm install の既定で、メジャー・マイナー・パッチのうち左端の 0 でない数字を固定し、その右側の更新だけを許容する。1.0.0 以降なら「マイナーとパッチを許容」になるが、^0.2.5 は 0.2.x のパッチだけ、^0.0.4 は一切の更新を許容しない。0.x はマイナー更新で破壊的変更が入る慣習に合わせた挙動で、^ を付けたつもりで実質固定になっている依存はここに該当する。
npm vs yarn vs pnpm
3 者の違いは、パッケージの実体をどこに置き、node_modules をどう組み立てるかにほぼ集約される。
| 観点 | npm | yarn | pnpm |
|---|---|---|---|
| ディスク使用量 | 大きい (各プロジェクトにコピー) | 大きい | 小さい (ハードリンク) |
| node_modules 構造 | フラット | フラット | シンボリックリンク |
| ワークスペース | v7 以降対応 | 初期から対応 | 対応 |
| 特徴 | Node.js 標準 | Plug'n'Play | 厳密な依存解決 |
pnpm はグローバルストアにパッケージを 1 回だけ保存し、各プロジェクトからハードリンクで参照する。モノレポで複数プロジェクトがある場合、ディスク使用量が大幅に削減される。インストール時間の差も同じ構造から来る。展開済みのファイルをリンクするだけで済む方式ほど、2 回目以降のインストールで展開とコピーの手間が減る。
サプライチェーンセキュリティ
npm パッケージは誰でも公開できるため、悪意のあるパッケージが紛れ込むリスクがある。
npm auditで既知の脆弱性を定期的にチェックするpackage-lock.jsonをコミットし、意図しないバージョン変更を防ぐnpm ciで lock ファイルを厳密に再現する- 依存パッケージの数を最小限に抑える
設定を見直すときの優先順位は決まっている。lock ファイルをコミットしているか、CI が npm ci になっているか、依存を追加するときに「これは本当に必要か」を一度止まって考えているか、の 3 点である。いずれも費用はほぼゼロで、効いてくるのは半年後以降である。
この記事は役に立ちましたか?