Node.jsについて　備忘録


# Node.jsとは？

Node.jsとは、サーバアプリケーションをJavaScript言語で開発するためのプラットフォームです。
Node.jsを導入することにより、高速でスケーラブルなネットワークアプリケーションを簡単に構築することが可能になります。
例えば、イベント処理が重要で、処理が短く頻繁なチャットアプリなどでNode.jsのメリットが活かしやすいと思います。

## ノンブロッキングI/O処理方式
Node.jsはノンブロッキングI/O方式を採用しているため、軽量で、分散されたデバイスにまたがるリアルタイムアプリケーションに適しています。
ノンブロッキングI/Oとは、入出力処理を非同期で実行する方式です。たとえば、データベースにクエリを発行した際、結果が返ってくるまで待ち時間がありますが、ノンブロッキングI/O方式は問い合わせ結果を待たず次の処理に移ります。結果はイベントで受け取ります。

## モジュールとパッケージについて
Node.jsでは、JavaScriptで記述されたファイルをモジュールと呼び、それらのモジュール郡をディレクトリにまとめたものをパッケージと呼びます。オフィシャルなパッケージ以外にも、サードパーティ製のモジュールも多数用意されています。代表的なパッケージ公開サイトとしてNode Packaged Modulesがあります。

- Node Packaged Modules

公式サイトで紹介されているサードパーティー製モジュールは下記の通りです。

- パッケージ管理: npm
- HTTP ミドルウェア: Connect
- フレームワーク: Express
- ソケット: Socket.IO
- HTML パーサー: HTML5
- mDNS/Zeroconf/Bonjour
- RabbitMQ, AMQP
- mysql
- シリアライゼーション: msgpack
- Scraping: Apricot
- デバッガ: ndb is a CLI debuggerinspector is a web based tool.
- pcap binding
- ncurses
- Testing/TDD/BDD: vows,
- expresso,
- mjsunit.runner



# npm（node package manager）

Node.jsのパッケージを管理するためのツールとして、npm（node package manager）というツールが用意されています。
このツールを使って、簡単にパッケージを導入したり、更新・削除といった処理を行うことができます。

## パッケージをインストール
パッケージのインストールはinstallオプションにパッケージ名を指定します。

    npm install [-g] パッケージ名

カレントディレクトリに node_modules というフォルダが作成され、その中に指定したパッケージがインストールされます。
インストールしたパッケージを使うには、requireを使ってパッケージを読み込みます。

    var pkg = require("パッケージ名");


- -gオプション

```npm install -g パッケージ名
```

-g オプションで、すべてのプロジェクトから利用可能な領域にインストールされます。
どのプロジェクトでも共通して利用するようなパッケージで使うと良いでしょう。


## パッケージのアンインストール
uninstall オプションで、指定したパッケージがアンインストールされます。

    npm unisntall [-g] パッケージ名


## インストール済のパッケージのリストを表示
listオプションで、インストール済パッケージの一覧が表示されます。

    npm list [-g]


## パッケージ情報を表示
infoオプションで、指定したパッケージの情報が表示されます。

    npm info パッケージ名




# 人気のパッケージ

## Socket.IO

http://socket.io/


WebSocketです。チャットなどのリアルタイム通信に有用です。

    npm install socket.io

## Express
http://expressjs.com/

MVCの自動生成、ルーティング機能などを持ったフレームワークです。

    npm install express

## EJS
https://ejs.co/

Expressから利用できるテンプレートエンジンです。

    npm install ejs

## node-mysql
MySQLへ接続するためのパッケージです。

    npm install mysql

## jsdom

https://github.com/tmpvar/jsdom


DOM操作が使えるようになるパッケージです。

    npm install jsdom

## node-validator
バリデーションや文字列操作、サニタイジングを行うパッケージです。

    npm install validator




# モジュールの読み込み

