Menu

Node.jsランタイムとは?ブラウザ外でJSを動かす仕組み

Node.jsランタイムの正体、ブラウザとの違い、そしてglobalsやmodules、process、fsといったサーバーサイドJavaScriptを支える主要APIをまとめて解説します。

このページのコードはエディタで実行できます — 編集してすぐに結果を確認できます。

Node.jsとは何か

Node.jsは、ざっくり言えば「JavaScriptファイルを実行するためにパソコンにインストールするプログラム」です。身も蓋もない説明ですが、これが一番正確だと思います。node script.js と打つと、Nodeはそのファイルを読み込み、GoogleのV8エンジン(Chromeで使われているJavaScriptエンジン)に渡して実行します。そこに、V8単体ではできないことをこなすための豊富なAPIがくっついている、というイメージです。

V8にできるのはJavaScriptを動かすことだけです。ファイルを開くことも、TCPソケットで待ち受けることも、プロセスを起動することも、環境変数を読むこともできません。そうした機能はすべてNodeがC++で実装して用意してくれていて、組み込みモジュールという形でJavaScriptから使えるようになっています。

node --version
node script.js

Node は言語ではありません。フレームワークでもありません。Node.js は ランタイムです。V8エンジン+標準ライブラリ+モジュールシステム+イベントループ、これで全部です。

はじめてのスクリプト

.js ファイルなら何でもそのまま Node プログラムとして動きます。決まり文句も main 関数も不要です。

console.log はブラウザと同じように動きます。テンプレートリテラル、Date、配列、Promise など、すでに馴染みのある言語機能はすべて V8 エンジン由来なので、挙動もまったく同じです。Node.js で変わるのは、言語そのものではなく その周りにある環境 のほうです。

Node.js 固有のグローバル変数

ブラウザには windowdocumentlocalStoragefetch などがあります。一方、Node.js ランタイムにはサーバーサイド向けに設計された、別のグローバル変数が用意されています。

  • process は実行中の Node プロセスを表すオブジェクトです。環境変数 (process.env)、コマンドライン引数 (process.argv)、プロセスを終了するメソッド (process.exit(1)) などを持っています。
  • __filename__dirname は、現在のファイルと、そのファイルが置かれているフォルダの絶対パスを返します。(ES モジュールではこれらは存在せず、代わりに import.meta.url を使います。)
  • global はトップレベルのオブジェクトで、ブラウザでいう window に相当します。

Node には documentwindow もありません。うっかり使おうとすると ReferenceError になります。「このライブラリ、ブラウザ向けに書かれていて Node ではそのままじゃ動かないな」と気づく最初のサインは、たいていこれです。

コマンドライン引数と環境変数を扱う

Node が活躍する場面 — CLI ツール、ビルドスクリプト、サーバー — の多くでは、引数や環境変数を読み取る処理が欠かせません。どちらも process 経由でアクセスします。

process.argv は配列で、最初の2要素には Node バイナリのパスとスクリプトのパスが入っているため、実際の引数はインデックス 2 から始まります。process.env は環境変数を表す単なるオブジェクトで、NODE_ENVPORT、API キーなどをここから読み込むのは定番のやり方です。

Node.js の組み込みモジュール

Node.js には標準ライブラリが同梱されていて、require(CommonJS)または import(ESM)で読み込めます。組み込みであることを明示するために、モジュール名は node: プレフィックスから始まります。

よく使うのは次のあたりです。

  • node:fs — ファイルの読み書き。async/await で書きたいときは node:fs/promises を使います。
  • node:path — ファイルパスの連結や解決、解析をクロスプラットフォームに扱えます。
  • node:http / node:https — HTTP サーバーの構築やリクエスト送信に使います。
  • node:url — URL のパースや組み立て。
  • node:os — 実行しているマシンの情報を取得できます。
  • node:crypto — ハッシュ化、乱数バイト、暗号化など。

これらはインストール不要で、Node に最初から組み込まれています。それ以外は npm から入れることになります。

Node.js のイベントループをざっくり理解する

Node は JavaScript をシングルスレッドで実行しますが、複数の処理を同時にさばけます。そのカギになるのがイベントループです。ファイル読み込み、HTTP リクエスト、タイマーといった非同期処理を呼び出すと、Node は実際の処理を OS(あるいは内部のスレッドプール)に任せて、自分はそのまま次のコードを実行し続けます。そして処理が終わるとコールバックがキューに積まれ、現在のコードを実行し終わったタイミングでループがそれを拾って実行する、という流れです。

出力される順番は 1423 です。まず同期コードが実行され、次にマイクロタスク(解決済みの Promise)、最後にタイマーという流れになります。CPU を酷使するループを書くとサーバー全体が止まってしまうのは、これが理由です。あなたのコードを動かすスレッドは 1 本しかありません。Node.js の並行処理はあくまで I/O のためのものであって、計算処理のためではないのです。

最小構成の HTTP サーバーを書いてみる

