Node.js + Express + mysqlを使った簡単な RESTful API デモ

#背景
アルバイトでbitflyer apiを叩いてbitcoinトレードをするアプリを作ることになりました。その表現の仕方として、RESTful APIの形にすることになったので、グーグル先生に聞きながら作っていたサンプルコードをポスティングします。

本プログラムは、主に以下の文章を参照に作りました。
[RESTful API Using Node and Express 4](https://codeforgeek.com/2015/03/restful-api-node-and-express-4/)

#概略
このサンプルコードは、Node.jsとExpressを使って、mysqlとやりとりをし、その結果をjson形式で返すような簡単なREST apiです。

#具体的な内容
1).データベースの設計

データベース名: Test, table名 : users

| カーラム名 | タイプ | 
|:-----------:|:------------:|
| id       |        INT(70) |
| email       |        VARCHAR(45) |
| password       |  VARCHAR(45) |
| created_date       |       TIMESTAMP |

コマンド:

```sql: 
create database Test;
use Test;
CREATE TABLE IF NOT EXISTS `users` (
  `id` INT(70) NOT NULL AUTO_INCREMENT,
  `email` VARCHAR(45) NOT NULL,
  `password` VARCHAR(45) NULL,
  `created_date` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE INDEX `email_UNIQUE` (`email` ASC))
ENGINE = InnoDB;
```

2). api一覧

| api           | タイプ | 説明 | 
|:-------------|:------------:|------------:|
| /users/create | POST | 新しいユーザーの作成 |
| /users         | GET | ユーザーリストの取得|
| /users/:id     | GET | 特定ユーザー情報の取得 |
| /users/:password/:email  | PUT |  ユーザー情報の更新|
|/users/:email|DELETE| ユーザー情報の削除 |
|/users/validate/:id/:email|GET|ユーザー認証結果の取得|

3). プロジェックトの作成とpackage.jsonファイルの修正、インストール

プロジェックトの作成

```bash
 create RESTDemo
 cd RESTDemo
 npm init 
```

これまでのプロジックトファイル構成
RESTDemo
  |--- Server.js
  |--- REST.js
  |--- package.json
  |--- node_modules (npm installした時に、生成されたdependencyファイル)

package.jsonの修正

```json
{
  "name": "restdemo",
  "version": "0.0.1",
  "scripts": {
    "start": "node Server.js"
  },
  "dependencies": {
    "express": "~4.12.2",
    "mysql": "~2.5.5",
    "body-parser": "~1.12.0",
    "MD5": "~1.2.1", 
    "request": "~2.81.0"
  }
}
```

dependenciesのインストール

```bash
npm install
```

4). プログラムの入り口としてのServer.jsの編集

```js:Server.js
var express = require("express");
var mysql   = require("mysql");
var bodyParser  = require("body-parser");
var md5 = require('MD5');
var rest = require("./REST.js");
var app  = express();

function REST(){
    var self = this;
    self.connectMysql();
};

REST.prototype.connectMysql = function() {
    var self = this;
    var pool      =    mysql.createPool({
        connectionLimit : 100,
        host     : 'localhost',
        user     : 'db user name',
        password : 'db password',
        database : 'Test',
        debug    :  false
    });
    pool.getConnection(function(err,connection){
        if(err) {
          self.stop(err);
        } else {
          self.configureExpress(connection);
        }
    });
}

REST.prototype.configureExpress = function(connection) {
      var self = this;
      app.use(bodyParser.urlencoded({ extended: true }));
      app.use(bodyParser.json());
      var router = express.Router();
      app.use('/api', router);
      var rest_router = new rest(router,connection,md5);
      self.startServer();
}

REST.prototype.startServer = function() {
      app.listen(3000,function(){
          console.log("サーバ起動中... ポート: 3000");
      });
}

REST.prototype.stop = function(err) {
    process.exit(1);
}

new REST();
```

5). 各種リクエストをhandlerとしてのREST.jsの編集

