Node.js(Express) と MongoDB で RESTful な API サーバを作る


これは [Heroku Advent Calendar 2014](http://qiita.com/advent-calendar/2014/heroku) の２１日目(12/21)の投稿です。

最近はクライアント(ネィティブアプリ、JavaScriptベースのWEBアプリ)とサーバを RESTful な WEB API でつなぐパターンが増えてきてる気がしています。エンドユーザ向けのサービスだけでなく、管理画面やツールなどでも。

今回は [AngularJS Full-Stack（generator-angular-fullstack）](https://github.com/DaftMonk/generator-angular-fullstack) を利用して **Node.js(Express)** と **MongoDB** ベースの **RESTful な API サーバ** を作り **Heroku** へデプロイするまでやってみます。（AngularJS の環境は、今回は使いません。フロントエンドは AngularJS をそのまま使ってもいいし、それ以外にしてもOK。）

> 『AngularJS Full-Stack』には Heroku 用のコマンドが用意されています。ただ、自分で Heroku デプロイしてもたいして大変ではないのですが。

### AngularJS Fullstack について

これは所謂 **MEAN** スタック（MongoDB/Express/AngularJS/Node.js）なのですが、クリティカルでないアプリケーションをさっと作ったり、ツール類の作成、またはモック作成用にいいな、と思っています。

> 同じようなジェネレータに [MEAN.JS](http://meanjs.org/) があるのですが、AngularJS Full-Stack の方が縛りが弱く、後から手を加えやすい印象だった。また他の Express 系のジェネレータは、サーバ側で HTML を生成してレスポンスすることを想定しており、API を作るのにはそれほど適してなさそうな感じがした。

Epress ベースのもの以外では [Sales.js](http://sailsjs.org) を使えば似たようなことが出来るのかもしれませんが、これは良く分かっていない.. そのほか Node.js のフレームワークについては、ここで色々あげられています。
⇒ http://nodeframework.com

AngularJS Full-Stack の使い方については、GitHub のページに書いてあります。
⇒ https://github.com/DaftMonk/generator-angular-fullstack

`$ yo angular-fullstack:endpoint hoge` で、`hoge` モデルおよび RESTful な API 一式を生成することができます。

### 今回の前提環境

私の手元の環境は次のとおりです。

> OS：`Mac OS X 10.9.5`
> YEOMAN(yo)：`1.3.2`
> AngularJS Full-Stack(generator-angular-fullstack)：`2.0.13`
> Node.js：`0.10.32`
> MongoDB：`2.6.5`
> Heroku Toolbelt：`3.20.0`

※ それぞれを Mac に導入する手順については、ここでは割愛します。

また、今回の手順で結果的に導入された主要なモジュールのバージョンは次のとおりです。

> Express：`4.0.0`
> Mongoose：`3.8.20`
> Grunt：`0.4.5`

IDE は WebStorm を使ってます。MongoDB のプラグインもあり便利。

# 手順

## ローカル環境構築

プロジェクト用のディレクトリを適当につくり、配下でジェネレータを起動します。

```shell:
$ mkdir api_demo
$ cd api_demo
$ yo angular-fullstack
```

> ジェネレータが無い場合は `$ npm install -g generator-angular-fullstack` で導入してください。

適当に質問に答えます。

![スクリーンショット 2014-12-18 12.18.20.png](https://qiita-image-store.s3.amazonaws.com/0/35733/cf97f5b1-cd7b-8159-2ddb-7ac599c0f133.png "スクリーンショット 2014-12-18 12.18.20.png")

> Client側(AngularJS側)は利用しないので(してもいいんだけど)、何でもいいです。

![スクリーンショット 2014-12-18 12.20.11.png](https://qiita-image-store.s3.amazonaws.com/0/35733/a9df02a6-68d0-c0f2-4b1f-406bf3d627e9.png "スクリーンショット 2014-12-18 12.20.11.png")

> Server側(Node.js側)は、MongoDB の項だけ有効にします。

ジェネレータがプロジェクトを生成してくれるまでしばらく待ちます。生成が終わったら、

```shell:
$ grunt serve
```

で確認します。（MongoDB はローカルのデフォルトポートで起動しておいてください。）

![スクリーンショット 2014-12-18 12.30.22.png](https://qiita-image-store.s3.amazonaws.com/0/35733/db1c57a8-155e-e85f-1f37-2cb38e19223e.png "スクリーンショット 2014-12-18 12.30.22.png")

上記の画面は、ジェネレータ起動時に選択したオプションにより異なると思います。次に、肝心の API について動作確認してみます。デフォルトで `thing` モデルと RESTful な API 一式が生成されており、

![スクリーンショット 2014-12-18 12.38.36.png](https://qiita-image-store.s3.amazonaws.com/0/35733/a40d0be4-010f-b592-f221-6778d124c87d.png "スクリーンショット 2014-12-18 12.38.36.png")

![スクリーンショット 2014-12-18 12.48.22.png](https://qiita-image-store.s3.amazonaws.com/0/35733/c212fc30-9d47-9ed9-42f3-c4d843262470.png "スクリーンショット 2014-12-18 12.48.22.png") ![スクリーンショット 2014-12-18 12.48.09.png](https://qiita-image-store.s3.amazonaws.com/0/35733/c8460ebc-db90-fb14-cd8a-03d9a13f39e5.png "スクリーンショット 2014-12-18 12.48.09.png")


先ほどの `$ grunt serve` 時に、MongoDB へ Seed データも挿入されています。

![スクリーンショット 2014-12-18 12.37.28.png](https://qiita-image-store.s3.amazonaws.com/0/35733/073e008a-7f2b-0cfb-3a92-48eb16168013.png "スクリーンショット 2014-12-18 12.37.28.png")

一覧データを取得してみます。`$ grunt serve` のプロセスを起動したままの状態で `http://localhost:9000/api/things/` にアクセスしてみます。次のようにデータが取得されるはずです。

![スクリーンショット 2014-12-18 12.54.00.png](https://qiita-image-store.s3.amazonaws.com/0/35733/5d8832ff-5cc0-ef34-60a2-1da48ed65e82.png "スクリーンショット 2014-12-18 12.54.00.png")

## 新しくモデルと RESTful API を作る

例として、`item` モデルの RESTful な API 一式を作成します。先ほどの `thing` モデルを参考にソースを書いても良いのですが、せっかくジェネレータがあるので利用してみます。

```
$ yo angular-fullstack:endpoint item

? What will the url of your endpoint to be? (/api/items) 
```

そのまま進めます。（URLを変えたければ指定してください。）

![スクリーンショット 2014-12-18 13.01.05.png](https://qiita-image-store.s3.amazonaws.com/0/35733/559282d1-44c9-3e80-603b-748740cc924f.png "スクリーンショット 2014-12-18 13.01.05.png")

モデルのファイルを修正します。適当に `name` `price` `description` というカラムを定義しました。

```javascript:server/api/item/item.model.js
'use strict';

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var ItemSchema = new Schema({
  name: String,
  price: Number,
  description: String
});

module.exports = mongoose.model('Item', ItemSchema);
```

一応 `$ grunt serve` し直してから、`http://localhost:9000/api/items/` にPOSTしてみます。

![スクリーンショット_2014-12-18_13_11_23.png](https://qiita-image-store.s3.amazonaws.com/0/35733/b1c69669-9521-227d-bd11-e2633123621a.png "スクリーンショット_2014-12-18_13_11_23.png")

MongoDB の中身を覗くと、データが作成されています。

![スクリーンショット 2014-12-18 13.12.58.png](https://qiita-image-store.s3.amazonaws.com/0/35733/1a479423-96fd-31ac-2787-afdceaed7a5c.png "スクリーンショット 2014-12-18 13.12.58.png")

GET `http://localhost:9000/api/items/:id` で先ほどPOSTしたデータを取得してみます。

![スクリーンショット_2014-12-18_13_15_16.png](https://qiita-image-store.s3.amazonaws.com/0/35733/db4e68d5-5d28-6a50-3328-815471558a1f.png "スクリーンショット_2014-12-18_13_15_16.png")

ちょっと表示が文字化けしてますが^^; きちんと動いているようですね。提供される API 郡は次のように、Rails とかで作られるようなものと同じです。

```javascript:server/api/item/index.js
〜

router.get('/', controller.index);
router.get('/:id', controller.show);
router.post('/', controller.create);
router.put('/:id', controller.update);
router.patch('/:id', controller.update);
router.delete('/:id', controller.destroy);

〜
```

ジェネレータで生成されるもので賄えない場合は、ソースの中身は単純なので、ここからカスタマイズをすれば良いかと思います。

## Heroku へのデプロイ

MongoDB のアドオンが必要です。下記の `MongoLab` は Sandbox プランだと無料なので、これを使ってみます。（無料のアドオンでも、アドオンを追加するためにはアカウントにクレジットカードを登録しておく必要があります。）

![スクリーンショット 2014-12-18 13.31.03.png](https://qiita-image-store.s3.amazonaws.com/0/35733/224c6091-0899-a876-556a-d9f7d271d020.png "スクリーンショット 2014-12-18 13.31.03.png")

デプロイは簡単で、下記のコマンドを叩くと、

```shell:
$ yo angular-fullstack:heroku

? Name to deploy as (Leave blank for a random name):
? On which region do you want to deploy ? (Use arrow keys)
❯ US 
  EU 
```

> URLをここで決めれますが、他のユーザ含めた全 Heroku アプリケーションで一意である必要があります。指定しなければランダムで振られます。

`dist`ディレクトリにデプロイ用のファイルが展開されて、

![スクリーンショット 2014-12-18 14.02.41.png](https://qiita-image-store.s3.amazonaws.com/0/35733/35a7fe0a-23e4-d3da-a4b2-3512ddedf168.png "スクリーンショット 2014-12-18 14.02.41.png")

その中で勝手に Git 管理がはじまり `$ heroku create` 諸々のことが自動でされます。
（このプロジェクトを Git で管理している場合は、すでに `dist`フォルダはデフォルトで `.gitignore`に追記されているのですが、`dist`ディレクトリ配下は Heroku に関する Git 管理下になる感じです。）

ただこのままだと MongoDB のアドオンが無いのでエラーになります。次のように追加します。

```shell:
$ cd dist
$ heroku addons:add mongolab
```

追加したら、Heroku 側を開いてみます。

```shell:
$ cd dist
$ heroku open
```

![スクリーンショット_2014-12-18_13_49_11.png](https://qiita-image-store.s3.amazonaws.com/0/35733/031a4c6d-a968-082b-31d7-8b2354e5392e.png "スクリーンショット_2014-12-18_13_49_11.png")

もちろん API も利用できます。

![スクリーンショット_2014-12-18_13_57_39.png](https://qiita-image-store.s3.amazonaws.com/0/35733/fd25393b-f8ae-eb73-396d-c4700e7946d2.png "スクリーンショット_2014-12-18_13_57_39.png")

何かおかしければ `$ heroku ps` や `$ heroku logs`、`$ heroku config` で Heroku 上のステータスを確認してみましょう。（Heroku のダッシュボートを見てもいいけど。）

### 補足：Heroku 系のコマンドについて

`yo angular-fullstack:heroku` コマンドを使わなくても、`$ grunt build` したら `dist` ディレクトリにデプロイ用のファイルが展開されるので、`dist` ディレクトリ配下で `$ heroku create` してデプロイしても良いかと。

> Heroku 上の環境変数 `NODE_ENV` を `production` にしておく必要があります。

まあせっかくコマンドが用意されているので、それを使えばよいのですが..

### 補足：自前サーバにデプロイする場合

Node.js と MongoDB が用意されたサーバに `dist` ディレクトリ配下のファイルを置いて、`$ npm install --production`、`$ npm start` すればよいかと思います。

> こちらも自前サーバ上で環境変数 `NODE_ENV` を `production` にしておく必要があります。

## Heroku へデプロイしたアプリケーションの更新

`$ grunt build` してから `$ grunt buildcontrol:heroku` とすると、変更が Heroku へプッシュされます。

# おわりに

モック用の API サーバとかさくっと作るには、今回の構成はわりといいなーと思います。今回のソースはこちらに置きました。よろしければ参照ください。
⇒ https://github.com/hkusu/node-apiserver-sample


明日(12/22)のアドベントカレンダーは [LuckOfWise](http://qiita.com/LuckOfWise) さんの記事です。よろしくお願いします！