📘

【Node.js】Expressでmvc環境構築!!

2020/11/29に公開
2

はじめに

オンラインプログラミング学習サービス「Progate」のコースに Web開発パス(Node.js) ってのがあったので学習してみました。
ただ学習しただけで終わりにしたくはなかったので、自分のローカルに環境を作成してProgateで出来たことが問題なくできるのか確認してみました。
その際に色々と調べて学んだことをつらつらと書いていきます。

Expressとは

Node.jsのWebアプリケーションフレームワークであり、高速で軽量なのが売りです。
またNode.jsでWebアプリケーションを開発する場合はデファクトスタンダードであり、インターネット上にも情報が豊富に存在します。

バックエンドもjavascriptを使いたいと思う方は触れてみても良いかもしれません。
※筆者はフロントエンドの経験が浅く、javascriptをもっと理解しようと思い触れました。

mvcとは

Webアプリケーションのフレームワークの基本的な概念の1つで、開発する際に処理の役割を分けてソースコードを管理しやすくできます。

  • モデル(Model):m
     ⇒DBとのやり取りをメインで担当
  • ビュー(View):v
     ⇒表示させる内容、処理をメインで担当
  • コントローラー(Controller):c
     ⇒ユーザーの入力に基づき、ModelとViewを制御する担当

Node.jsの環境について

この記事はあくまでExpressの実行環境を構築&開発を進める上でのインストール等々を記載します。Node.jsはインストール済みとして進めます。

筆者はdockerでNode.jsの環境を作成し、使用しています。
念のため以下のファイルの内容を載せておきます。

適当なディレクトリを作成し、

  • docker-compose.yml
  • Dockerfile
  • opt(フォルダ)

を用意してdocker-composeコマンドを使用してビルド&アップすれば簡単にできます。

docker-compose.yml↓

version: '3'
services:
  node:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./opt:/usr/src/app # ローカルとコンテナ内の同期    
    ports:
      - "3000:3000"
    stdin_open: true 
Dockerfile
FROM node:14.5.0-alpine
WORKDIR /usr/src/app

※alpineはNode.jsの最小のイメージです。もしかすると規模によっては開発に影響が出るかも…

Expressの環境作成

Node.jsの実行環境が用意できたらnpm(Node Package Manager)コマンドが使えるようになっているはずです。
npmコマンドを使ってExpressを含め、様々なパッケージをインストールしてWebアプリケーションを作成していきます。

Expressの環境を構築する方法が大きく2つあります。
①ディレクトリから各ファイルまで自分で1から作成する
②パッケージを作って自動でフォルダ構成と必要なファイルを作成する。

どちらでもExpressでWebアプリケーションを作成できますが②の方法で記載していきます。

前準備

express-generatorのインストール

$ npm install express-generator -g

Expressの開発元が提供するジェネレーター。express用のディレクトリ構成を自動で作ってくれます。さらに必要なライブラリが記載済みのpackage.jsonファイルも作ってくれます。
※ここでの「-g」はグローバルインストール。
 expressコマンドを使用できるようにするため。

Sequelize-cliのインストール

$ npm install sequelize-cli -g

Node.js用のORM(Object Relation Mapping)でマイグレーション操作のためのライブラリです。express-generatorではDB操作部分は作成されないのでこのライブラリで作成します。

プロジェクトを作ろう

任意のディレクトリ上で

$ express --view=[テンプレートエンジン] [プロジェクト名]

今回は以下で進めます。

  • テンプレートエンジン:ejs
  • プロジェクト名:login_app_ejs
$ express --view=ejs login_app_ejs

※どのテンプレートエンジンを指定できるかは「express -h」コマンドで確認できます。

コマンドを実行すると以下が表示されます。

   create : login_app_ejs/
   create : login_app_ejs/public/
   create : login_app_ejs/public/javascripts/
   create : login_app_ejs/public/images/
   create : login_app_ejs/public/stylesheets/
   create : login_app_ejs/public/stylesheets/style.css
   create : login_app_ejs/routes/
   create : login_app_ejs/routes/index.js
   create : login_app_ejs/routes/users.js
   create : login_app_ejs/views/
   create : login_app_ejs/views/error.ejs
   create : login_app_ejs/views/index.ejs
   create : login_app_ejs/app.js
   create : login_app_ejs/package.json
   create : login_app_ejs/bin/
   create : login_app_ejs/bin/www

   change directory:
     $ cd login_app_ejs

   install dependencies:
     $ npm install

   run the app:
     $ DEBUG=login-app-ejs:* npm start

