Expressで超簡単API

# 概要
`Node.js`・`Express`・`MongoDB`を使用してAPIを作成する方法を紹介します。

[作成するAPIへのリンク](https://github.com/K-Sato1995/node_api)


# 前提条件
ここでは`Node.js`と`MongoDB`がインストールされている事を開発の前提条件とします。
インストールされていない方は下記から簡単に行えます。

- [MongoDBインストール](https://docs.mongodb.com/manual/installation/)
- [Node.jsインストール](https://nodejs.org/en/download/)

# 手順
本記事では以下の手順でAPIの開発を行います。

1. 初期設定
2. DB関連設定
3. ルート設定
4. コントローラ設定
5. APIを組み上げる
6. APIの動作確認

# 初期設定

### プロジェクトの作成
まずはAPIを保管するディレクトリ(プロジェクト)の作成を行い、移動します。

```
$ mkdir todoApp
$ cd todoApp
```

### package.jsonの作成

プロジェクト下で下記のコマンドを実行し、`package.json`を作成します。
`package.json`ではプロジェクトの情報を記載したり、プロジェクトの` dependencies`の管理を行ったりします。

```
$ npm init
```

`npm init`実行後、CLIにて下記のような質問がされます。
基本的に今は`enter`連打でデフォルトの設定で問題ありません。

```
package name: (todoAPP)
version: (1.0.0)
description: 
entry point: (index.js)
test command: 
git repository:
keywords:
author:
license: (ISC)
```

すると下記のような`package.json`が出来上がります。

```json:package.json
{
  "name": "todoapp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
```

### 必要ファイルの設定
下記のコマンドで必要なディレクトリやファイルを作成していきます。

```
$ touch server.js
$ mkdir api
$ mkdir api/controllers api/models api/routes
$ touch api/controllers/taskController.js api/models/taskModel.js api/routes/taskRoutes.js
```

ここまで行うと、下記のようなディレクトリ構成になります。
![image](https://user-images.githubusercontent.com/32632542/65142526-e02ea100-da4d-11e9-8b41-54cb3deb7ca2.png)

# DB関連設定

まずは`mongoose`という`Node.js`アプリから`MongoDB`を操作するためのライブラリをインストールします。

```
npm install mongoose --save
```

そして、`api/models/taskModel.js`を下記のように編集します。

```js:api/models/taskModel.js
var mongoose = require("mongoose");
var Schema = mongoose.Schema;

var TaskSchema = new Schema({
  name: {
    type: String,
    required: "Enter the name of the task"
  },
  Created_date: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model("Tasks", TaskSchema);
```

各データがどのような構成になるべきか(`Schema`)をここで設定しています。
コードをみてわかる通り、各`task`は`string`型の`name`と`Date`型の`Created_date`によって構成されています。

# ルート設定
ルートではクライアントのリクエストに応じてどのようにアプリケーションが反応すべきかを定義していきます。
`api/routes/taskRoute.js`を下記のように編集しましょう。

```js:api/routes/taskRoute.js
module.exports = function(app) {
  var taskList = require('../controllers/taskController');

  app.route('/tasks')
    .get(taskList.all_tasks)
    .post(taskList.create_task);


  app.route('/tasks/:taskId')
    .get(taskList.load_task)
    .put(taskList.update_task)
    .delete(taskList.delete_task);
};
```
コードからもわかる通り、`/tasks`では`GET`リクエストを受けると`api/controllers/taskController`に定義された`all_tasks`を実行し、`POST`リクエストを受けると`create_task`を実行するよう設定されています。
`api/controllers/taskController`の関数はこれから定義します。

# コントローラ設定
ルートで設定した関数を`api/controllers/taskController.js`に定義していきます。
CRUDを満たすように以下の5つの関数を定義します。各関数の役割はコード内のコメントにて記載しました。

```js:api/controllers/taskController.js
var mongoose = require("mongoose"),
  Task = mongoose.model("Tasks");

// 全てのタスクを取得する。
exports.all_tasks = function(req, res) {
  Task.find({}, function(err, task) {
    if (err) res.send(err);
    res.json(task);
  });
};

// 新しいタスクを作成する。
exports.create_task = function(req, res) {
  var new_task = new Task(req.body);
  new_task.save(function(err, task) {
    if (err) res.send(err);
    res.json(task);
  });
};

// 特定のタスクを取得する。
exports.load_task = function(req, res) {
  Task.findById(req.params.taskId, function(err, task) {
    if (err) res.send(err);
    res.json(task);
  });
};

// 特定のタスクを更新する。
exports.update_task = function(req, res) {
  Task.findOneAndUpdate(
    { _id: req.params.taskId },
    req.body,
    { new: true },
    function(err, task) {
      if (err) res.send(err);
      res.json(task);
    }
  );
};

// 特定のタスクを削除する。
exports.delete_task = function(req, res) {
  Task.remove(
    {
      _id: req.params.taskId
    },
    function(err, task) {
      if (err) res.send(err);
      res.json({ message: "Task successfully deleted" });
    }
  );
};
```
# APIを組み上げる

### 必要パッケージのインストール
まずはAPIを動かすのに必要な下記の2つの`package`をインストールします。
`express`は冒頭で記載しました通り、`Node.js`のMVCフレームワークです。
`nodemon`はファイルの変化があれば自動でアプリをリスタートしてくれる便利な`package`です。

```
$ npm install --save-dev nodemon
$ npm install express --save
```

### server.jsにてAPIの組み上げを行う。
ここまでで設定した`Model`, `Route`, `Controller`を`server.js`にて組み合わせてAPIを稼働できるようにします。
`server.js`を以下のように設定しましょう。

```js:server.js
var express = require("express"),
  app = express(),
  port = process.env.PORT || 3000,
  mongoose = require("mongoose"),
  Task = require("./api/models/taskModel"), // 作成したModelの読み込み
  bodyParser = require("body-parser");

mongoose.Promise = global.Promise;
mongoose.connect("mongodb://localhost/Tododb");

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

var routes = require("./api/routes/taskRoutes"); // Routeのインポート
routes(app); //appにRouteを設定する。

app.listen(port); // appを特定のportでlistenさせる。

console.log("todo list RESTful API server started on: " + port);
```

# APIの起動・動作確認

## APIの起動方法

まずは下記のコマンドでDBサーバを走らせます。

```
$ mongod
```

そして、ターミナル上で別のタブを開き、下記のコマンドでAPIを動かしてみましょう。

```
$ npm run start
```


## [postman](https://www.getpostman.com/)を使用しての動作確認

### POST (http://localhost:3000/tasks)
まずはデータの作成を行なってみましょう。
`Post`リクエストなのでURLを入力す左側の選択肢を`Post`に設定し、json形式のデータを渡します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258219/ffeeb62b-5848-8515-b73e-d327b9bc4016.png)

### Get(http://localhost:3000/tasks)
次にデータを取得してみましょう。
`GET`リクエストなのでURLを入力する左側の選択肢を`POST`から`GET`に変更し、実行してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258219/d6a98b79-6f24-9f57-924d-d548d7f7c1ed.png)

### GET(http://localhost:3000/tasks/:id)
今度は`id`で指定して必要なデータのみを取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258219/2ef5dae9-f699-3bc4-73a7-6f655ed768f4.png)

### PUT(http://localhost:3000/tasks/:id)
次は一度登録したデータを`Update(更新)`してみましょう。
URL左の選択肢を`PUT`に変更。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258219/2ccc576b-a04a-b40c-7652-05965bf1e725.png)

### DELETE(http://localhost:3000/tasks/:id)
最後に一度登録したデータを`Destroy(削除)`してみましょう。
URL左の選択肢を`DELETE`に変更。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258219/dded1162-9fd9-a120-83e5-623fd4b2e8ba.png)