ここまで理解できれば、実用的な Web サーバーがほんの数行で書けてしまうのが Node.js の魅力です。

フレームワークも依存パッケージも不要です。createServer はリクエストごとに実行される関数を受け取り、listen がイベントループを起動して接続の処理を始めます。実際のアプリケーションでは、この上に Express や Fastify を載せて使うのが一般的ですが、その土台で動いているのは同じ組み込みの http モジュールです。

node.js とブラウザの違い

何が共通で何が違うのか、ここではっきりさせておきましょう。

両方で動くNode.js だけブラウザだけ
言語機能(クラス、Promise、async/awaitfshttpprocess__dirnamewindowdocument、DOM
console.logCommonJS の require や Node 固有の ESM の癖localStoragesessionStorage
fetch(Node 18 以降)ファイルシステムやネットワークソケットへのアクセスユーザーイベント、描画処理
setTimeoutsetInterval子プロセス、ストリームHistory API、navigator

最近の Node.js は fetchURLAbortControllerstructuredClone といったブラウザ由来の API を次々と取り込んでおり、両者の差は以前より小さくなっています。とはいえ、DOM が Node にやって来ることはありませんし、ファイルシステムがブラウザに載ることもありません。

Node vs Deno vs Bun

Node.js は事実上の標準ですが、JavaScript ランタイムは今やこれだけではありません。Deno と Bun が代表的な選択肢で、Deno は Node のオリジナル作者が手がけたもの、Bun は速度を徹底的に追求する新しいチームによるものです。どちらも JavaScript(と TypeScript をネイティブに)実行でき、テストランナーやバンドラといったツールを標準装備しています。モジュールの扱い、権限管理、パッケージのインストール方法あたりが Node との主な違いです。

ただ、JavaScript を学ぶ目的であれば、ドキュメント・チュートリアル・求人情報のいずれもまだまだ Node.js 中心です。イベントループ、モジュール、組み込み API といった考え方は、他のランタイムにほぼそのまま通用します。まずは Node から覚えて、プロジェクトで必要になったときに他のランタイムへ手を伸ばすのがおすすめです。

スクリプトをサクッと実行する

作業中にコードを実際に走らせる方法をいくつか紹介します。

# ファイルを実行
node script.js

# ワンライナーを実行
node -e "console.log(2 ** 10)"

# REPL(対話プロンプト)を開く
node

# ファイルを監視し、保存時に再実行 (Node 18.11+)
node --watch script.js

REPLは、メソッドの戻り値をちょっと確認したいときにファイルを作らずに試せる便利なスクラッチパッドです。--watch は開発中に重宝します。保存すれば、Nodeがスクリプトを自動で再実行してくれます。

次は「エラーをキャッチする」

コードを動かすのと、何かが起きたときにきちんと対処するのは別の話です。ファイルの読み込みは失敗しますし、HTTPリクエストはタイムアウトしますし、JSONのパースは例外を投げます。次章では try/catch の使い方、エラーの種類、そして壊れたときに備えるためのパターンを扱います。Nodeで動くプログラムでは、結局のところ、いつかは何かが壊れるものですから。

よくある質問

Node.jsランタイムとは何ですか?

Node.jsは、ブラウザの外でJavaScriptを実行するためのプログラムです。ChromeでJSを動かしているのと同じGoogle製のV8エンジンに、V8単体では持っていないAPI(ファイルシステム、ネットワーク、プロセス、タイマーなど)をC++で用意したレイヤーを組み合わせた構成になっています。これが揃っているおかげで、JavaScriptでサーバーやCLI、ビルドツールまで書けるというわけです。

ブラウザとNodeは何が違うのですか?

どちらもJavaScriptを動かす点は共通ですが、言語の周りにあるAPIが別物です。ブラウザにはwindowdocument、DOMがあります。一方のNodeにはprocessfshttp__dirname、そしてCommonJSやESMのモジュール読み込みの仕組みがあります。NodeにはDOMがなく、ブラウザにはファイルシステムがない — 言語は同じでも、動いている土台が違うのです。

Node.jsはフレームワークですか?それともランタイムですか?

ランタイムです。Node自身はアプリの構造を何も押し付けず、ただJavaScriptを実行してAPIを提供するだけです。ExpressやNext.js、NestJSといったフレームワークは、あくまでそのNodeの上に乗っかっています。DenoやBunはNodeと競合する別のJavaScriptランタイムですが、役割としては同じポジションです。

Nodeのイベントループとは何ですか?

イベントループは、Nodeがシングルスレッドで多くの処理を同時にさばくための仕組みです。ファイル読み込みやHTTPリクエスト、setTimeoutのような非同期処理を呼ぶと、Nodeはその仕事をOS側に渡してしまい、自分は次の処理に進みます。仕事が終わるとコールバックがキューに積まれ、イベントループがそれを拾って実行する、という流れです。fs.readFileがプログラム全体を止めないのは、まさにこの仕組みのおかげです。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める