Node.jsにはモジュールを読み込むための関数 require() が用意されています。
require()は引数指定されたモジュールを読み込み、そのモジュールに含まれるメソッドやクラスを格納したオブジェクトを返します。このオブジェクトを通じてモジュール内のコンテンツにアクセスできるようになります。

httpモジュールを利用するには、次のようにrequireの引数に指定し、モジュールと同名のローカル変数に代入します。

    var http = require('http');

読み込んだモジュール内の関数は.演算子で呼び出せます。

    var server = http.createServer();


## シンプルなモジュールの作成
シンプルなモジュールを作成し、呼び出すサンプルです。
モジュールmod.jsと、それを呼び出すmain.jsを作成し、それぞれに下記の内容を記述します。

```mod.js
    exports.printBoo = function(){
        return "Hello!";
    }
```

上記ではexportsにprintBoo()メソッドを追加し、エクスポートしています。
こうすることで別ファイルから該当メソッドを呼び出すことができます。


```main.js
    var mod = require('./mod.js');
     
    console.log( mod.printBoo() );
```

mod.jsモジュールを読み込み、先ほどエクスポートしたprintBoo()メソッドを実行し、それをコンソールに出力しています。
下記を実行すると、コンソールに Hello!と出力されます。

    node main.js


## コアモジュール
node.jsに標準で組み込まれているモジュールはコアモジュールと呼ばれます。

    require('モジュール名')


コアモジュール一覧

| モジュール名         | 機能                     | 安定度  |
| -------------- | ---------------------- | ---- |
| assert         | アサーション                 | 5    |
| buffer         | バイト列の格納・操作             | 3    |
| child_process  | 子プロセスの生成・管理            | 3    |
| cluster        | 複数のプロセスを使った負荷分散        | 1    |
| console        | コンソールへのメッセージ出力         | 4    |
| crypto         | 暗号化/ハッシュ               | 2    |
| dgram          | UDPを扱うソケット関連の処理        | 3    |
| dns            | DNS関連の処理               | 3    |
| domain         | 複数のIO処理間の連携            | 1    |
| events         | イベント処理を実装するための基底クラス    | 4    |
| fs             | ファイルおよびファイルシステムの操作     | 3    |
| http           | HTTPサーバー/クライアント        | 3    |
| https          | HTTPSサーバー/クライアント       | 3    |
| net            | ソケットの操作                | 3    |
| os             | OS情報の取得                | 4    |
| path           | パス文字列の処理               | 3    |
| punycode       | Punycode文字列のエンコード/デコード | 2    |
| querystring    | クエリ文字列の処理              | 3    |
| readline       | 標準入出力を使用した対話的インターフェイス  | 2    |
| repl           | REPL                   | （なし） |
| stream         | ストリーム入出力処理の基底クラス       | 2    |
| string_decoder | バイナリ列から文字列へのデコード       | 3    |
| tls            | OpenSSLを使ったTLS/SSL通信   | 3    |
| tty            | TTYの情報取得               | 2    |
| url            | URL文字列のパース・フォーマット      | 3    |
| util           | 各種ユーティリティ              | 5    |
| vm             | JavaScriptの実行エンジン      | 2    |
| zlib           | zlibを使ったデータの圧縮・展開      | 3    |







# 実装

## 最初に

適当なフォルダーを作成し、ターミナルでそこへ移動します。

