Node.js で小規模なアプリケーションを開発する方法 その4

毎週土曜日の午前は一週間分の掃除と洗濯です。今、洗濯機を回しているのですが、洗濯機による洗浄が完了する12:10を目指して記事を完成させたいと思います。

# Sequelizeのインストール
Node.jsって言ったらMongoDB! でも、向学心が無いので使い慣れたリレーショナルDBを使おうと思います。

Sequelizeをインストールするには下記のコマンドを入力します。

```
cd app
npm install --save sequelize sqlite3
npm install --save-dev sequelize-cli
```

```sequelize``` コマンドを使用してDB関連ファイルを初期化します。

```
node_modules/.bin/sequelize init
```

コマンド実行後、下記のファイルやディレクトリが生成されます。

- config/config.json
- migrations/
- models/index.js
- seeders/

ファイルの名称を変更したり、使用しないディレクトリを削除したりします。

```
mv config/config.json config/database.json
rm -r migrations/
rm -r seeders/
```

```config/database.json``` を編集します。

```json:config/database.json(変更前)
{
  "development": {
    "username": "root",
    "password": null,
    "database": "database_development",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}
```

```json:config/database.json(変更後)
{
  "development": {
    "username": "root",
    "password": null,
    "database": "database_development",
    "host": "127.0.0.1",
    "dialect": "sqlite",
    "storage": "./data/development.sqlite3"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "sqlite",
    "storage": "./data/test.sqlite3"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "sqlite",
    "storage": "./data/production.sqlite3"
  }
}

```

変更箇所は ```dialect``` を ```mysql``` から ```sqlite``` にした点と ```storage``` を追加した点です。

データベースのファイルを格納するための ```data``` ディレクトリを作成します。

```
mkdir data
touch data/.gitignore
```

```.gitignore``` は下記のようにして、ディレクトリ（と.gitignore）だけコミットするようにします。

```data/.gitignore
*
!.gitignore
```

最後に ```models/index.js``` を編集します。

```js:models/index.js(変更前)
'use strict';

var fs        = require('fs');
var path      = require('path');
var Sequelize = require('sequelize');
var basename  = path.basename(module.filename);
var env       = process.env.NODE_ENV || 'development';
var config    = require(__dirname + '/../config/config.json')[env];
var db        = {};

if (config.use_env_variable) {
  var sequelize = new Sequelize(process.env[config.use_env_variable]);
} else {
  var sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs
  .readdirSync(__dirname)
  .filter(function(file) {
    return (file.indexOf('.') !== 0) && (file !== basename);
  })
  .forEach(function(file) {
    if (file.slice(-3) !== '.js') return;
    var model = sequelize['import'](path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach(function(modelName) {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

```

```js:models/index.js(変更後)
'use strict';

var fs        = require('fs');
var path      = require('path');
var Sequelize = require('sequelize');
var basename  = path.basename(module.filename);
var env       = process.env.NODE_ENV || 'development';
var config    = require(__dirname + '/../config/database.json')[env];
var db        = {};

if (config.use_env_variable) {
  var sequelize = new Sequelize(process.env[config.use_env_variable]);
} else {
  var sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs
  .readdirSync(__dirname)
  .filter(function(file) {
    return (file.indexOf('.') !== 0) && (file !== basename);
  })
  .forEach(function(file) {
    if (file.slice(-3) !== '.js') return;
    var model = sequelize['import'](path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach(function(modelName) {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

```

変更箇所は下記の1点です。

```js:
var config    = require(__dirname + '/../config/database.json')[env];
```

以上でSequelizeのインストールと初期設定完了です。

# モデルを定義する

まずは動作確認のため1つのモデルファイルだけ作成したいと思います。下記のコマンドを入力して ```models/administrators.js``` を作成します。

```
touch models/administrator.js
```

```models/administrator.js``` の内容は下記の通りです。

```js:models/administrator.js
'use strict';

module.exports = function (sequelize, DataTypes) {
  var Administrator = sequelize.define('Administrator', {
    username: DataTypes.STRING,
    password: DataTypes.STRING,
  })

  return Administrator
}
```

# bin/sync を作成する
コマンド一発でデータベースを初期化できるスクリプトを作成します。下記のコマンドを入力して ```bin/sync``` を作成します。

```
touch bin/sync
```

```bin/sync``` の内容は下記の通りです。

```
#!/usr/bin/env node

'use strict';

var models = require('../models')

models.sequelize.sync({
  force: true,
  logging: console.log,
})
  .then(function () {
    return models.sequelize.close()
  })

```

```npm run sync``` でコマンドを呼び出すことができるように ```package.json``` を編集します。

```json:package.json(変更前)
{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node_modules/.bin/nodemon bin/www",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "nodemon": "^1.7.1",
    "sequelize": "^3.12.1",
    "sequelize-cli": "^2.0.0",
    "sqlite3": "^3.1.0"
  },
  "dependencies": {
    "express": "^4.13.3",
    "jade": "^1.11.0",
    "morgan": "^1.6.1",
    "proxy-middleware": "^0.14.0",
    "serve-static": "^1.10.0"
  }
}

```

```json:package.json(変更後)
{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node_modules/.bin/nodemon bin/www",
    "sync": "node bin/sync",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "nodemon": "^1.7.1",
    "sequelize": "^3.12.1",
    "sequelize-cli": "^2.0.0",
    "sqlite3": "^3.1.0"
  },
  "dependencies": {
    "express": "^4.13.3",
    "jade": "^1.11.0",
    "morgan": "^1.6.1",
    "proxy-middleware": "^0.14.0",
    "serve-static": "^1.10.0"
  }
}

```

下記コマンド入力で ```data``` ディレクトリに ```development.sqlite3``` が作成されます。

```
npm run sync
```

# フィクスチャの作成
開発中にテストデータがあると便利なので作成します。

```
mkdir -p test/fixture
touch test/fixture/index.js
touch test/fixture/administrator.js
```

```js:test/fixture/administrator.js
'use strict';

module.exports = function (models) {
  return models.Administrator.bulkCreate([
    {
      id: 1,
      username: 'admin',
      password: 'password',
    },
    {
      id: 2,
      username: 'loremipsumjp',
      password: 'qwerty',
    },
  ])
}

```

```js:test/fixture/index.js
'use strict';

module.exports = function (models) {
  var Promise = models.Sequelize.Promise

  return Promise.all([
    require('./administrator')(models),
  ])
}

```

```bin/sync``` を編集します。

```bin/sync(変更前)
#!/usr/bin/env node

'use strict';

var models = require('../models')

models.sequelize.sync({
  force: true,
  logging: console.log,
})
  .then(function () {
    return models.sequelize.close()
  })

```

```bin/sync(変更後)
#!/usr/bin/env node

'use strict';

var models = require('../models')
var fixture = require('../test/fixture')

models.sequelize.sync({
  force: true,
  logging: console.log,
})
  .then(function () {
    return fixture(models)
  })
  .then(function () {
    return models.sequelize.close()
  })

```

これでもう一度 ```npm run sync``` をすると、今度はテストデータ挿入のSQLが表示されます。

# おわりに
明日はサンプルアプリケーション開発に必要なモデルをどんどん追加していこうと思います。