Node.JSのSequelize ORM入門

開発者はもう観察しているように、バックエンドウェブ開発のためのExpress.JSというフレームワークが少しずつ人気になっています。それに加えて、Express.JSはMongoose ORMと共に、一般的にREST APIの作成に使います。しかし、MySQL、PostgreSQL、SQLiteなどのような他のDatabase Schemaを使いたい開発者の場合に、どうするのか？SQLに関するデータベース言語のための探せるORMがどこかにありますか？ありがたいことに、そんなことがあります。こういうことはSequelizeというORMです。前の書いた記事とは違って、この記事にExpress.JSにデータベースを統合するという話が書いてあります。これから、Express.JSをSequelizeで使用して開始する方法を紹介します。

こんにちは！マルクス（Twitter Account: [@markusveeyola](http://twitter.com/markusveeyola)）と申します。現在、奥多摩日本語学校の学生で、同時に、IT開発者としてアルバイトをしています。宜しくお願い致します！

それでは、始めましょうか。

![1__SmZzhgGpZ5tHeUM8toUWQ.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300940/5b9f9bd2-02e4-73c9-b8a9-ed77d2f48bdd.jpeg)

##ORMとは
Object Relation Mappingというモデルはオブジェクトとリレーショナルデータベースの接続を手順としています。手動でSQLステートメントを書く必要性を排除するために使用されますが、むしろ、オブジェクトを通して、はるかに速くそしてより読みやすいQuery操作をすることができます。


##Sequelizeとは
Sequelizeは、Node.js用のpromised-basedのORMです。それは習得が容易で、**Synchronization**、**Association**のような機能を持っています。Sequelizeで PostgreSQL、MySQL、MariaDB、SQLite、MSSQLはサポートされています。

## 前提条件
後は、申請をするとき、我々の手順が正しく機能するようにするために、これらは必要な条件です。

- Node.jsとNPMはインストールした必要があります。
- Node Package Manager「NPM」の基本的な知識
- Node.jsの基本的な知識
- Postman: HTTP Requestをテストするものを責任としています。
- 設定したSQLデータベース

##開発時間
プロジェクトを初期化するために、先ずはアプリを作りたい場所のフォルダに行ってください。そのフォルダーから、ターミナルに行って、「**mkdir**」コマンドラインを使って、プロジェクトの名前として「**SequelizeCRUD**」に付けて、入力してください。新しいフォルダーは作られるはずです。その後はそのフォルダーに行くため、「**cd**」コマンドラインと「**SequelizeCRUD**」を付けて、入力してください。

```console
例: 
D:\Programming\Testing\> mkdir SequelizeCRUD
D:\Programming\Testing\> cd SequelizeCRUD
D:\Programming\Testing\SequelizeCRUD\>
```
「**SequelizeCRUD**」フォルダーはもう作られたら、とうとうプロジェクトを始めるために、ターミナルからこのNPMのコードを入力してください。普通に、各々のコマンドライン実行の中には時間がかかるかもしれないので、しばらくお待ちください。

```console
(ターミナルから作ったフォルダに行く必要です。)
npm init -y
npm install -g nodemon sequelize-cli
npm install -s express mysql2 sequelize body-parser
```

それでは、何が起こったんでしょうか。上のコードを見られるように、NPMの「**init**」を使って、プロジェクトを初期化するようになりました。初期化できた後、NPMの「**install**」でグローバルライブラリパッケージとして**sequelize-cli**と**nodemon**をインストールするようになりました。**sequelize-cli**の目的は、アプリにORMコマンドを使用させるためです。次のインストールしたライブラリはREST APIアプリにデータベースを統合するものを目的としています。

必要な**NPM**パッケージはもうインストールされたので、今回は操作のコードを書き始めます!

最初は好んだコードエディタを開けてください。私にはVisual Studio Codeを使っています。エディタからSequelizeCRUDのフォルダーを開けてください。生成した「node_modules、package.json、package-lock.json」ファイル以外に、下のストラクチャーを基に**他のフォルダ**と**app.js**を作ってください。

```console
SequelizeCRUD
    > config/ 
    > controllers/ 
    > migrations/ 
    > models/ 
    > routes/ 
    > app.js 
    > node_modules/
    > package.json
    > package-lock.json
```
今回は、コードを書く時間です！「**app.js**」ファイルに行ってください。後で、下のコードを全て入れてください。

```javascript
const express = require('express');
const bodyParser = require('body-parser');
const http = require('http');
const app = express();
const port = 8000;

app.set('port', port);
app.use(bodyParser.json());

const server = http.createServer(app);
server.listen(port);
module.exports = app;
```

このファイルはウェブサービスを作るために、全てを縛るメインファイルです。だから、折々このファイルに戻ります。コードに見られたように、サーバーを設定するためのブラリとして、**express**と**body-parser**と**http**がインポートされました。そして、**PORT**としては**8000**を使って、サーバーが確立されます。

次に、**config**というフォルダに行って、**config.json**を名前としてファイルを作ってください。また、提供したコードを入ってください。

```json
{
  "development": {
    "username": "root",
    "password": "mysql",
    "database": "sequelize", 
    "host": "192.168.0.112",
    "dialect": "mysql"
  }
}
```
このファイルはデータベースの接続を設定するものを目的としています。あなたの好んだSQLに応じて、**dialect**というプロパティの価値になります。SQLデータベースに対応する必要なプロパティを変更してください。

さて、データベースのモデルを作り始める前に、モデルのフォルダに**index.js**を作るべきです。次の見せるコードをいれてください。

```javascript
'use strict';

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

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

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

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

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

module.exports = db;
```
なぜ、こういうコードが必要なのか？理由は全てのモデルファイルを一つずつインポートするというより、むしろこのファイルがインポートの手順を自動化します。それに加えて、**Sequelize ORM**の接続をインスタンスするために、このところに**config.json**ファイルから、先の設定した環境を使います。このため、SequelizeでデータベースのQueryを簡単に使えるようになります。

後は、これからモデルの作成に進めます。**Employee**と**Company**のモデルを作ります。この2つの間にそれぞれの**Employee**が1つの**Company**で働いてて、逆でそれぞれの**Company**がたくさん**Employee**を持てるという関係になります。だから、後に、Sequelizeで**belongsTo**が`One-is-to-One`を表し、**hasMany**が`One-is-to-Many`を表すという**assocation**を使うと、テーブル関係を確立できるようになります。

ターミナルから、モデルを作るために、下の部分に書いてあるように、こうやって入力する：

```console
// Employee
sequelize model:generate --name Employee --attributes name:string,salary:integer
// Company
sequelize model:generate --name Company --attributes name:string

「 sequelize-cliというライブラリはもうインストールされたから、こういうことができます。」
```

上のコマンドを正しく入力すれば、4つのファイルが生成されるはずです。このファイルは2つに分けられて、**Migrations**と**Models**を一部としています。


## Migrations: Company & Employee
```javascript
'use strict';
module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.createTable('Companies', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      name: {
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  down: (queryInterface, Sequelize) => {
    return queryInterface.dropTable('Companies');
  }
};
```
**Company**の**Migration**ファイルが見られるように、**id**と**createdAt**と**updatedAt**と共に、**name**フィールドがすでに設定されています。


 一方で**Employee**というMigrationには**name**と**salary**の両方フィールドがあります。しかし、**Employee**は**Company**次第なので、手動で、**FK**フィールドは**companyId**を名前として、追加するべきです。参照として、すでに下のコードでどのように書かれているかを見るはずです。

```javascript
'use strict';
module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.createTable('Employees', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      name: {
        type: Sequelize.STRING
      },
      salary: {
        type: Sequelize.INTEGER
      },
      companyId: {
        type: Sequelize.INTEGER,
        onDelete: 'CASCADE',
        references: {
          model: 'Companies',
          key: 'id',
        }
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  down: (queryInterface, Sequelize) => {
    return queryInterface.dropTable('Employees');
  }
};
```

今回は、データーベースの**Assocation**を設定するために、モデルに行きましょう。**Association**という**Sequelize**特徴は**Query**を簡単に作る上で、使う必要なのです。なぜなら、こういう特徴を使って、**FK**でお互いに接続されているテーブルに**Query**を直接に作れます。

## Models: Company & Employee
**Company**というモデルは`One-is-to-Many`の**Cardinality**を持っているので、次に示すコードのように**HasMany**という**Association**が使用されます。

```javascript
'use strict';
module.exports = (sequelize, DataTypes) => {
  const Company = sequelize.define('Company', {
    name: DataTypes.STRING
  }, {});
  Company.associate = function(models) {
    // associations can be defined here
    Company.hasMany(models.Employee, {
      foreignKey: 'companyId',
      as: 'employees',
    })
  };
  return Company;
};
```
次に、Employeeの場合に、One-is-to-ManyのCardinalityを持ってて、Company次第なので、BelongsToというAssocationを使います。

```javascript
'use strict';
module.exports = (sequelize, DataTypes) => {
  const Employee = sequelize.define('Employee', {
    name: DataTypes.STRING,
    salary: DataTypes.INTEGER
  }, {});
  Employee.associate = function(models) {
    // associations can be defined here
    Employee.belongsTo(models.Company, {
      foreignKey: 'companyId',
      onDelete: 'CASCADE',
    })
  };
  return Employee;
};
```
## Model Synchronization

今、**Association**をちゃんと作動してもらうものを目的として、モデルのメインファイルにシンクロナイズします。そうするために、**app.js**に戻って、下の書いてあるコードと同じように、そういうコードをいれてください。**Employee**モデルのウェブメソッドだけを作るので、**Company**のデーターを直接にイニシャライズします。

このコードに表示しているように、モデル変数として**index.js**をインポートしました。そういう変数を使って**Company**のモデルを使えます。

```javascript

const express = require('express');
const bodyParser = require('body-parser');
const http = require('http');
const models = require('./models/index.js');
const Company = models.Company;
const app = express();
const port = 8000;

app.set('port', port);
app.use(bodyParser.json());

models.sequelize.sync().then(() => {
    initial();
    console.log('Seems like the backend is running fine...');
}).catch((err) => {
    console.log(err, 'Something went wrong with the operation');
});

require('./routes/employee.route')(app);

function initial(){
    Company.create({
        name: "ABC Company"
    });
    Company.create({
        name: "DEF Company"
    });
    Company.create({
        name: "GHI Company"
    });
}

const server = http.createServer(app);
server.listen(port);
module.exports = app;
```

この後は、ターミナルに、設定したモデルをテーブルにするために、このようなコードを入力してください。

```console
sequelize db:migrate
```
## Controller: Employee

```javascript
const db = require('../models/index.js');
const Employee = db.Employee;
const Company = db.Company;

module.exports = {
  create(req, res) {  
    Employee.create({  
      name: req.body.name,
      salary: req.body.salary,
      companyId: req.body.companyId, 
    }).then(employee => {    
      res.send(employee);
    });
  },
  findAll(req, res) {
    Employee.findAll({
      include: [{ model: Company }]
    }).then(companies => {
      res.send(companies);
    });
  },
  findOne(req, res) {  
    Employee.findOne({
      where: {
        id: req.params.id
      },
      include: [{ model: Company }]
    })
    .then(company => {
      res.send(company);
    })
  },
  update(req, res) {
    Employee.update(
      { 
        name: req.body.name,
        salary: req.body.salary
      }, {
        where: { id: req.params.id } 
      }
    ).then(() => {
        res.status(200).send("Successfully updated a Employee ID: " + req.params.id);
    });
  },
  delete(req, res) {
    Employee.destroy({
      where: { id: req.params.id }
    }).then(() => {
      res.status(200).send("Successfully deleted a Employee ID: " + req.params.id);
    });
  }
}
```
上に見せられるように、データベースに**Employee**と**Company**のテーブルが生成されたので、**Controller**を作る時間になりました。**Controller**フォルダに行って、**employee.controller.js**を名前として、新しいファイルを作ります。このところにウェブメソッドを作っていきます。**Sequelize**接続をアクセスするために、モデルフォルダの**index.js**ファイルをインポートして、**Employee**と**Company**のモデルを変数に入ります。

SequelizeはORMなので、手動で**Query**を作らなくていいです。`create`、`findAll`、`findOne`、`update`、`destroy`のような定義済み関数を使うだけです。また、一目瞭然の関数名前なので、使い方はすぐ分かります。Sequelizeの**Query**が**promise-based**の関数だから、`callback`と`error`は管理しやすいです。

このコードのハイライトはその`findOne`と`findAll`というメソッドです。なぜなら、Sequelizeの**Association**で、**Employee**のデーターをQueryでもらうと、`include`のタッグを使って**Company**のデーターも直接にもらえます。とても便利ですよね！

## Route: Employee
自分でこのアプリをテストする前に、**Route**フォルダに行って、最後の新しいファイルを作って、**employee.route.js**を名前としていくべきです。

```javascript
module.exports = function(app) {
    
    const employeesController = require('../controllers/employee.controller');
    const employeesAPI = '/api/employees'

    // Create a new Employee
    app.post(employeesAPI, employeesController.create);
 
    // Retrieve all Employees
    app.get(employeesAPI, employeesController.findAll);
 
    // Retrieve a Employee by ID
    app.get(employeesAPI + '/:id', employeesController.findOne);
 
    // Update a Employee by ID
    app.patch(employeesAPI + '/:id', employeesController.update);
 
    // Delete a Employee by ID
    app.delete(employeesAPI + '/:id', employeesController.delete);
};
```
コードに見られるように、先の作った**controller**をインポートして、**controller**のエンドポイントを変数に保存しました。そして、それぞれの作ったメソッドに**route**がセットされました。

最終的に、**app.js**ファイルに戻って、その作った**route**をインポートして、とうとうテストがもうできます。そうするために、ターミナルに行って、次に見せられるように、このコマンドように書きます：

```console
nodemon app.js
```
もう今、**Postman**の**HTTP Client**で、テストできますよ！
##終わり
全部の手順を正確にすれば、作動している**Express**と**Sequelize**を統合された**Node.js REST API**のアプリをもう書くようにできました！やったね！日本語勉強中なので、この記事は内容が分かりにくいかもしれなくても、読んで頑張ってありがとうございました！　

じゃあまたね！