ExpressとMongoDBで手っ取り早くREST APIを作る

# はじめに

自分の覚書としてこの記事を書きます。
ExpressやMongoDBをインストールするところから、レシピを作成・取得などできるシンプルなWeb APIを作りました。執筆時点でWebページは作っていません。
ここではExpress Application Generatorを使ったのですが、Web APIだけを作りたいのなら自分の手で一から作っても良いかもしれません。

## 完成版ソースコード

* [recipes(v1.0.0)](https://github.com/okunoyuki/recipes/tree/v1.0.0)

## 参考

以下を大変参考にさせていただきました。

* [Node.js+Express+MongoDBでREST APIをつくる](https://qiita.com/itagakishintaro/items/a1519998a91061cbfb1e)
* [MongoDB 3.0.6（Windows版）をインストールして起動するまでの手順](https://qiita.com/moto_pipedo/items/c3bb40370ba2792143ad)
* [Express Todo Example](https://github.com/dreamerslab/express-todo-example)

# 本題

## Expressアプリの骨組みを作る

##### [Express Application Generator](http://expressjs.com/en/starter/generator.html)をインストール

```shell-session
$ npm install express-generator -g
```

##### Express Application Generatorで骨組みを作る

```shell-session
$ cd <適当なディレクトリ>
$ express --view=pug <アプリの名前>
$ cd <アプリの名前>
$ npm install && npm start
```

##### ブラウザで確認

[http://localhost:3000](http://localhost:3000)や [http://localhost:3000/users](http://localhost:3000/users)にシンプルな画面ができています。

##### ソースコードを解釈→WebAPIに変更

[http://localhost:3000](http://localhost:3000)表示には以下のコード(抜粋)が関係していそうだとわかります。

```js:app.js
var app = express();

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

var index = require('./routes/index');

app.use('/', index);
```

```js:route/index.js
var router = express.Router();

router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router
```

```jade:views/index.pug
extends layout

block content
  h1= title
  p Welcome to #{title}
```

[`app.use`](http://expressjs.com/en/4x/api.html#app.use)がルートパスと `router` を紐づけ
→ [`router.get`](http://expressjs.com/en/4x/api.html#router)でルートパスへのGETリクエストに対する挙動を指定
→ [`res.render`](http://expressjs.com/en/4x/api.html#res.render)でviewとその中で使用できるローカル変数を指定
→ `views/index.pug` を表示
となっているようです。

`res.render` ではなく、以下のように[`res.json`](http://expressjs.com/en/4x/api.html#res.json)を使うと固定のJSONを返すWeb APIに変化します！

```js:routes/index.js
var router = express.Router();

router.get('/', function(req, res, next) {
  res.json({
    recipes: [
      {title: 'nikujaga', content: 'niku + jaga'},
      {title: 'curry', content: 'veggie + curry cube'}
    ]
  });
});

module.exports = router
```

## MongoDBを使ってみる

##### [MongoDB Community Server](https://www.mongodb.com/download-center#community)をダウンロード＆インストール

インストールが完了したら、PATHに `C:\Program Files\MongoDB\Server\3.0\bin` を追加します (バージョンが異なる可能性があるので注意)。

##### コマンドが使えることを確認

```shell-session
$ mongod --version
$ mongo --version
```

* `mongod` ：MongoDBサーバ起動用コマンド
*  `mongo` ：MongoDBシェル起動用のコマンド

##### MongoDBサーバの起動設定（参考：[Configuration File Options](https://docs.mongodb.com/manual/reference/configuration-options/)）

MongoDBサーバの起動にはDBのデータやログの格納場所を指定する必要があります。
今回は以下のように設定ファイルに設定してみます。（※パスは適宜変更してください）

```conf:db/mongodb.conf
storage:
  dbPath: db\data

systemLog:
  destination: file
  path: db\logs\mongodb.log

```

* `storage.dbPath` : DBのデータを格納するためのディレクトリ
* `systemLog.destination` : ログを格納する場所として、 `file` または `syslog` を指定
* `systemLog.path` : （上記で `file` を設定した場合）ログを格納するファイル名

##### MongoDBサーバを起動（参考：[Start mongod Processes](https://docs.mongodb.com/manual/tutorial/manage-mongodb-processes/#start-mongod-processes)）

```shell-session
$ mongod --config db/mongodb.conf
```

##### MongoDBシェルを起動して触ってみる（参考：[mongo Shell Quick Reference](https://docs.mongodb.com/manual/reference/mongo-shell/)）

```shell-session
$ mongo
```

`quit()` でMongoDBシェルを終了できます。

##### MongoDBサーバを停止

以下はMongoDBシェルからサーバを停止する方法です。停止方法は他にもあります。([Stop mongod Processes](https://docs.mongodb.com/manual/tutorial/manage-mongodb-processes/#stop-mongod-processes))

```shell-session
$ mongo
$ use admin
$ db.shutdownServer()
```

## Node.jsからMongoDBを使う

##### [mongoose](https://github.com/Automattic/mongoose)をインストール

MongoDBのモデル作成などが簡単にできるツールです。

```shell-session
$ npm install mongoose --save
```

##### mongooseでMongoDBに接続＆モデル作成

DBのデータの取得・作成するためには、スキーマを定義する必要があります。
全体に関する詳しい説明は[README](https://github.com/Automattic/mongoose#overview)を、スキーマの詳細な定義方法は[SchemaTypes](http://mongoosejs.com/docs/schematypes.html)参考にしてください。

```js:db/mongo.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var Recipe = new Schema({
    title: String,
    content: String,
    created_at: {
      type: Date,
      default: Date.now
    },
    last_modified_at: {
      type: Date,
      default: Date.now
    }
});

mongoose.model('Recipe', Recipe);
mongoose.connect('mongodb://localhost:27017/recipes');
```
app.jsの先頭に `require('./db/mongo')` を追加することで、アプリ起動時にMongoDBに接続するようにします。

##### mongooseでデータ作成＆取得

先ほど定義したRecipeスキーマのインスタンスを作成して、作成と検索ができます。
[`save`](http://mongoosejs.com/docs/api.html#document_function%20Object()%20%7B%20%5Bnative%20code%5D%20%7D-save)や[`find`](http://mongoosejs.com/docs/api.html#query_Query-find)を使用ます。

```js
var db = require('mongoose');
var Recipe = db.model('Recipe');

new Recipe({
      title: 'jaga bata',
      content: 'potato + butter'
}).save(function (err, recipe){
  if(err) {
    console.log(err);
  } else {
    console.log('yay');
    console.log(recipe);
  }
});

Recipe.find()
  .exec()
  .then(function(recipes){
    console.log('wow');
    console.log(recipes);
  })
  .catch(function(err) {
    console.log(err);
  });
```

## ExpressとMongoDBを組み合わせる

ExpressでGETリクエストやPOSTリクエストを作って上記のコードと組み合わせることで、動的なWebAPIを作ることができるようになりました。例えば、前述のルートパスへのGETリクエストの実装は以下のようになります。

```js:routes/index.js
var router = express.Router();

router.get('/', function(req, res, next) {
  Recipe.find()
    .exec()
    .then(function(recipes){
      res.json({recipes: recipes});
    })
    .catch(function(err) {
      res.status(500).json({err: err});
    });
});

module.exports = router
```

# 終わりに

最後まで読んでくださってありがとうございました！
ソースコードは少しずつ改善させていく予定です。