Node.js/ExpressアプリケーションからRDBへ接続してみる。
使用するツール・ライブラリは以下。
以下の記事を読んだ前提で書く。
環境設定
先の記事で紹介したプロジェクトにて以下を実行する。
$ yarn add sequelize sqlite3 morgan
$ mkdir app/db
.gitignore へ以下を追加。
node_modules
app/db/*.sqlite
.DS_Store
./**/.DS_Store
実装
- app/repositories/sequelize.js- Sequelizeを使ったRDB接続用モジュール
 
- app/repositories/models.js- Clientモデルを作成
- テーブルの作成とサンプルデータのインサート
 
- app/controllers/get_client- 上記の models.js経由でClientの検索
 
- 上記の 
app/repositories/sequelize.js
const Sequelize = require('sequelize');
const path = require('path');
const logger = require('morgan');
const sequelize = new Sequelize('SampleDB', '', '', {
  host: 'localhost',
  dialect: 'sqlite',
  pool: {
    max: 5,
    min: 0,
    idle: 10000
  },
  // SQLite only
  storage: path.join(__dirname, '../db/database.sqlite'),
  logging: logger.info // ログをロガーに流す
});
// Test the connection
sequelize
  .authenticate()
  .then(() => {
    console.log('Connection has been established successfully.');
  })
  .catch(err => {
    console.error('Unable to connect to the database:', err);
  });
module.exports = sequelize;
app/repositories/models.js
const Sequelize = require('sequelize');
const sequelize = require('./sequelize');
const Client = sequelize.define('client', {
  firstName: {
    type: Sequelize.STRING
  },
  lastName: {
    type: Sequelize.STRING
  }
});
// force: true will drop the table if it already exists
Client.sync({force: true}).then(() => {
  // Table created
  return Client.create({
    firstName: 'Hoge',
    lastName: 'Fuge'
  });
});
module.exports = {
  Client: Client
};
app/controllers/get_clients.js
const models = require('../repositories/models');
const Client = models.Client;
const get_clients = (req, res, next) => {
  Client.findAll().then(clients => {
    res.send(clients);
  });
};
module.exports = get_clients;
実行
以下で起動。
$ node app/app.js
http://localhost:3000 にブラウザでアクセスして Client 情報が見れたら成功。
Association
ちょっとハマったので、ここではモデル同士のリレーション・関連付けについて記載する。
リレーションには以下がある。
- hasOne
- 1:1 のリレーション
 
- belongsTo
- 1:N のリレーション
- hasOneや- hasManyされる側
 
- hasMany
- 1:N のリレーション
 
- belongsToMany
- N:M のリレーション
 
ここでは belongsToMany の例のみ記載する。
参考/チュートリアル、参考/リファレンス
実装
- app/models/user.js
- ユーザテーブルのモデル
 
- app/models/team.js
- チームテーブルのモデル
 
- app/models/team-user.js
- チームテーブルとユーザテーブルの N:M のリレーションを構築する中間テーブルのモデル
 
- app/repositories/sequelize.js
- Sequelize のロード・設定を行う
- Sqlite3 を使用した例になっている
 
- app/repositories/db.js
- モデルのリレーション作成、DBとの同期を行う
 
- app/app.js
- 適当な実行ファイル、全く参考にならない
- node app/app.jsで実行
 
個人的な好みだが、モデルは 単数形 で作成する。
また、 モデルのリレーションを実装してから sync() する のがポイント。(ここでハマった)
app/models/user.js
const Sequelize = require('sequelize');
const sequelize = require('../repositories/sequelize');
const User = sequelize.define('user',
  {
    user_id: {
      type: Sequelize.STRING,
      primaryKey: true,
      allowNull: false,
      autoIncrement: false,
      unique: true
    },
    user_name: {
      type: Sequelize.STRING
    }
  },
  {
    timestamps: false,
    tableName: 'user'
  }
);
module.exports = User;
user_id が主キー。
app/models/team.js
const Sequelize = require('sequelize');
const sequelize = require('../repositories/sequelize');
const Team = sequelize.define('team',
  {
    team_id: {
      type: Sequelize.STRING,
      primaryKey: true,
      allowNull: false,
      autoIncrement: false,
      unique: true
    },
    team_name: {
      type: Sequelize.STRING
    }
  },
  {
    timestamps: false,
    tableName: 'team'
  }
);
module.exports = Team;
team_id が主キー。
app/models/team-user.js
const Sequelize = require('sequelize');
const sequelize = require('../repositories/sequelize');
const TeamUser = sequelize.define('team_user',
  {
    team_id: {
      type: Sequelize.STRING,
      primaryKey: true,
      allowNull: false,
      unique: false,
      references: {
        model: "team",
        key: "team_id"
      }
    },
    user_id: {
      type: Sequelize.STRING,
      primaryKey: true,
      allowNull: false,
      unique: false,
      references: {
        model: "user",
        key: "user_id"
      }
    }
  },
  {
    timestamps: false,
    tableName: 'team_user'
  }
);
module.exports = TeamUser;
user_id 、 team_id が複合キーで、それぞれ外部キーを参照しているのがポイント。
app/repositories/sequelize.js
const Sequelize = require('sequelize');
const path = require('path');
const logger = require('morgan');
const sequelize = new Sequelize('SampleDB', '', '', {
  host: 'localhost',
  dialect: 'sqlite',
  pool: {
    max: 5,
    min: 0,
    idle: 10000
  },
  // SQLite only
  storage: path.join(__dirname, '../db/database.sqlite'),
  logging: logger.info // ログをロガーに流す
});
// Test the connection
sequelize
  .authenticate()
  .then(() => {
    console.log('Connection has been established successfully.');
  })
  .catch(err => {
    console.error('Unable to connect to the database:', err);
  });
