npmとpackage.jsonの基本|依存関係管理の仕組みを理解する
npmとpackage.jsonを理解すると、Node.jsプロジェクトで「何を入れたか」「どこまで自動でそろうか」「なぜ同じコードなのに環境差が出るのか」を整理して見られるようになります。実務では、社内ツールのCLI、フロントエンドの開発環境、ビルド用のライブラリ管理で必ず通る基礎です。
先に結論を書くと、package.jsonは依存関係の宣言、package-lock.jsonは実際に解決された版の固定です。npm installはこの2つを見ながらnode_modulesを組み立て、開発環境やCIで同じ依存関係を再現しやすくします。
- できること: 必要なパッケージを宣言し、チームで同じ依存関係を再現できる
- 使う場面: Node.jsアプリ、フロントエンド開発、ビルドツール、テスト環境の構築
- 最初に押さえる点:
dependenciesとdevDependenciesの違い、package-lock.jsonの役割、npm ciの使いどころ - 想定環境: Node.jsのLTS系とnpm。本文は2026年4月時点のnpm Docs CLI v11系とNode.js公式リリース情報を前提に整理
npmとpackage.jsonで何が起きているのか
npmはNode.js向けのパッケージマネージャーです。ライブラリを1個入れるだけでも、そのライブラリが内部で使う別のライブラリまでまとめて取得し、node_modules配下に配置します。
ここで中心になるのがpackage.jsonです。これは、そのプロジェクトが必要とするパッケージや実行コマンドを記録する設定ファイルです。
ここがポイント:
package.jsonが「この条件の依存関係を使いたい」、package-lock.jsonが「今回この版で確定した」を担当します。
ざっくり言うと、役割は次のとおりです。
package.json: プロジェクト名、バージョン、スクリプト、依存関係の範囲を定義するpackage-lock.json: 依存関係ツリーの具体的な解決結果を固定するnode_modules: 実際にインストールされたパッケージ本体が入る
この3つを分けて考えると、npmの動きがかなり読みやすくなります。
前提環境とバージョンの見方
npmまわりはバージョン差で挙動が変わることがあります。とくにpeerDependenciesやロックファイル形式は、古いnpmと新しいnpmで説明が食い違いやすい部分です。
2026年4月時点では、Node.js公式のLTS系としてv24系とv22系が案内されており、npm Docsの最新系ドキュメントはCLI v11系です。基本操作そのものはnpm 7以降でもかなり共通ですが、古い記事を読むときは「その説明が何系のnpmか」を確認したほうが安全です。
確認コマンドはこれで足ります。
node -v
npm -v
package.jsonの最小構成
最初は全部覚える必要はありません。入門段階で重要なのは、name、version、scripts、dependencies、devDependenciesです。
最小構成の例です。
{
"name": "sample-app",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "node index.js",
"test": "node --test"
},
"dependencies": {
"axios": "^1.9.0"
},
"devDependencies": {
"eslint": "^9.0.0"
}
}
この内容で分かることは明確です。
- アプリ名は
sample-app - 実行用ライブラリとして
axiosを使う - 開発用ツールとして
eslintを使う npm startでnode index.jsを実行する
つまりpackage.jsonは、コードを直接書くファイルではなく、そのコードを動かす前提をそろえるための設計図です。
依存関係の種類をまず分けて覚える
依存関係管理で初心者が詰まりやすいのは、「全部dependenciesに入れてしまう」ことです。ここは用途で分けたほうが、デプロイやCIで混乱しません。
dependencies
本番実行で必要なものです。アプリが動くために必須のライブラリを入れます。
例:
expressaxiosreact
追加コマンド:
npm install axios
devDependencies
開発中だけ必要なものです。テスト、Lint、ビルド、型チェックなどが入ります。
例:
eslintprettiertypescriptvitest
追加コマンド:
npm install -D eslint
peerDependencies
自分のパッケージが、特定のホストライブラリと一緒に使われる前提を示すときに使います。アプリ開発より、プラグインやライブラリ開発で重要です。
たとえばReact向けの部品ライブラリなら、「React 18系が必要」と宣言したい場面があります。そのときにpeerDependenciesを使います。
optionalDependencies
入ると便利だが、入らなくてもインストール全体は止めたくない依存関係です。OS差やネイティブビルドの事情があるパッケージで見かけます。
overrides
依存関係のさらに先にあるパッケージの版を上書きしたいときに使います。脆弱性回避や、依存先の版をそろえたいときに効きます。
ただし、これは応急処置として使う場面が多いです。普段の入門では、まずdependenciesとdevDependenciesを正しく使い分けるだけで十分です。
^や~は何を意味するのか
package.jsonの依存関係には、版そのものではなく「許容範囲」を書くことが多くあります。これが分からないと、なぜ環境ごとに少し違う版が入るのか見えません。
よく使う指定は次の3つです。
1.2.3: その版だけを使う~1.2.3: だいたい1.2系の更新を許可する^1.2.3: 通常は同じメジャー版内の更新を許可する
例:
{
"dependencies": {
"axios": "^1.9.0",
"dayjs": "~1.11.0",
"zod": "4.0.0"
}
}
この書き方があるので、package.jsonだけでは最終的なインストール結果が毎回完全一致するとは限りません。そこでpackage-lock.jsonが効いてきます。
package-lock.jsonはなぜ必要か
package-lock.jsonは、npmが実際に解決した依存関係ツリーを記録するファイルです。npm公式ドキュメントでも、後続のインストールで同じ依存関係ツリーを再現しやすくするためのものとして説明されています。
これが重要なのは、直接入れた1個のライブラリの背後に、さらに多くの間接依存があるからです。axiosを1個追加しても、実際にはその周辺ライブラリも含めた木構造ができます。
package-lock.jsonをコミットする意味は次のとおりです。
- チーム全員が同じ版でインストールしやすくなる
- CIや本番デプロイで差分が出にくくなる
- 「昨日は動いたのに今日は依存関係だけ変わった」を減らせる
npm installは、package.jsonの範囲とpackage-lock.jsonの解決結果を比較して処理します。npm公式では、ロックファイルの内容がpackage.jsonの範囲を満たすならロックファイルの版を使い、満たさないならpackage.json側を優先して再解決し、ロックファイルも更新すると説明されています。
実際の流れを最小例で見る
ここでは、新規プロジェクトにHTTPクライアントを追加する流れを見ます。
1. プロジェクトを作る
mkdir sample-app
cd sample-app
npm init -y
この時点で、最低限のpackage.jsonができます。
{
"name": "sample-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}
2. 実行用ライブラリを追加する
npm install axios
すると、主に次の3つが起こります。
dependenciesにaxiosが追記されるpackage-lock.jsonが生成または更新されるnode_modulesに実体が入る
package.jsonの変化はこうです。
{
"name": "sample-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"axios": "^1.9.0"
}
}
3. 開発用ツールを追加する
npm install -D eslint
今度はdevDependenciesに入ります。
{
"devDependencies": {
"eslint": "^9.0.0"
}
}
この違いは、本番環境に何を持っていくかを整理するうえで効きます。Webアプリをデプロイするとき、ESLint自体は本番で実行しないことが多いからです。
npm installとnpm ciの違い
実務で混同しやすいのがここです。結論だけ先に書くと、普段の追加や更新はnpm install、CIで再現性を優先するならnpm ciが基本です。
npm install
- 依存関係を追加するときに使う
package.jsonやpackage-lock.jsonを更新することがある- ローカル開発で最もよく使う
npm ci
- 既存の
package-lock.jsonに厳密に合わせてクリーンインストールする - 自動テスト、CI、デプロイ向け
package.jsonとpackage-lock.jsonの不整合があると失敗させやすい
たとえば、GitHub Actionsや社内CIで毎回同じ依存関係を入れたいなら、npm ciのほうが向いています。ローカルでは便利さを優先してnpm install、自動化では再現性を優先してnpm ci、という住み分けが分かりやすいです。
scriptsもpackage.jsonの重要機能
package.jsonは依存関係だけのファイルではありません。scriptsにコマンドをまとめると、チーム内で実行手順をそろえやすくなります。
例:
{
"scripts": {
"dev": "node index.js",
"lint": "eslint .",
"test": "node --test"
}
}
これで、実行側は長いコマンドを覚えなくて済みます。
npm run lint
npm test
実務で意味があるのは、READMEや口頭説明に依存しにくくなることです。「Lintはこのコマンド」「テストはこの引数付き」と人ごとに違う状態を減らせます。
実務でよくある使いどころ
npmとpackage.jsonは、次のような場面でほぼ必須です。
Node.jsの社内ツール
CSV変換、API取得、定期実行スクリプトなどを作るとき、axiosやcsv-parseのようなライブラリを管理できます。担当者が変わっても、npm installで環境を再現しやすいのが利点です。
フロントエンド開発
ReactやViteのプロジェクトでは、アプリ本体だけでなく、ビルドツール、Lint、テストツールまでnpmで管理します。package.jsonがその構成図になります。
CI/CD
自動テストやデプロイでnpm ciを使うと、ローカルでは通るのにCIだけ失敗する、という依存関係起因の揺れを抑えやすくなります。
よくある失敗と対処
npm入門で起きやすい失敗は、だいたい決まっています。
依存関係の種類を入れ間違える
eslintやtypescriptまでdependenciesに入れると、本番不要なものまで抱え込みやすくなります。
対処:
- 実行時に必要なら
dependencies - 開発時だけなら
devDependencies
package-lock.jsonを消して運用する
ローカルでは動いても、別環境で別の版が入る原因になります。アプリ開発では、基本的にpackage-lock.jsonも一緒に管理したほうが安全です。
node_modulesをGit管理してしまう
容量が大きく、環境依存差分も増えやすいので非推奨です。通常はpackage.jsonとpackage-lock.jsonを管理し、node_modulesは毎回インストールで作ります。
古い記事のコマンドをそのまま使う
npm 6以前とnpm 7以降で、peerDependenciesやロックファイル周辺の説明が違う記事があります。うまくいかないときは、まず自分のnpm -vと公式ドキュメントの版を見比べてください。
関連ツールや代替手段との使い分け
JavaScriptのパッケージ管理はnpmだけではありません。実務ではpnpmやYarnを見ることもあります。
ただ、入門段階ではnpmから始めるのが無難です。Node.jsを入れると一緒に使い始めやすく、公式ドキュメントも豊富だからです。
使い分けの目安は次のとおりです。
- npm: 標準的に始めやすい。学習コストが低い
- pnpm: ディスク効率や高速性を重視したい現場で選ばれやすい
- Yarn: 既存プロジェクトで採用されているなら合わせる価値がある
まずはnpmでpackage.jsonとpackage-lock.jsonの役割を理解しておけば、別のパッケージマネージャーに移っても考え方は流用できます。
最初に覚えるべき要点
最後に、実務で迷いにくくするための最小セットだけ絞ります。
package.jsonは依存関係の宣言書package-lock.jsonは解決結果の固定ファイル- 実行時に必要なものは
dependencies - 開発時だけ必要なものは
devDependencies - 追加や更新は
npm install - CIやデプロイの再現性重視なら
npm ci
npmを理解する最初の一歩は、コマンドを丸暗記することではありません。package.jsonに何を書き、package-lock.jsonが何を守っているのかを見分けることです。ここが分かると、次にReact、Vite、TypeScript、テストツールへ進んでも、依存関係のトラブルをかなり自力で追えるようになります。