ディレクトリが生成されていると思います。

login_app_ejs
├─app.js
├─package.json
├─bin
│  └─www
├─public
│  ├─images
│  ├─javascripts
│  └─stylesheets
│    └─style.css    
├─routes
│  ├─index.js
│  └─users.js  
└─views
   ├─error.ejs
   └─index.ejs

ライブラリをインストールします。

$ cd login_app_ejs
$ npm install

この段階でWebアプリケーションとして最低限の構成ができたので実際に動かしてみましょう。

$ DEBUG=login-app-ejs:* npm start
※解除はCtrl + c

ブラウザで3000ポートを指定して以下の画面が表示されれば成功

ORM環境を追加しよう

※ここではDBはMySQLを使用する前提で記載します。

MySQLへの接続用ライブラリとsequelizeの本体をインストールします。

$ npm install mysql2 sequelize

sequelize-cliでORM用のディレクトリとファイルを生成

$ npx sequelize-cli init

この時点でディレクトリ構成は以下になります。

login_app_ejs
├─app.js
├─package-lock.json
├─package.json
├─bin
│  └─www  
├─config ← できてる!
│  └─config.json
├─migrations ← できてる!
├─models ← できてる!
│  └─index.js  
├─node_modules
├─public
│  ├─images
│  ├─javascripts
│  └─stylesheets
│    └─style.css
├─routes
│  ├─index.js
│  └─users.js  
├─seeders ← できてる!
└─views
   ├─error.ejs
   └─index.ejs

DBの接続情報はconfig/config.jsonに記載します。

config.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"
  }
}

ここでの「development」「test」「production」は開発環境、テスト環境、本番環境の意味です。それぞれの環境に適用する際に切り替えられるように接続情報をこのファイルに集めています。

※詳しくはprocess.envNODE_ENVを調べてみてください。

マイグレーションをしてみよう

Userテーブルを作るまでをサンプルとして記載してみます。

〇Userテーブルの構成

物理名
id int(11)
name varchar(255)
email varchar(255)
password varchar(255)
rememberToken varchar(255)
createdAt datetime
updatedAt datetime

テーブルを作成

Userテーブルのモデルを作成します。

$ npx sequelize-cli model:generate --name User --attributes name:string,email:string,password:string,rememberToken:string
※--attributesの後に項目と型指定する
※id、CreatedAt、UpdatedAtのフィールドが自動作成される
  • /models/user.js
  • /migrations/xxxxxxxxxxxxxx-create-user.js

というファイルが生成される。

user.js
'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class User extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      // define association here
    }
  };
  User.init({
    name: DataTypes.STRING,
    email: DataTypes.STRING,
    password: DataTypes.STRING,
    rememberToken: DataTypes.STRING
  }, {
    sequelize,
    modelName: 'User',
  });
  return User;
};
xxxxxxxxxxxxxx-create-user.js
'use strict';
module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable('Users', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      name: {
        type: Sequelize.STRING
      },
      email: {
        type: Sequelize.STRING
      },
      password: {
        type: Sequelize.STRING
      },
      rememberToken: {
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable('Users');
  }
};

マイグレーションファイルのみ作成

モデルを作らず、マイグレーションファイルのみ作成もできます。

$ npx sequelize-cli migration:generate --name User

Migrationの実行

$ npx sequelize-cli db:migrate

正常に処理が終わればDBにUserテーブルが生成される。
モデルも生成済みなのですぐにでもデータの操作ができるようになる。

Migrationを元に戻す

1つ前に戻す場合

$ npx sequelize-cli db:migrate:undo

すべてを戻し、最初から行う場合

$ npx sequelize-cli db:seed:undo:all

Seedも使ってデータを登録してみる

Seedファイルを生成します

$ npx sequelize-cli seed:generate --name test-user
  • /seeders/xxxxxxxxxxxxxx-test-user.js