```js:REST.js
var mysql = require("mysql");

var request = require('request');
var crypto = require('crypto');

var timestamp = Date.now().toString();

function REST_ROUTER(router,connection,md5) {
    var self = this;
    self.handleRoutes(router,connection,md5);
}

REST_ROUTER.prototype.handleRoutes= function(router,connection,md5) {
    router.get("/",function(req,res){
        res.json({"Message" : "NodeJS REST API Demo"});
    });

    //====================================
    //
    //			   Operate Users
    //
    //====================================
    //create user, POST request
    router.post("/users/create",function(req,res){
        var query = "INSERT INTO ??(??,??) VALUES (?,?)";
        var table = ["users","email","password",req.body.email,md5(req.body.password)];
        query = mysql.format(query,table);
        connection.query(query,function(err,rows){
            if(err) {
                res.json({"Error" : true, "Message" : "Error executing MySQL query"});
            } else {
                res.json({"Error" : false, "Message" : "User created !"});
            }
        });
    });

    //show user list, GET request
    router.get("/users",function(req,res){
        var query = "SELECT * FROM ??";
        var table = ["users"];
        query = mysql.format(query,table);
        connection.query(query,function(err,rows){
            if(err) {
                res.json({"Error" : true, "Message" : "Error executing MySQL query"});
            } else {
                res.json({"Error" : false, "Message" : "Success", "Users" : rows});
            }
        });
    });
    
    //show specific user, GET request
    router.get("/users/:id",function(req,res){
        var query = "SELECT * FROM ?? WHERE ??=?";
        var table = ["users","id",req.params.id];
        query = mysql.format(query,table);
        connection.query(query,function(err,rows){
            if(err) {
                res.json({"Error" : true, "Message" : "Error executing MySQL query"});
            } else {
                res.json({"Error" : false, "Message" : "Success", "Users" : rows});
            }
        });
    });
    
    //update uses, PUT request
    router.put("/users",function(req,res){
        var query = "UPDATE ?? SET ?? = ? WHERE ?? = ?";
        var table = ["users","password",md5(req.body.password),"email",req.body.email];
        query = mysql.format(query,table);
        connection.query(query,function(err,rows){
            if(err) {
                res.json({"Error" : true, "Message" : "Error executing MySQL query"});
            } else {
                res.json({"Error" : false, "Message" : "Updated the password for email "+req.body.email});
            }
        });
    });
    
    //delete users, DELETE request
    router.delete("/users/:email",function(req,res){
        var query = "DELETE from ?? WHERE ??=?";
        var table = ["users","email",req.params.email];
        query = mysql.format(query,table);
        connection.query(query,function(err,rows){
            if(err) {
                res.json({"Error" : true, "Message" : "Error executing MySQL query"});
            } else {
                res.json({"Error" : false, "Message" : "Deleted the user with email "+req.params.email});
            }
        });
    });
    
    //validate users by id and email, GET request
    router.get("/users/validate/:id/:email",function(req,res){
		var query = "SELECT * FROM ?? WHERE ??=? and ??=?";
        var table = ["users","email",req.params.email,"id", req.params.id];
        query = mysql.format(query,table);
        connection.query(query,function(err,rows){
            if(err) {
                res.json({"Error" : true, "Message" : "Error executing MySQL query!"});
            } else {
            	if (rows=="") {
                	res.json({"Error" : true, "Message" : "User not exist!"});
               	} else {
               		res.json({"Error" : true, "Message" : "OK", "token" : rows[0]["user_password"]});
                }
            }
        });
    });
}

module.exports = REST_ROUTER;
```

#実行結果の確認
API機能確認のツールとして、POSTMANというソフトがよく使われるようです。今回このツールを使って実行結果を確認します。

POSTMANの使い方は簡単なので、詳しい紹介は省略します。

今回のデモプログラムに対して、以下の図のようなpostmanのUI上、

    1. 一番目の枠でapiのrequestタイプを選択 (get, post, put, deleteなど)
    2. 二番目の枠にapiのurlを入力
    3. post　タイプの場合, 三番目の枠にパラメータのkeyとvalueを入力
    4. 四番目の枠で出力タイプを選択 (JSON, XMLなど)
    5. Sendボータンを押すと四番目の枠で結果が出力される
1~5のような流れで操作すると、結果を確認することができます。

![postman.png](https://qiita-image-store.s3.amazonaws.com/0/77744/7830d3ef-af00-26ae-cdbe-1f84376f7e56.png)

#備考

私の場合、以下のようなエラーが出ていました。

###Error:

```bash
events.js:161
      throw er; // Unhandled 'error' event
      ^

Error: listen EADDRINUSE :::3000
    at Object.exports._errnoException (util.js:1023:11)
    at exports._exceptionWithHostPort (util.js:1046:20)
    at Server._listen2 (net.js:1261:14)
    at listen (net.js:1297:10)
    at Server.listen (net.js:1375:9)
    at Function.app.listen (/Users/chi/qiita-blog/RESTDemo/node_modules/express/lib/application.js:595:24)
    at REST.startServer (/Users/chi/qiita-blog/RESTDemo/Server.js:43:11)
    at REST.configureExpress (/Users/chi/qiita-blog/RESTDemo/Server.js:39:12)
    at /Users/chi/qiita-blog/RESTDemo/Server.js:27:16
    at Handshake.onConnect [as _callback] (/Users/chi/qiita-blog/RESTDemo/node_modules/mysql/lib/Pool.js:59:7)
```
###原因: 
ポート3000がすでに使われている。
###解決策:
[node.js起動時エラー : Error: listen EADDRINUSE](http://qiita.com/kkam0907/items/65ceedbf3c71ae086838)

#コード
以下のURLからソースコードがダウンロードできます。
[デモソースコード](https://github.com/donaldchi/RESTAPIDemo)

#参考
[RESTful API Using Node and Express 4](https://codeforgeek.com/2015/03/restful-api-node-and-express-4/)
[Mac へ MySQL を Homebrew でインストールする手順](http://qiita.com/hkusu/items/cda3e8461e7a46ecf25d)
[nvm + Node.js + npmのインストール](http://qiita.com/sansaisoba/items/242a8ba95bf70ba179d3)
[Node.jsとnpmをアップデートする方法](http://parashuto.com/rriver/tools/updating-node-js-and-npm)
[[ツール] Postmanを利用したAPIの試験 [機能の紹介]](http://dev.classmethod.jp/tool/api-test-with-postman-01/)