module.exports = sequelize;
app/repositories/db.js
const Sequelize = require('sequelize');
const sequelize = require('./sequelize');
const User = require('../models/user');
const Team = require('../models/team');
const TeamUser = require('../models/team-user');
const UserAccount = require('../models/user-account');
// 以下で m:n のリレーションを作成でき、以下が実行可能になる
// user.addTeam : 既存のteamテーブルのteamモデル(配列も可)をuserと関連付け。
// user.addTeams : 既存のteamテーブルのteamモデル(配列)をuserと関連付け。
// user.countTeams : userに関連付けされたteamを数え上げる。
// user.createTeam : teamオブジェクトを渡して、新規にteamテーブルにteamレコードを作成して且つ関連付け。
// user.getTeams : userと関連付けされたteamモデルを取得する。
// user.hasTeam : userに引数で与えたteamモデルが関連付けされているか確認する。(true/false)
// user.hasTeams : userに引数で与えたteamモデルが関連付けされているか確認する。(true/false)
// user.removeTeam : userと引数で与えたteamモデル(配列も可)の関連付けを削除する。ただし、teamモデルは削除されない。
// user.removeTeams : userと引数で与えたteamモデル(配列)の関連付けを削除する。ただし、teamモデルは削除されない。
// user.setTeams : 意味がわかってない。addのように関連付けはされない。
// 参考:http://docs.sequelizejs.com/class/lib/associations/belongs-to-many.js~BelongsToMany.html
User.belongsToMany(Team, {
  through: TeamUser,
  foreignKey: 'user_id',
  otherKey: 'team_id'
});
// 以下で m:n のリレーションを作成でき、以下が実行可能になる
// team.addUser(s), team.countUsers, team.createUser, team.getUsers,
// team.hasUser(s), team.removeUser(s), team.setUsers
Team.belongsToMany(User, {
  through: TeamUser,
  foreignKey: 'team_id',
  otherKey: 'user_id'
});
User.hasMany(UserAccount, {
  foreignKey: 'user_id'
});
UserAccount.belongsTo(User, {
  foreignKey: 'user_id',
  targetKey: 'user_id'
});
// テーブル作成
User.sync();
Team.sync();
TeamUser.sync();
UserAccount.sync();
module.exports = {
  User: User,
  Team: Team,
  TeamUser: TeamUser,
  UserAccount: UserAccount
};
belongsToMany を使用して中間テーブルを介したモデルを構築する際は必ず through を記載する。
belongsToMany でリレーションを構築するとユーザモデルから以下の関数を実行できるようになる。(チームモデルも同様)
- user.addTeam
- 既存のteamテーブルのteamモデル(配列も可)をuserと関連付け。
 
- user.addTeams
- 既存のteamテーブルのteamモデル(配列)をuserと関連付け。
 
- user.countTeams
- userに関連付けされたteamを数え上げる。
 
- user.createTeam
- teamオブジェクトを渡して、新規にteamテーブルにteamレコードを作成して且つ関連付け。
 
- user.getTeams
- userと関連付けされたteamモデルを取得する。
 
- user.hasTeam
- userに引数で与えたteamモデルが関連付けされているか確認する。(true/false)
 
- user.hasTeams
- userに引数で与えたteamモデルが関連付けされているか確認する。(true/false)
 
- user.removeTeam
- userと引数で与えたteamモデル(配列も可)の関連付けを削除する。
- ただし、teamモデルは削除されない。
 
- user.removeTeams
- userと引数で与えたteamモデル(配列)の関連付けを削除する。
- ただし、teamモデルは削除されない。
 
- user.setTeams
- add との違いがわかってない。。。
 
app/app.js
const db = require('./repositories/db');
const User = db.User;
const Team = db.Team;
User.create({
  user_id: '0001',
  user_name: 'user0001'
}).then(() => {
  User.findOne({
    where:{user_id: '0001'}
  }).then((user) => {
    console.log("user_name: " + user.user_name);
    user.createTeam({
      team_id: `0001`,
      team_name: 'team0001'
    }).then(() => {
      user.countTeams().then((num) => {
        console.log("team num of user: " + num);
      }).then(() => {
        user.getTeams().then((teams) => {
          console.log("teams: " + teams[0].dataValues.team_name);
          Team.create({
            team_id: '0002',
            team_name: 'team0002'
          }).then(() => {
            Team.findOne({
              where: {team_id: '0002'}
            }).then((team) => {
              console.log("team_name: " + team.dataValues.team_name);
              user.addTeam(team).then(() => {
                user.countTeams().then((num) => {
                  console.log("team num of user: " + num);
                }).then(() => {
                  let teams = [];
                  teams.push(team);
                  user.removeTeams(teams).then(() => {
                    user.countTeams().then((num) => {
                      console.log("team num of user: " + num);
                    });
                  });
                });
              });
            });
          });
        })
      });
    });
  });
});
おすすめ書籍
リンク
 
                    