こんにちは!ひさふるです。
今回は"ちゃんと業務で使えるようになるための"というテーマで、npmを取り上げたいと思います。
私自身、業務でもnpmを多用します。
ただ、新卒で案件に配属された私には、単に"パッケージをインストールできるもの"という理解より、一歩踏み込んだ理解が求められました。
そんな経験から、今回はちゃんと業務で使えるようになるためのnpmを解説していこうと思います。
npmとは?
Node.js用のパッケージマネージャです。
最近は、web開発でReactやNext.js、Express.jsの開発を行うときに利用することが多い印象です。
【余談】
npmは「Node Package Manager」の略...ではありません。(よくそう紹介されることが多いですが)
正確には「npm is not an acronym (npmは頭文字ではない)」という文章を再帰的に表現したものだそうです。
(そういえばGNUも「GNU’s Not Unix」の略でしたが、アレのノリなんですかね...)
そもそもNode.jsとは
公式の言葉を借りるならクロスプラットフォームなJavaScriptランタイム環境です。
要は、もともとブラウザでしか動かなかったJavaScriptを様々な場所で動かせるようにするためのものであり、これによりローカルでJavaScriptを使った開発やサーバーサイドにおけるJavaScriptの実行が可能となります。
詳しくは、この方の記事がわかりやすいので参照してください。
Node.jsはJavaScriptをブラウザ外でも実行できるようにするためのもの
・用途の例①:ローカルで開発するための開発環境として利用する
・用途の例②:Next.jsなどにおいて、JavaScriptをサーバーサイドで実行する
npmができること
npmの主な役割は、パッケージのインストール・管理です。
パッケージはコードをまとめて配布可能にしたものであり、インストールすることで様々な機能を簡単に使えるようになります。
パッケージには様々な種類があり、例えばHTTPクライアントのAxiosなんかが有名ですね。
ReactやNext.jsなども、Nodeのパッケージとしてインストールすることで使うことができます。
〜パッケージとライブラリの違い〜
ちなみに、パッケージとライブラリはそれぞれ以下のような定義があるようです。
- ライブラリ:特定のコードを持つ機能をまとめたもの
- パッケージ:ライブラリを配布可能な状態にしたもの
ほぼ同じ意味として使われることが多い両者ですが、配布可能かどうかという点が大きな違いですね。
npmのすごいところ
(色々ありますが)一番はやはり簡単にパッケージ管理、特に依存関係の解決を行えるところでしょう。
例えば、あなたが使いたいパッケージAがあったとします。
しかし、パッケージAは内部でパッケージBやパッケージCを使っているため、必然的にパッケージAを使うためにはBもCもインストールせねばなりません。
まあB,Cの2つくらいならなんとかなりそうですが、パッケージによっては数十、数百と依存関係があったりしますし、BやCも別のパッケージDやパッケージEを必要とするかもしれません。
これを手動で解決するのはひじょ〜〜〜〜に大変です。
そこで、npmはAのインストールを指示するだけで、B,C,D,Eを含む必要なパッケージを全て取ってきてくれるのです。
このように、複雑な依存関係を自動で解決してくれるのがnpmのすごいところ、と言えるでしょう。
ただし、全てが完全に自動化しきれるわけではなく、npmを使いこなすには内部の仕組みの理解が必要不可欠です。
まずは、**内部で複雑な依存関係を解決してくれているんだな〜**ということは、頭に入れておきましょう。
基本的な使用の流れ
まずは実践あるのみ、実際に使ってみましょう。
Node.js + npmのインストール
npmはあくまでNode.jsのパッケージ管理ツールなので、Node.jsをインストールすると自動的にnpmも付いてきます。
インストール方法は環境(Windows, macOS, Linux)ごとに違うので、以下のページを参考にしてインストールを進めてください。
複数のNodeのバージョンを使いたいときは?
プロジェクトによっては、特定のNodeのバージョンが指定されていることも多いです。しかし、複数のプロジェクトで開発を行う場合は、指定されるバージョンが異なる場合も...
そんなときはNode.js用のバージョン管理ツールを使うことをオススメします。バージョン管理ツールを使えば、プロジェクトごとに別バージョンのNode.jsを使うことも可能です。
代表的なものはnvmなどがありますが、非常に様々な種類のものがありますので、以下の記事などを参考に選んでみてください。
プロジェクト初期化からパッケージインストールまで
まずはプロジェクトを初期化します。適当なフォルダの中で以下のコマンドを実行してください。
npm init
実行するといくつかの質問に答えるよう促されますが、最初は全てそのままEnterを押してしまってOKです。
すると、フォルダ内にpackage.jsonというファイルが作成されるはずです。
React/Next.jsの場合は?
Reactの場合はnpm create vite@latest、Next.jsの場合はnpx create-next-app@latest my-appコマンドでプロジェクトを作成する過程において、package.jsonが自動で作成されます。
このように使用するフレームワークなどによっては自動でpackage.jsonを作成してくれることも多いので、使用フレームワークが決まっている場合はそちらのドキュメントを優先してください。
package.jsonの説明は一旦置いとくとして、パッケージインストールを試してみましょうか。
HTTPクライアントのaxiosを入れてみましょう。
npm install axios
基本的には、このようにして必要なライブラリを追加していくことになります。
package.json, package-lock.json, mode_modules
上記のnpm initからnpm install 〇〇までを行うと、プロジェクトフォルダ内に以下の3つのファイルやフォルダが生成されるはずです。
- package.json
- package-lock.json
- node_modules
package.json
package.jsonは、そのプロジェクト(パッケージ)の様々な情報が記載されます。
名前やバージョン情報、ライセンス形態から始まり、npmにおいて最も重要な依存関係も記載されます。
イメージとしては、以下のような形式になります。
{
"name": "npm_test",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"axios": "^1.13.5"
}
}
dependenciesが依存関係ですね。先程axiosをインストールしていれば、"axios": "^1.13.5"のように依存関係が追加されているはずです。
他にも様々な情報を記載することができます。気になる方は以下のURLを見てみてください。
package.jsonはプロジェクトのメタデータ + 依存関係の管理ファイル
バージョニング表記について
さて、ここでバージョニング表記についても少し勉強しておきましょう。
dependenciesに記載するバージョン情報は、"完全一致(必ずそのバージョンをインストールする)"という指定方法以外に様々な指定方法があります。
例えば、以下のような指定はわかりやすいですね。
-
version:versionと完全一致するバージョンのみ -
>version:versionより大きいバージョンはインストールできる -
>=version:version以上のバージョンはインストールできる
では、axiosで指定されていた^1.13.5の^はどうでしょう。これはメジャー、マイナー、パッチというバージョン管理方法に基づく指定方法です。
例えば、1.13.5というバージョンにおいて、最初の1がメジャー、次の13がマイナー、最後の5がパッチを示す番号です。このように、バージョンは基本的にメジャー.マイナー.パッチという並びになっています。
これをもとに、以下のように指定することもできます。
-
^version: マイナー、パッチのアップデートは良いがメジャーアップデートは許可しない-
^1.13.5の場合、1.13.6や1.14.1を使っても良いが、2.0.0は使ってはいけない
-
-
~version: パッチアップデートは良いがメジャー、マイナーのアップデートは許可しない-
~1.13.5の場合、1.13.6は使っても良いが、1.14.0や2.0.0は使ってはいけない
-
基本的にnpm install 〇〇コマンドでインストールすれば、このあたりもよしなにやってくれますが、セキュリティの都合などで特定のバージョンを避けたい場合は手動で指定することもあります。
パッケージ管理ツールのnpmにおいて非常に重要な部分なので、ぜひ押さえておきましょう。
ちなみに、他にも多種多様な指定方法があります。気になる方はこちらをご参照ください。
package-lock.json
package-lock.jsonはpackage.jsonの依存関係をもとに自動的に生成されるファイルで、インストールされるパッケージのバージョンを固定(lock)するために使われます。
バージョニング表記についてで説明した通り、package.jsonの依存関係は完全一致以外にも指定方法があり、許可するバージョンに幅を持たせることができます。
また、npmのすごいところで述べたように、インストール対象のパッケージも、他のパッケージに依存していることがほとんどです。
そのため、そのような複雑な依存関係を解決し、最終的にnpmが「このパッケージのこのバージョンをインストールします!」と決定したもの一覧が記されているのがpackage-lock.jsonです。
また、あくまでnpmが依存関係を解決した結果が記載されるので、人が編集してはいけないのも重要なポイントです。
package-lock.jsonは実際にインストールするバージョン一覧を管理
node_modules
node_modulesはフォルダであり、npm installでインストールしたパッケージの実際のファイルが格納されます。
開くと大量のファイルが格納されており、「何がなんだかわからないな...」という印象を受けるかもしれませんが、基本的にここの管理はnpmが自動的にやってくれるので気にしなくてOKです。
また、node_modulesはpackage-lock.jsonをもとにインストール・生成されるので、一時的に消しちゃっても特に問題ありません(というか、たまに依存関係で不具合が起きたときは一度消して再インストールしたりします)。
node_modulesは実際にインストールされたパッケージの格納場所
package.json vs package-lock.json
名前も役割も似ている部分があるこの2つのファイルですが、今一度その違いを明確にしておきましょう。
| package.json | package-lock.json | |
|---|---|---|
| 役割 | プロジェクト情報の記載 依存関係の記載 |
インストールする パッケージ一覧の記載 |
| 誰が編集するか | 人間 (一部npm) | npm |
| バージョンの指定方法 | 使っても良いバージョンの 範囲を指定 |
実際にインストールする 特定のバージョンを指定 |
そもそもなぜpackage-lock.jsonが必要かと言えば、環境が変わっても同じ挙動を保証・再現するためです。
package.jsonは、あくまで使っても良いバージョンの範囲を指定します。
そのため、例えば>1.0.0という指定であれば1.0.1も1.5.0も2.0.0もありえるわけで...
これを素直に受け取り、チーム開発において各メンバーがそれぞれ好きなバージョンを使い始めたら環境が一致しなくなり、Aさんの環境では動くけどBさんの環境では動かない、なんてことが発生するかもしれません。
所謂再現性が無くなるというやつですね。
これを防止し、バージョンをロックするためのものがpackage-lock.jsonです。覚えておきましょう。
どれをチームで共有するべきか?
最後に、ファイル共有の有無と削除の可否について整理しておきます。
| 共有(git管理) | 削除 | |
|---|---|---|
| package.json | する | してはいけない |
| package-lock.json | する | (基本的に)しない |
| node_modules | しない | 可能 |
まず共有の有無についてですが、package.jsonはプロジェクト設定に関わる重要なファイルですし、package-lock.jsonも環境差分を無くすためのファイルなのでもちろん共有(git管理)してください。
ただし、node_modulesは、1.package-lock.jsonがあればいつでも再インストール可能なことと、2.フォルダ自体が非常に巨大であることから原則として.gitignoreに記述してgit管理の対象から外します。
それから削除の可否ですが、package.jsonは絶対に削除してはいけません。
package-lock.jsonはチーム内で環境差分を無くすことが目的なので基本的には削除しませんが、package.jsonがあれば再生成できるので、依存関係がコンフリクトして壊れてしまったときには一時的に削除して再生成することがあります。
node_modulesはpackage-lock.jsonからいつでも再生成できますし、何か依存関係で不具合が発生した場合はとりあえず雑に削除&再生成することも多いです。
それぞれの役割を把握して、正しく管理!
npm installの種類
さて、次はnpm installコマンドについても詳しく見ていきましょう。
npm install キホンのキ
まずは基本的な使い方から覚えていきましょう。
まず、npm installですが、他にもいくつか書き方(エイリアス)があります。
npm install
npm i
npm add
これらは全て同じ意味です。どれを使っても良いですが、打つのがラクなので慣れてくるとiを使う人が多い印象ですね。
インストールの使い分け
それから、インストールは大きく分けて2種類(ciコマンドを含めると3種類)あります。
| コマンド | 動作 |
|---|---|
npm install {パッケージ名} |
{パッケージ名}で指定されたパッケージをインストールし、package.jsonに依存関係を追加する (package-lock.jsonも更新される) |
npm install |
package.jsonに記載の依存関係に従いインストールを行う (package-lock.jsonが更新される) |
参考:npm ci
|
package-lock.jsonに記載の依存関係に従いインストールを行う |
npm install {パッケージ名}としてパッケージ名を指定すると、それ単体がインストールされます。パッケージを追加したいときによく使いますね。
npm install単体だと、package.jsonをもとにインストールを行います。
インストールしたいパッケージをpackage.jsonに手動で追記した場合や、package-lock.jsonとnode_modulesを消してインストール状況をリセットしたい場合に使いますね。
最後にnpm ciですが、これはpackage-lock.jsonをもとにしてインストールを行います。
先述の通り、package-lock.jsonは環境ごとのバージョンを固定するためのファイルです。
npm ciはこれをもとにしてパッケージインストールを行うので、CI/CDでデプロイを行うときにそっくりそのまま同じ環境を用意する、などの用途に向いています。
npm ciについての補足
npm ciはpackage-lock.jsonに従うという以外にも、次のような特徴があります。
- バージョン不一致の検出:
package.jsonとpackage-lock.jsonのバージョン指定に齟齬があるとき、実行を停止します -
node_modulesの削除: 既にnode_modulesが存在する場合は、削除してから再度node_modulesを作成し、インストールします -
package.jsonの不変:package.jsonを更新することはありません。
このような理由から、デプロイなど正しく確実に動く環境が欲しいときに向いています。
インストール時のバージョン指定
パッケージ名の後ろに@を付けることで、バージョンを指定できます。以下のようなイメージです。
npm install axios@1.0.0
とりあえず最新版が欲しい!という場合にはlatestを指定することもできます。
npm install axios@latest
dependencies と devDependencies
インストールしたパッケージの使用方法も2種類あります。それがdependenciesとdevDependenciesです。
| コマンド | 動作 |
|---|---|
npm install {パッケージ名} |
パッケージがdependenciesに追加される。 |
npm install {パッケージ名} --save-devor npm install {パッケージ名} -D
|
パッケージがdevDependenciesに追加される。 |
今までず〜〜〜っと説明してきた動作は、全てdependenciesに関連するものです。通常使用されるパッケージは全てdependenciesに追加されます。
ではdevDependenciesとは何か?
これは、開発時にのみ使い、リリース時には使用しないパッケージを含めておく項目のことです。
例えば、テストフレームワークのvitestはテストにのみ使うため、一般的にはデプロイ時には不要となります。(デプロイに含まれていてもすぐに何か問題を引き起こすわけではないですが、放置すると容量の圧迫等に繋がります)
よって、vitestなど本番では使わないパッケージはdevDependencies側に追加しておくのが定石とされています。
本番時にdevDependenciesのパッケージを含めないようにするには、--omit=devオプションを使います。
npm install --omit=dev
npm ci --omit=dev
--productionという--omit=devと同じ動作をするオプションもありますが、現在は--omit=devが推奨されているようです
npm run (実行コマンド系)
reactやnext.jsを使うときは特に切っても切り離せない、npm runについてもご紹介しておきましょう。
npm run
npm runは、package.jsonに定義されたコマンドを実行するためのコマンドです。
package.json内に、scriptsという項目があり、ここに様々なスクリプトが定義されているのを見たことがあるかもしれません。
{
"scripts": {
"dev": "next dev",
"build": "next build",
"test": "vitest",
"lint": "eslint ."
}
}
例えばnext devはnext.jsを起動するためのスクリプトですが、これをnpm run devコマンドで起動できるようになる、というわけです。
reactやnext.jsの場合はプロジェクト作成時に自動的にscriptsが設定されるので、あまり意識して設定することは少ないですが、実際にはここに設定されているスクリプトが動作する、ということは覚えておきましょう。
〜npm run devよりnext devの方が短くない?〜
ここまで読むと、npm run devよりnext devの方が短いので、next devを直接実行すれば良いのでは?と思う方もいるでしょう。
ただ、実際にはscripts内に定義されたコマンドを直接実行しても動かないことが多々あります。
原因としては、next devなどのコマンドは実際にはnode_modules/.binに定義されているためです。
npm runコマンドはnode_modules/.binを自動的に実行対象(PATH)に含めるため実行できますが、直接実行する場合には./node_modules/.bin/next devのような指定が必要になります。
特殊なコマンド一覧
start, stop, test, restartは、npm run startではなくnpm startのように、runを省いて実行することができます。
react/next.jsにおけるstartコマンド
そういえば、よくreactでは開発時にnpm startコマンドが実行されますが、next.jsではnpm run devを実行しますよね?この違いは何なのでしょうか。
reactでは、次のようにscriptsが設定されます。
{
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
}
}
開発サーバーの起動がstartなのに対し、reactは静的なフロントエンドのみなので本番使用時はbuildコマンドとなります。
一方、next.jsは次のような設定がなされます。
{
"scripts": {
"dev": "next dev",
"start": "next start"
}
}
next.jsの場合は開発サーバーの起動がdev、本番用サーバーの起動がstartに割り当てられています。
なので、開発中はreactはnpm start、next.jsはnpm run devなんですね。
npmとセキュリティ
パッケージは最新に保っておかないと、思わぬ脆弱性を抱えたままになり、セキュリティリスクを生むことがあります。
ここでは、パッケージの更新とセキュリティリスクの確認について学びましょう。
パッケージの更新
npm outdatedコマンドは、インストール済みのパッケージが古くなっていないかチェックすることができます。
各パッケージにつきCurrent,Wanted,Latestが表示されます。それぞれ次のような内容を示します。
| 表示 | 内容 |
|---|---|
| Current | 現在のインストール済みバージョン |
| Wanted |
package.jsonで指定された範囲の中で最新のバージョン |
| Latest | 全てのバージョンの中で最新のもの |
CurrentとWantedで乖離がある場合は、パッケージのアップデートをしたほうが良い、という判断になりますね。
パッケージアップデートにはnpm updateを使いましょう。
updateコマンドは、package.jsonの指定範囲内で最新のもの(Wanted)までバージョンを引き上げてくれます。
パッケージのセキュリティ確認
さて、もう1つ重要なコマンドがnpm auditコマンドです。
このコマンドは、インストール済みのパッケージに脆弱性が存在するか調査してくれます。
脆弱性が見つかった場合はinfo,low,moderate,high,criticalのレベルで教えてくれます。
基本的には全て対応するのが望ましいですが、どんな状況でもhigh,criticalについては最優先で対応すべきでしょう。
自動的に修正を行う場合にはnpm audit fixを使えます。このコマンドは、package.jsonで指定された範囲内で解決を試みるコマンドです。(なので実行は比較的安全)
これで解決出来ない場合は、npm audit fix --forceというオプション付きコマンドも使えます。
これは脆弱性が無くなるバージョンまで強制的に引き上げますが、バージョン指定を無視するため破壊的変更を引き起こす可能性があり、使用は要注意です。
npmパッケージの開発について
これを読んでいる人はreactやnext.jsの開発目的で読んでいる人も多いかもしれませんが、一応npmのパッケージそのものを作りたいときによく使うコマンドも紹介しておきます。
npmへのログイン
パッケージを作って公開するには、まずアカウントを作る必要があります。npmの公式ページからアカウントを作成しましょう。
その後、開発環境でnpm loginコマンドを使うことで作成したアカウントでログインできます。
基本的にはブラウザ認証となるので、アカウント作成時にログイン済みであれば、問題なくローカルでもログインできると思います。
どのアカウントでログインしているかは、npm whoamiで確認できます。
パッケージとして公開できるもの
パッケージとして公開できるのは、何もライブラリのようなものだけではありません。アプリ自体を公開することもできます。
例えば、Claude Codeはnpm経由でインストールしても使えるようになっています。
npm install -g @anthropic-ai/claude-code
参考: https://www.npmjs.com/package/@anthropic-ai/claude-code
このように、JavaScript/TypeScriptで作成したCLIツールなどを公開するのにも向いているため、興味のある方はチャレンジしてみてください。
バージョン更新
作成しているアプリやパッケージを外部に公開する場合は、バージョニングを正しく行うことも重要です。
package.jsonのversion欄を直接編集しても良いのですが、実は自動でバージョン更新ができるコマンドもあります。
それがnpm versionで、実際には以下のように使用することが多いです。
-
npm version major: メジャーバージョンを上げる (v1.1.1⇒v2.0.0) -
npm version minor: マイナーバージョンを上げる (v1.1.1⇒v1.2.0) -
npm version patch: パッチバージョンを上げる (v1.1.1⇒v1.1.2)
バージョン更新は、実際にはpackage.jsonだけではなくpackage-lock.jsonや、場合によってはその他いくつかのファイルも更新する必要があります。
更新忘れを防ぐために、npm versionを使ったほうが無難です。
npm versionはreactの開発等でももちろん問題なく使えます。
ただ、パッケージを公開する場合においては正しくバージョンを指定しないと外部公開できないこともあるため、この章で説明しました。
パッケージ公開
最後に、パッケージを公開する場合はnpm publishコマンドを使います。
このコマンドにより、別の人がnpm install 〇〇としてそのパッケージを利用可能になります。
おわりに
今回は、ちゃんと業務で使えるようになるためのnpmまとめとして、少し詳しいnpmの解説をしてみました。
複雑な依存関係を意識しなくてもパッケージを使えるのがnpmの良いところですが、それはそれとして内部で何をやっているか正しく理解しておくことは、npmの効率的かつセキュアな使用に繋がります。
この記事が、ぜひ皆さんの業務や個人開発でのnpm利用の助けになれば幸いです。
最後までお読みいただきありがとうございました。また次回の記事でお会いしましょう🙇