というファイルが生成される。

xxxxxxxxxxxxxx-test-user.js(生成時)
'use strict';

module.exports = {
  up: async (queryInterface, Sequelize) => {
    /**
     * Add seed commands here.
     *
     * Example:
     * await queryInterface.bulkInsert('People', [{
     *   name: 'John Doe',
     *   isBetaMember: false
     * }], {});
    */
  },

  down: async (queryInterface, Sequelize) => {
    /**
     * Add commands to revert seed here.
     *
     * Example:
     * await queryInterface.bulkDelete('People', null, {});
     */
  }
};

xxxxxxxxxxxxxx-test-user.js(修正後)
'use strict';

module.exports = {
  up: (queryInterface, Sequelize) => {
    const now = new Date();
    return queryInterface.bulkInsert('Users', [
      { name: 'テストA',  email: 'aaa@example.com', password: 'aaa-password', createdAt: now, updatedAt: now},
      { name: 'テストB',  email: 'bbb@example.com', password: 'bbb-password', createdAt: now, updatedAt: now},
      { name: 'テストC',  email: 'ccc@example.com', password: 'ccc-password', createdAt: now, updatedAt: now},
      { name: 'テストD',  email: 'ddd@example.com', password: 'ddd-password', createdAt: now, updatedAt: now},
      { name: 'テストE',  email: 'eee@example.com', password: 'eee-password', createdAt: now, updatedAt: now},
    ], {});
  },

  down: (queryInterface, Sequelize) => {
    return queryInterface.bulkDelete('Users', null, {});
  }
};

Seedの実行

npx sequelize-cli db:seed:all

以下のようにテーブルにデータが登録される

id name email password rememberToken createdAt updatedAt
1 テストA aaa@example.com aaa-password null yyyy-MM-dd HH:mm:ss yyyy-MM-dd HH:mm:ss
2 テストB bbb@example.com bbb-password null yyyy-MM-dd HH:mm:ss yyyy-MM-dd HH:mm:ss
3 テストC ccc@example.com ccc-password null yyyy-MM-dd HH:mm:ss yyyy-MM-dd HH:mm:ss
4 テストD ddd@example.com ddd-password null yyyy-MM-dd HH:mm:ss yyyy-MM-dd HH:mm:ss
5 テストE eee@example.com eee-password null yyyy-MM-dd HH:mm:ss yyyy-MM-dd HH:mm:ss

Seedを元に戻す

すべてを戻し、最初から行う場合

$ npx sequelize-cli db:migrate:undo:all

まとめてやっちゃおう!

$ npx sequelize db:migrate:undo:all &&  sequelize db:migrate && sequelize db:seed:all

何かしらモデルの追加や変更、テストデータの追加等が発生した場合はこのコマンドでマイグレーション~Seedの追加まで行ってくれます。
※テーブルのデータは作り直されてしまうので注意

まとめ

ここまででmvcモデルの開発がある程度できる状態になりました。
Controllerは自分でディレクトリ、ファイルを作成しなければいけません。
しかし、ライブラリでディレクトリ構成とマイグレーションまではできたので一旦はOKでしょう。

mvcを使用してのコーディングの中身は所属する会社、チーム、個人で多少変わると思われるので周りに合わせる形で綺麗なコーディングができるとよいですね。

Discussion

Ren SuyamaRen Suyama

情報をまとめてくださってありがとうございました!
2点修正のお願いです!

①テーブルを作成する箇所

テーブルを作成
Userテーブルのモデルを作成します。

テーブル項目の「rememberToken」が抜けてましたので、下記修正をお願いしたいです!

■誤:$ npx sequelize-cli model:generate --name User --attributes name:string,email:string,password:string
■正:$ npx sequelize-cli model:generate --name User --attributes name:string,email:string,password:string,rememberToken:string

②Seedの箇所

Seedを元に戻す
すべてを戻し、最初から行う場合

コマンドのところがseedではなくmigrateになっているので下記修正をお願いしたいです!

■誤:$ npx sequelize-cli db:migrate:undo:all
■正:$ npx sequelize-cli db:seed:undo:all

リョースケリョースケ

丁寧なご指摘ありがとうございます!
該当箇所を修正させていただきました!