とほほのNode.js入門
- Node.js とは
- 歴史
- JavaScript関連技術
- インストール
- Hello world (コンソール版)
- Hello World (Web版)
- コンソールに改行無しで書き出す
- クライアントからの情報を得る
- アプリケーションフォルダを作成する
- アプリケーションフォルダにパッケージをインストールする
- GET パラメータを受け取る
- POST データを受け取る
- Express で GET POST を処理する
- Express で POST データを受け取る
- EJS テンプレートエンジンを用いる
- ファイルをノンブロッキングで読み出す
- モジュールを作成する
- 文字列中の変数を展開する
- リンク
Node.js とは
- サーバーサイドの JavaScript 実行環境。
- Google V8 JavaScript エンジンを使用しており、高速。
- Linux サーバにインストールして使用することが多いが、macOS 版や、Windows 版もある。
- npm (Node Package Manager) と呼ばれるパッケージ管理システムを同梱。
- ノンブロッキングI/O と イベントループ アーキテクチャにより、10K問題 (クライアント1万台レベルになると性能が極端に悪化する問題) に対応。
- 通信やファイルの読み書きをノンブロッキングI/Oで処理するため、スレッドが I/O 待ちになる頻度が少なく、効率的。
- クライアントからパケットを読み込む、ファイルの次のブロックを読み出すなどすべてがイベント処理で実装されている。
- 基本的にはシングルスレッドだが、内部では暗号化などの重い処理を複数スレッドで処理。
- 基本的にはシングルプロセスだが、JavaScript から fork() を呼び出すことで、マルチプロセスも可能。
- ライセンスは MITライセンス。商用利用可能。
- 長期サポートの LTS(Long Term Support)版と、非LTS版がある。
- 今のところ、v6, v8 等の偶数系は LTS 版。v7, v9 等の奇数系は非 LTS 版。
- 現在の最新版は v9 系だが、2018年中頃にはサポート終了。v8 系は、LTS 版のため、2019年12月までサポート継続。
- Node.js 上のフレームワークとして、Express, Meteor, Sails, Koa, LoobBack などがある。
- MEAN(MongoDB+Express+AngularJS+Node.js)と呼ばれる構成で利用されることも多い。
- HTTP 通信のみでなく、TCP や UDP 通信も可能。
- WebSocker サーバとして利用されることも多い。
- 比較的小規模で、高パフォーマンスを要求されるプロジェクトでの利用が多い。
- AWS Lambda, PayPal, Walmart, Uber など大量アクセス領域で利用されている。
- 非互換強化を含むバージョンアップがあり、仕様の安定期に入るには、もう少しだけかかりそう。
本書では現時点で最新の LTS 版である v8.9.4 をベースに説明します。
歴史
- 2009年 ?月、カリフォルニア州のライアン・ダール(Ryan Dahl)氏、Node.js の開発に着手。
- 2011年 8月、Dahl 氏、Node.js 初期公開バージョン 0.1.14 をリリース。
- 2012年 ?月、Joyent 社、Node.js 開発支援を開始。Dahl 氏も Joyent 社の社員に。
- 2012年 1月、BDFL(優しい終身の独裁者)の時代を終え、開発リーダが Dahl 氏から Isaac Z. Schlueter 氏に変更。
- 2014年 ?月、開発リーダが、Schlueter 氏から TJ Fontaine 氏に変更。
- 2014年 ?月、Node.js Core の開発が停滞しはじめ、一部の推進者が新団体 Node Forward を設立。
- 2015年 1月、Node Forward、 Node.js の fork 版である io.js 1.0 をリリース。
- 2015年 2月、Joyent 社、Node.js Foundation 設立をアナウンス。
- 2015年 5月、Node Forward、io.js 2.0 をリリース。
- 2015年 5月、io.js も Node.js Foundation への統合を選択。
- 2015年 7月、Joyent 社、Node.js Foundation を設立。
- 2015年 8月、Node Forward、io.js 3.0 をリリース。
- 2015年 9月、Node.js と io.js の最初の統合版 Node.js 4.0 をリリース。
- 2015年10月、Node.js 5.0 をリリース。
- 2016年 4月、Node.js 6.0 をリリース。
- 2016年 7月、韓国 Samsung 社が、Joyent 社を買収。
- 2016年10月、Node.js 7.0 をリリース。
- 2017年 5月、Node.js 8.0 をリリース。
- 2017年10月、Node.js 9.0 をリリース。
JavaScript関連技術
JavaScript/Node.js 関連技術を下記で紹介しています。
インストール
Node.js はバージョンの変動が激しく、バージョンを上げたり戻したりすることがよくあります。CentOS/RHEL 標準の yum や、Ubuntu/Debian 標準の apt-get でインストールするよりも、nvm, nodebrew, n などの、Node.js 用パッケージ管理を用いてインストールする方がおすすめです。
yum の場合 (CentOS)
CentOS のシステム領域に、特権モードでインストールします。インストールされるバージョンは少し古いものになります。
# CentOS 7 # yum -y install epel-release # yum -y install nodejs # node --version v16.18.1 # yum -y install npm 8.19.2 # Rocky Linux 8 # yum -y install nodejs # node --version v10.24.0 # npm --version 6.14.11
apt の場合 (Ubuntu)
Ubuntu のシステム領域に、特権モードでインストールします。インストールされるバージョンは少し古いものになります。
# Ubuntu 22.04
$ sudo apt update
$ sudo apt -y install nodejs
$ node --version
v12.22.9
$ sudo apt -y install npm
$ npm --version
8.5.1
nvm の場合 (CentOS/Ubuntu)
個人のホームディレクトリ配下に複数のバージョンをインストールします。環境変数で使用するバージョンを切り替えます。Ubuntu 22.04 コンテナでは curl コマンドをインストールしないとnvm ls-remote で一覧を取得できませんでした。CentOS 7 コンテナで試すと OS のライブラリが古すぎて v18.16 は動きませんでした。
# Rocky Linux 8 $ git clone https://github.com/creationix/nvm.git ~/.nvm $ source ~/.nvm/nvm.sh $ nvm ls-remote インストール可能なバージョンの一覧を表示する $ nvm install v18.16.0 最新の LTS 版をインストール $ nvm install v20.3.0 最新版をインストール $ nvm ls インストールされているバージョンの一覧を表示する $ nvm use v20.3.0 一時的に v20.3.0 を使用する(再ログイン時にはデフォルトに戻る) $ nvm alias default v20.3.0 デフォルトを v20.3.0 に切り替える
再ログイン時にも nvm を有効にするために、~/.bash_profile や ~/.bashrc に下記を追記しておきます。
if [[ -s ~/.nvm/nvm.sh ]]; then source ~/.nvm/nvm.sh fi
nodebrew の場合 (CentOS/Ubuntu)
個人のホームディレクトリ配下に複数のバージョンをインストールします。シンボリックリンクで使用するバージョンを切り替えます。
$ curl -L git.io/nodebrew | perl - setup $ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bashrc $ source ~/.bashrc $ nodebrew ls-remote インストール可能なバージョンの一覧を表示する $ nodebrew install-binary v18.16.0 v18.16.0(LTS) 版をインストール $ nodebrew install-binary v20.3.0 v20.3.0 をインストール $ nodebrew ls インストールされているバージョンの一覧を表示する $ nodebrew use v18.16.0 v18.16.0 を使用する
n の場合 (Ubuntu)
Ubuntu の場合 n を推奨するサイトが多いようです。まず、apt で nodejs と npm をインストールし、npm で n をインストールし、n で node と npm の指定バージョンをインストールし、apt でインストールした古い nodejs と npm を削除します。
$ sudo apt update 必要に応じてアップデートする $ sudo apt install -y curl curl をインストールする $ sudo apt install -y nodejs nodejs をインストールする $ sudo apt install -y npm npm をインストールする $ sudo npm install -g n npm で n をインストールする $ sudo apt purge -y nodejs npm apt でインストールした nodejs と npm をアンインストールする $ hash -r パスキャッシュをクリアする $ sudo n ls-remote 利用可能なバージョンの一覧を表示する $ sudo n 18.16.0 n で Node.js v18.16.0(LTS) をインストールする $ sudo n 20.3.0 n で Node.js v20.3.0 に切り替える $ sudo n ls インストールされているバージョンの一覧を表示する $ sudo n 18.16.0 v18.16.0 に戻す $ node --verison v18.16.0 $ npm --version 9.5.1
リポジトリを利用する場合 (Ubuntu)
PPA(Personal Package Archive) は、NodeSource が管理するモジュールをインストールします。
$ sudo apt update 必要に応じてパッケージをアップデートする $ curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash - 9.x リポジトリ情報をインストール $ sudo apt-get install -y nodejs Node.js をインストールする $ node --version v18.16.0 $ npm --version 9.5.1
Hello world (コンソール版)
まずは、おきまりの Hello world から。$ は一般ユーザの、# は特権ユーザのプロンプトを示します。
console.log("Hello world!");
$ node sample1.js Hello world!
Hello World (Web版)
Web サーバとして動作させる場合のサンプルがこちら。
var http = require('http'); var server = http.createServer(function(req, res) { res.write("Hello world!\n"); res.end(); }).listen(8080);
まず、サーバを起動する。
$ node sample2.js
別の端末から、Node.js を呼び出す。
$ curl http://127.0.0.1:8080/ Hello world!
コンソールに改行無しで書き出す
process.stdout.write("Hello");
クライアントからの情報を得る
var http = require('http'); var server = http.createServer(function(req, res) { console.log("URL: " + req.url); console.log("Method: " + req.method); console.log("Header[Content-Type]: " + req.headers['content-type']); res.end(); }).listen(8080);
アプリケーションフォルダを作成する
$ mkdir ~/myapp $ cd ~/myapp $ npm init (略) name: (myapp) [Enter] version: (1.0.0) [Enter] description: [Enter] entry point: (index.js) [Enter] test command: [Enter] git repository: [Enter] keywords: [Enter] author: [Enter] license: (ISC) [Enter] (略) Is this ok? (yes) yes $ ls -l total 4 -rw-rw-r--. 1 taro taro 201 Jan 3 22:54 package.json
アプリケーションフォルダにパッケージをインストールする
例として express パッケージをインストールします。npm 5.x 以降では --save オプションは不要です。
$ cd ~/myapp $ npm install express
GET パラメータを受け取る
GET のパラメータは req.url で受け取ります。url モジュールでパースすることもできます。
var http = require('http'); var url = require('url'); var server = http.createServer(function(req, res) { var url_parse = url.parse(req.url, true); console.log(url_parse); res.end(); }).listen(8080);
$ curl -X GET http://localhost:8080/test?name=Taro
$ node app.js Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: '?name=Taro', query: { name: 'Taro' }, pathname: '/test', path: '/test?name=Taro', href: '/test?name=Taro' }
POST データを受け取る
POST の場合、データは分割して受け取ることがあります。下記の例では、オブジェクト req に対して data 受信のイベント発生時に断片データ(chunk)を受け取り、body 変数に連結しておき、受信完了の end イベント発生時に、その内容をコンソールに書き出します。
var http = require('http'); var server = http.createServer(function(req, res) { if (req.method == 'POST') { var body = ''; req.on('data', function(chunk) { body += chunk; }); req.on('end', function() { console.log(body); res.end(); }); } }).listen(8080);
$ curl -X POST -d 'name=Taro' http://localhost:8080/test
$ node app.js name=Taro
Express で GET POST を処理する
まず、express モジュールをインストールします。
$ cd ~/myapp 作成済のアプリケーションフォルダに移動 $ npm install express Express パッケージをインストール
サーバプログラムを用意します。
var express = require('express'); var app = express(); app.listen(8080); app.get('/test1', function(req, res) { res.send('TEST1\n'); }); app.post('/test2', function(req, res) { res.send('TEST2\n'); });
クライアントから呼び出します。
$ curl -X GET http://localhost:8080/test1 TEST1 $ curl -X POST http://localhost:8080/test2 TEST2
Express で POST データを受け取る
Express 4.xで POST データを受け取る方法は下記の様にします。
const express = require('express'); const app = express(); app.use(express.urlencoded({ extended: true })); app.post('/', (req, res) => { console.log(req.body); res.send('OK\n'); }); app.listen(8080);
$ curl -X POST -d 'name=Yamada&age=26' http://localhost:8080/
express.urlencoded() の部分は express のバージョンによって色々変更があるようです。元々は bodyDecoder() でしたが bodyParser() に名称変更。Express4 では body-parser という別モジュールに切り出されたのですが、再度 express に同梱されました。
// 古い書き方(1): bodyDecoder時代 app.use(express.bodyDecoder()); // 古い書き方(2): Express3 bodyParser時代 app.use(express.bodyParser()); // 古い書き方(3): Express4前期 urlencoded + body-parser時代 var bodyParser = require('body-parser'); app.use(bodyParser.urlencoded({ extended: false }); // 新しい書き方: Express4後期 urlencoded 時代 app.use(express.urlencoded({ extended: true }));
RAW データを受け取る
raw() を用いて POST データをバイナリデータとして受け取ります。
var express = require('express'); var app = express(); app.use(express.raw({ type:'*/*' })); app.post('/', function(req, res) { console.log(req.body); res.send('OK\n'); }); app.listen(8080);
$ curl -X POST -d 'Hello' http://localhost:8080/
TEXT データを受け取る
text() を用いて POST データをテキストデータとして受け取ります。
var express = require('express'); var app = express(); app.use(express.text({ type:'*/*' })); app.post('/', function(req, res) { console.log(req.body); res.send('OK\n'); }); app.listen(8080);
$ curl -X POST -d 'Hello' http://localhost:8080/
FORM データを受け取る
urlencoded() を用いて POST された FORM データを受け取ります。デコーダとして、extended:true の場合は qs ライブラリを、extended:false の場合は querystring ライブラリを使用します。デフォルトは true です。qs ライブラリの場合は、name[1]=Taro, name[2]=Jiro などの配列も解釈してくれます。
var express = require('express'); var app = express(); app.use(express.urlencoded({extended:true})); app.post('/', function(req, res) { console.log(req.body); res.send('OK\n'); }); app.listen(8080);
$ curl -X POST -d 'name[1]=Yamada&name[2]=Taro&age=36' http://localhost:8080/
JSON データを受け取る
json() を用いて JSON データを受け取ります。
var express = require('express'); var app = express(); app.use(express.json()); app.post('/', function(req, res) { console.log(req.body); res.send('OK\n'); }); app.listen(8080);
$ curl -X POST \ -H 'Content-Type: application/json' \ -d '{"name":["Yamada","Taro"],"Age":36}' \ http://localhost:8080/
EJS テンプレートエンジンを用いる
EJS(Effective JavaScript templating) を用いて、HTML テンプレートなどに変数を埋め込むことが可能となります。まず、ejs をインストールします。
$ npm install ejs
views フォルダを作成し、その下に test.ejs ファイルを作成します。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title><%=title %></title> </head> <body> <h1><%=title %></h1> <p><%-content %></p> </body> </html>
シンプルにレンダリングするには下記の様に用います。
var fs = require('fs'); var ejs = require('ejs'); var template = fs.readFileSync('./views/test.ejs', 'utf8'); var buf = ejs.render(template, { title: "EJS Sample Code", content: "This is EJS Sample..." }); console.log(buf);
app.engine() を用いて標準レンダリングエンジンに指定することもできます。
var express = require('express'); var app = express(); var ejs = require('ejs'); app.engine('ejs', ejs.renderFile); app.get('/', function(req, res) { res.render('test.ejs', { title: "EJS Sample Code", content: "This is EJS Sample..." }); }); app.listen(8080);
テンプレートでは下記の記法を使用できます。<%= の場合は < などの文字が < に置換されます。<%- の場合は置換されません。<% スクリプトはレンダリング時にサーバー側でスクリプトを実行します。
<%=変数名(HTMLエンコードあり) %> <%-変数名(HTMLエンコードなし) %> <% スクリプト %> <%# コメント %> <%% '<%' を文字列として表示したい場合に使用 %> 通常の閉じタグ -%> 後続する改行や空白文字を削除(トリム)する
ファイルをノンブロッキングで読み出す
var fs = require("fs"); fs.readFile("./data.dat", "utf8", function(err, data) { console.log(data); });
モジュールを作成する
exports を用いて、モジュールを作成することができます。
exports.hello = function() { return "Hello!"; }
var mymod = require('./mymod'); console.log(mymod.hello());
モジュール名が / で始まる場合は絶対パス、./ で始まる場合は相対パス、その他の場合は (2)コアモジュール(module.exports._buildinLibs)、(2)module.paths 配列、(3)環境変数 NODE_PATH の順番で探索します。拡張子(.js) は省略可能です。フォルダ名を指定した場合はその配下の index.js を読み込みます。index.js のファイル名は package.json の "main" パラメータで変更可能です。
文字列中の変数を展開する
ES6(ES2015) で導入された Template Literal にも対応しており、バッククォート(`) 文字列の中で、${変数名} を展開することが可能です。
const PORT = 8080; console.log(`localhost:${PORT}`);