Node.jsのExpressで作ったサーバーをHerokuで動かす


ミニマムでNode.jsのサーバーをHerokuで動かす。

Herokuの公式 ( https://devcenter.heroku.com/articles/getting-started-with-nodejs ) は、Expressを使っているのだが、https://github.com/heroku/node-js-getting-started をCloneしているのでなんか微妙。

Generatorで作らない場合は、以下を参照すると良かった。

- Express Generatorを使わない場合
 - http://qiita.com/hoshi-takanori/items/2128a6cf1dbb533379a2
 - 特にGithubのコミットを一個ずつ見ると分かりやすい
- Expressすら使わない場合
 - http://howtonode.org/deploy-blog-to-heroku

## ソースコード

https://github.com/awakia/express-sample

2コミットで出来ました。ごめんなさい。

## 準備

### Node.jsのインストール

```bash
brew install node
```

### Expressのインストール

ExpressはNode.jsでメジャーっぽいアプリケーションフレームワーク。

Express4以降`npm install express`ではgeneratorは入らない。
`express-generator`を`npm`を使ってグローバルに入れる。

```bash
npm install express-generator -g
```

## ソースコードの準備

### ExpressテンプレートをGenerate

```bash
express express-sample
``` 

一応`git init`しておく

```bash
cd express-sample
git init
git add .
git commit -m "express express-sample"
```

### .gitignoreの設置

Node用の`.gitignore`を
https://github.com/github/gitignore/blob/master/Node.gitignore
から頂いて用意。
`.DS_Store`とかを入れない記述もついでに。

```bash
git add .gitignore
git commit -m "Add .gitignore"
```

### npm install

```json:package.json
{
  "name": "express-sample",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.12.0",
    "cookie-parser": "~1.3.4",
    "debug": "~2.1.1",
    "express": "~4.12.2",
    "jade": "~1.9.2",
    "morgan": "~1.5.1",
    "serve-favicon": "~2.2.0"
  }
}
```

の`dependencies`部分に書いているモジュールのインストール

```
npm install
```

`node_modules`ディレクトリができ、それいかに配置される。
`node_modules`は`.gitignore`に設定されているのでgitではトラックされないはず

## ローカルでサーバーの起動

```bash
bin/www
```

もしくは

```bash
npm start
```

これは`package.json`の`scripts.start`の部分にかいてあるから

http://localhost:3000

を叩く

## Herokuでサーバーの起動

```bash
heroku create
git push heroku master
heroku open
```

でサーバーが立つ。

### 何をしているか

何故、これだけで起動するか、ぱっとわからなかったので、Herokuにpushした時のログを見て解釈してみた。

Herokuアプリは大体以下の様なことができていれば動く。

1. 何のフレームワークか認識してもらい、それに対応したビルドパックで起動してもらい、必要なDependencyとかをインストールしてもらう
2. Port番号を環境変数からとるようにする
3. Procfileに起動方法を書く

このそれぞれについて、しっかり対応されているので動く。

1.は、まぁHerokuが対応しているというので出来るでしょという感じ。`package.json`の存在とか見ているのだろうか？
なんにせよ`remote: -----> Node.js app detected`となっているのでOK。

2.は、expressがgenerateした`bin/www`内で`var port = normalizePort(process.env.PORT || '3000');`となっているので大丈夫。

3.は、expressが`package.json`に`script.start`をしっかり定義しているのと、Heroku側が、Procfileがなかった時に`No Procfile; Adding 'web: npm start' to new Procfile`というように、`npm start`を勝手に足してくれる設定になっていることで実現されている。

```bash
$ git push heroku master
Counting objects: 19, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (16/16), done.
Writing objects: 100% (19/19), 3.49 KiB | 0 bytes/s, done.
Total 19 (delta 2), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Node.js app detected
remote: 
remote: -----> Reading application state
remote:        package.json...
remote:        build directory...
remote:        cache directory...
remote:        environment variables...
remote: 
remote:        Node engine:         unspecified
remote:        Npm engine:          unspecified
remote:        Start mechanism:     npm start
remote:        node_modules source: package.json
remote:        node_modules cached: false
remote: 
remote:        NPM_CONFIG_PRODUCTION=true
remote:        NODE_MODULES_CACHE=true
remote: 
remote: -----> Installing binaries
remote:        Resolving node version (latest stable) via semver.io...
remote:        Downloading and installing node 0.12.3...
remote:        Using default npm version: 2.9.1
remote: 
remote: -----> Building dependencies
remote:        Installing node modules
remote:        debug@2.1.3 node_modules/debug
remote:        └── ms@0.7.0
remote:        
remote:        cookie-parser@1.3.4 node_modules/cookie-parser
remote:        ├── cookie-signature@1.0.6
remote:        └── cookie@0.1.2
remote:        
remote:        morgan@1.5.3 node_modules/morgan
remote:        ├── basic-auth@1.0.1
remote:        ├── depd@1.0.1
remote:        ├── debug@2.2.0 (ms@0.7.1)
remote:        └── on-finished@2.2.1 (ee-first@1.1.0)
remote:        
remote:        serve-favicon@2.2.1 node_modules/serve-favicon
remote:        ├── fresh@0.2.4
remote:        ├── ms@0.7.1
remote:        ├── parseurl@1.3.0
remote:        └── etag@1.6.0 (crc@3.2.1)
remote:        
remote:        express@4.12.3 node_modules/express
remote:        ├── cookie-signature@1.0.6
remote:        ├── cookie@0.1.2
remote:        ├── fresh@0.2.4
remote:        ├── merge-descriptors@1.0.0
remote:        ├── content-type@1.0.1
remote:        ├── utils-merge@1.0.0
remote:        ├── parseurl@1.3.0
remote:        ├── methods@1.1.1
remote:        ├── escape-html@1.0.1
remote:        ├── range-parser@1.0.2
remote:        ├── finalhandler@0.3.4
remote:        ├── vary@1.0.0
remote:        ├── content-disposition@0.5.0
remote:        ├── path-to-regexp@0.1.3
remote:        ├── depd@1.0.1
remote:        ├── on-finished@2.2.1 (ee-first@1.1.0)
remote:        ├── qs@2.4.1
remote:        ├── etag@1.5.1 (crc@3.2.1)
remote:        ├── proxy-addr@1.0.8 (forwarded@0.1.0, ipaddr.js@1.0.1)
remote:        ├── send@0.12.2 (destroy@1.0.3, ms@0.7.0, mime@1.3.4)
remote:        ├── serve-static@1.9.3 (send@0.12.3)
remote:        ├── type-is@1.6.2 (media-typer@0.3.0, mime-types@2.0.11)
remote:        └── accepts@1.2.7 (negotiator@0.5.3, mime-types@2.0.11)
remote:        
remote:        body-parser@1.12.4 node_modules/body-parser
remote:        ├── content-type@1.0.1
remote:        ├── bytes@1.0.0
remote:        ├── depd@1.0.1
remote:        ├── on-finished@2.2.1 (ee-first@1.1.0)
remote:        ├── qs@2.4.2
remote:        ├── debug@2.2.0 (ms@0.7.1)
remote:        ├── raw-body@2.0.1 (bytes@2.0.1)
remote:        ├── iconv-lite@0.4.8
remote:        └── type-is@1.6.2 (media-typer@0.3.0, mime-types@2.0.11)
remote:        
remote:        jade@1.9.2 node_modules/jade
remote:        ├── character-parser@1.2.1
remote:        ├── void-elements@2.0.1
remote:        ├── commander@2.6.0
remote:        ├── mkdirp@0.5.1 (minimist@0.0.8)
remote:        ├── with@4.0.3 (acorn-globals@1.0.4, acorn@1.1.0)
remote:        ├── constantinople@3.0.1 (acorn-globals@1.0.4)
remote:        └── transformers@2.1.0 (promise@2.0.0, css@1.0.8, uglify-js@2.2.5)
remote: 
remote: -----> Checking startup method
remote:        No Procfile; Adding 'web: npm start' to new Procfile
remote: 
remote: -----> Finalizing build
remote:        Creating runtime environment
remote:        Exporting binary paths
remote:        Cleaning npm artifacts
remote:        Cleaning previous cache
remote:        Caching results for future builds
remote: 
remote: -----> Build succeeded!
remote: 
remote:        express-sample@0.0.0 /tmp/build_3fd93387a2b876ab90201bf2c61aae41
remote:        ├── body-parser@1.12.4
remote:        ├── cookie-parser@1.3.4
remote:        ├── debug@2.1.3
remote:        ├── express@4.12.3
remote:        ├── jade@1.9.2
remote:        ├── morgan@1.5.3
remote:        └── serve-favicon@2.2.1
remote:        
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote: 
remote: -----> Compressing... done, 10.4MB
remote: -----> Launching... done, v3
remote:        https://salty-river-9329.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
To https://git.heroku.com/salty-river-9329.git
 * [new branch]      master -> master
```

とても簡単に起動できた。

最近Railsやっていてもフロント周りでNode.jsの知識がちょっと必要になったり、
Native appを作れる[Electron](https://github.com/atom/electron)とかもNode.jsの知識必要だったりするから、
これを元にNode周りの技術を色々いじってみようと思う。