![](https://paper-attachments.dropbox.com/s_11B397F511C5F405EEA7E0694F1177F705E82D1FC5CA6EFD8CDA70F30666AC48_1600318177807_image.png)


まず以下のコマンドを実行してください。

    npm init --yes

これは、npmの設定ファイルである`package.json`を生成するコマンドです。`package.json`には、npmパッケージの設定情報などが書き込まれていきます。

![](https://paper-attachments.dropbox.com/s_11B397F511C5F405EEA7E0694F1177F705E82D1FC5CA6EFD8CDA70F30666AC48_1600319051110_image.png)




## 最もシンプルなHTTPサーバを作成

ブラウザでアクセスすると「Hello World」というメッセージを表示するHTTPサーバを作成します。
まず、先に作成したフォルダ以下にコードをapp.jsとして保存します。

```app.js
    var http = require('http');
    http.createServer(function (req, res) {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end('Hello World\n');
    }).listen(8300, "127.0.0.1");
    console.log('Server running at http://127.0.0.1:8300');
```

httpモジュールをhttpオブジェクトとしてロードしています。

次にhttpモジュールのcreateServerメソッドにはHTTPリクエストを処理してレスポンスを返すHTTPサーバアプリケーションとしての処理を定義する関数を指定します。
このサンプルでは無名関数としてリクエストの内容にかかわらず，レスポンスとして，ヘッダをステータスコード200 OKでContent-Typeにtext/plainを返し，レスポンスボディとして「Hello World」を書き出しています。

createServerメソッドはHTTPサーバオブジェクトを返し，さらにそのlistenメソッドで8300番ポートとループバックアドレスにバインドしてサーバを開始します。

nodeコマンドの引数に作成したファイル名を指定して実行します。

    node app.js

ブラウザを開いて「localhost:8300」or「127.0.0.1:8300」というURLにアクセスします。 

**参考**

https://qiita.com/1ain2/items/194a9372798eaef6c5ab


このように Hello World と表示されれば成功です。

![](https://paper-attachments.dropbox.com/s_11B397F511C5F405EEA7E0694F1177F705E82D1FC5CA6EFD8CDA70F30666AC48_1600321758595_image.png)


jsファイルを変更した際にはサーバーを再起動しないと反映されないので注意。
一度サーバーを停止してから再度サーバーを起動することで再起動ができます。
ファイルの変更を反映したい場合は、`Ctrl + C`でサーバーを停止した後に、 `node app.js` でサーバーを再起動しましょう。

おそらく普段使われている言語では，WebサーバとしてTomcatやApache＋CGIといったアプリケーションを動作させるコンテナがある状態で，中身のアプリケーションだけを，サーブレットのように定まった仕様で構築することが多いでしょう。nodeの場合は，前述のサンプルのようにそれ自身でコンテナとなるWebサーバもアプリケーションも，どちらもまとめて実装することになります。




## ファイル更新時に自動でサーバーが再起動するようにしよう

ページを表示することはできましたが、jsファイルの変更を反映するには、毎回サーバーを再起動する必要があります。
手動でサーバーを再起動する必要がないように、ファイルが変更されると自動でサーバーを再起動してくれるnodemonというnpmパッケージをインストールしていきましょう。

    npm install nodemon

次に、nodemonを使ってサーバーを起動していきましょう。nodemonを実行するには、以下のコマンドを実行してください。(コピーしたコマンドはCtrl + Vもしくは Ctrl + Shift + V で貼り付けることができます。)

- Windowsの場合
```.\\node_modules\\.bin\\nodemon app.js
```

- Macの場合
```./node_modules/.bin/nodemon app.js
```

以下の図の下部のように表示されれば、nodemonでのサーバー起動は成功です。

![](https://paper-attachments.dropbox.com/s_11B397F511C5F405EEA7E0694F1177F705E82D1FC5CA6EFD8CDA70F30666AC48_1600324910916_image.png)



## nodemonを簡単に呼び出そう

サーバーを起動するために `.\\node_modules\\.bin\\nodemon app.js`と入力するのは少し面倒です。
これをより簡単に実行できるようにしていきましょう。
ここでいう「nodejs_test」に値するフォルダの中にある`package.json`を開いてください。
すると、`package.json`の中に`scripts`という項目があるのが確認できるかと思います。

![](https://paper-attachments.dropbox.com/s_11B397F511C5F405EEA7E0694F1177F705E82D1FC5CA6EFD8CDA70F30666AC48_1600325220997_image.png)


この`scripts`の中に、よくつかうコマンドをタスクとして登録することで、登録したタスクを簡単に呼び出すことができます。
それでは、実際にタスクを登録していきましょう。
ここでは、`scripts`にもともと書いてある`test`というタスクを削除し`start`というタスクを登録していきます。 `"test": 〜`の行を削除し、以下のコマンドを貼り付けてください。

- Windowsの場合
```"start": ".\\node_modules\\.bin\\nodemon app.js"
```

- Macの場合
```"start": "./node_modules/.bin/nodemon app.js"
```

![](https://paper-attachments.dropbox.com/s_11B397F511C5F405EEA7E0694F1177F705E82D1FC5CA6EFD8CDA70F30666AC48_1600325369047_image.png)


登録した`start`というタスクを実行しましょう。
`npm run タスク名` という形で実行できます。
以下のように実行し、nodemonでサーバーが起動できるかを確認してください。

    npm run start


![](https://paper-attachments.dropbox.com/s_11B397F511C5F405EEA7E0694F1177F705E82D1FC5CA6EFD8CDA70F30666AC48_1600325409125_image.png)


これで、簡単にサーバーを起動できるようになりました！







# Expressの概要

expressは、nodeを使ったWebアプリケーションを作成するためのフレームワークです。
非常にシンプルで軽量なフレームワークで、HTMLやCSSのテンプレートエンジンや、Cookie処理を行うcookieモジュール、HTTPリクエストを処理するConnectモジュールといった多彩なモジュールを必要に合わせて組み合わせることができます。


## 簡単なExpressの利用方法
Expressを使って簡単なHTTPサーバを構築します。
HTMLとCSSのテンプレートエンジンは複数用意されているので、任意のエンジンをインストールして選択することができます。
デフォルトのテンプレートエンジンはJadeですが、ここではPHPと同じように使えるejs（Embedded JavaScript）を使います。--cssオプションでlessやstylusといったCSSエンジンも指定可能です。

expressとejsをインストールします。以下のコマンドを実行してください。

    npm install express ejs

※`npm install express` `npm install ejs`と1つずつinstallコマンドを実行していくことも可能ですが、 `npm intall インストールしたいパッケージ① インストールしたいパッケージ②...` というようにパッケージ名を列挙することで、一度にまとめてインストールすることができます。よく使う便利な方法なので覚えておきましょう。



## プロジェクトの作成
express コマンドを使うことで、プロジェクトを生成できます。
express コマンドを使うために、以下を実行します。

    npm install -g express-generator

ここではテンプレートエンジンにejsを指定し、exp01というプロジェクト名を指定しています。
実行後、プロジェクト名のexp01でディレクトリが作成され、その中に必要なファイル・ディレクトリ一式が展開されます。

    express --view=ejs exp01
![](https://paper-attachments.dropbox.com/s_11B397F511C5F405EEA7E0694F1177F705E82D1FC5CA6EFD8CDA70F30666AC48_1600326817838_image.png)


作成されたディレクトリに移動し、依存関係にあるパッケージ・モジュールをインストールします。

    cd exp01
    npm install

npmのオプション一覧は下記で確認できます。

    npm help

上図の「run the app」に記載されているコマンドを実行すると、Expressのデフォルトのポート番号3000で作成したWebサーバが起動します。

    SET DEBUG=exp01:* & npm start

もしくは、

    npm start
----------
    npm run start

でも起動します。
ブラウザからは
「http://localhost:3000」
といったアドレスでアクセスできるようになります。

![](https://paper-attachments.dropbox.com/s_11B397F511C5F405EEA7E0694F1177F705E82D1FC5CA6EFD8CDA70F30666AC48_1600327206730_image.png)



## Expressプロジェクトについて注意

```app.js
    var createError = require('http-errors');
    var express = require('express');
    var path = require('path');
    var cookieParser = require('cookie-parser');
    var logger = require('morgan');
    
    var indexRouter = require('./routes/index');
    var usersRouter = require('./routes/users');
    
    var app = express();
    
    // view engine setup
    app.set('views', path.join(__dirname, 'views'));
    app.set('view engine', 'ejs');
    
    app.use(logger('dev'));
    app.use(express.json());
    app.use(express.urlencoded({ extended: false }));
    app.use(cookieParser());
    app.use(express.static(path.join(__dirname, 'public')));
    
    app.use('/', indexRouter);
    app.use('/users', usersRouter);
    
    // catch 404 and forward to error handler
    app.use(function(req, res, next) {
      next(createError(404));
    });
    
    // error handler
    app.use(function(err, req, res, next) {
      // set locals, only providing error in development
      res.locals.message = err.message;
      res.locals.error = req.app.get('env') === 'development' ? err : {};
    
      // render the error page
      res.status(err.status || 500);
      res.render('error');
    });
    
    module.exports = app;
```

 [最もシンプルなHTTPサーバを作成](#最もシンプルなhttpサーバを作成)のapp.jsでは、サーバを立てる処理（listenメソッド）を記述していましたが、上記ソースでは記述していません。そのため、

    node app.js

としてもサーバは起動しません。
サーバを立てる処理は「C:\Users\...\nodejs_test\exp01\bin\www」に記述されています。
サーバを立てる際は、「www」ファイルを実行するタスクとして、「package.json」内の「scripts」項目にて「start」が定義されているのでそれを利用します。


```package.json
    {
      "name": "exp01",
      "version": "0.0.0",
      "private": true,
      "scripts": {
        "start": "node ./bin/www"
      },
      "dependencies": {
        "cookie-parser": "~1.4.4",
        "debug": "~2.6.9",
        "ejs": "~2.6.1",
        "express": "~4.16.1",
        "http-errors": "~1.6.3",
        "morgan": "~1.9.1"
      }
    }
 ```   

- 「C:\Users...\nodejs_test\exp01\bin\www」

```js
    #!/usr/bin/env node
    
    /**
     * Module dependencies.
     */
    
    var app = require('../app');
    var debug = require('debug')('exp01:server');
    var http = require('http');
    
    /**
     * Get port from environment and store in Express.
     */
    
    var port = normalizePort(process.env.PORT || '3000');
    app.set('port', port);
    
    /**
     * Create HTTP server.
     */
    
    var server = http.createServer(app);
    
    /**
     * Listen on provided port, on all network interfaces.
     */
    
    server.listen(port);
    server.on('error', onError);
    server.on('listening', onListening);
    
    /**
     * Normalize a port into a number, string, or false.
     */
    
    function normalizePort(val) {
      var port = parseInt(val, 10);
    
      if (isNaN(port)) {
        // named pipe
        return val;
      }
    
      if (port >= 0) {
        // port number
        return port;
      }
    
      return false;
    }
    
    /**
     * Event listener for HTTP server "error" event.
     */
    
    function onError(error) {
      if (error.syscall !== 'listen') {
        throw error;
      }
    
      var bind = typeof port === 'string'
        ? 'Pipe ' + port
        : 'Port ' + port;
    
      // handle specific listen errors with friendly messages
      switch (error.code) {
        case 'EACCES':
          console.error(bind + ' requires elevated privileges');
          process.exit(1);
          break;
        case 'EADDRINUSE':
          console.error(bind + ' is already in use');
          process.exit(1);
          break;
        default:
          throw error;
      }
    }
    
    /**
     * Event listener for HTTP server "listening" event.
     */
    
    function onListening() {
      var addr = server.address();
      var bind = typeof addr === 'string'
        ? 'pipe ' + addr
        : 'port ' + addr.port;
      debug('Listening on ' + bind);
    }
```    



# 参考

https://gihyo.jp/dev/serial/01/nodejs/0001

https://rfs.jp/sb/javascript/node/01node-js.html

https://prog-8.com/docs/nodejs-new-application

https://techacademy.jp/magazine/14872