今回は、Express と MongoDB を繋げます。

コードは、前々回のコードを使用します。

views/index.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <h1>メニュー</h1>
      <p>メニュー一覧</p>
      <% if(menus.length > 0) { for (let menu of menus){ %>
      <p><%= menu.title %></p>
      <a href="/menu/<%= menu.id %>">詳細画面</a>
      <div>
        <a href="/edit-menu/<%= menu.id %>">編集</a>
        <form action="/delete-menu" method="POST">
          <input type="hidden" value="<%= menu.id %>" name="menuId" />
          <button type="submit">削除</button>
        </form>
      </div>
      <%} %> <% } else { %>
      <p>注文がありません</p>
      <% } %>
    </main>
  </body>
</html>

views/menu.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/css/styles.css" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <form action="/menu" method="POST">
        <div class="form-control">
          <label for="title">メニュー名</label>
          <input type="text" name="title" />
        </div>
        <div class="form-control">
          <label for="description">内容</label>
          <input type="text" name="description" />
        </div>
        <button type="submit">メニューを追加する</button>
      </form>
    </main>
  </body>
</html>

views/menu-detail.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <h1><%= menu.title %></h1>
      <p><%= menu.description %></p>
    </main>
  </body>
</html>

views/menu-edit.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/css/styles.css" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <form action="/edit-menu" method="POST">
        <input type="hidden" name="id" value="<%= menu.id %>" />

        <div class="form-control">
          <label for="title">メニュー名</label>
          <input type="text" name="title" value="<%= menu.title %>" />
        </div>
        <div class="form-control">
          <label for="description">内容</label>
          <input
            type="text"
            name="description"
            value="<%= menu.description %>"
          />
        </div>
        <button type="submit">メニューを更新する</button>
      </form>
    </main>
  </body>
</html>

index.js

const express = require("express");
const app = express();
const path = require("path");

app.set("view engine", "ejs");
app.set("views", "views");

const menuRoutes = require("./routes/index.js");
const menus = require("./routes/menu.js");

app.use(express.urlencoded({ extended: true }));

app.use(express.static(path.join(__dirname, "public")));

app.use("/", menus);
app.use(menuRoutes);

app.use((req, res, next) => {
  res.status(404).send("<h1>ページが見つかりません</h1>");
});

app.listen(8000, () => console.log("Server is running ..."));

models/menu.js

const fs = require("fs");
const path = require("path");

const menus = [];

module.exports = class Menu {
  constructor(id, title, description) {
    this.id = id;
    this.title = title;
    this.description = description;
  }

  save() {
    if (this.id) {
      const menuPath = path.join(
        path.dirname(require.main.filename),
        "data",
        "menus.json"
      );
      fs.readFile(menuPath, (err, fileContent) => {
        const menuData = JSON.parse(fileContent);

        const thisMenu = menuData.findIndex((menu) => menu.id === this.id);
        const menuUpdate = [...menuData];
        menuUpdate[thisMenu] = this;
        fs.writeFile(menuPath, JSON.stringify(menuUpdate), (err) => {
          console.log(err);
        });
      });
    } else {
      this.id = (Math.random() + 1).toString(36).slice(2, 8);
      const menuPath = path.join(
        path.dirname(require.main.filename),
        "data",
        "menus.json"
      );
      fs.readFile(menuPath, (err, fileContent) => {
        let menus = [];
        if (!err) {
          menus = JSON.parse(fileContent);
        }
        menus.push(this);
        fs.writeFile(menuPath, JSON.stringify(menus), (err) => {
          console.log(err);
        });
      });
    }
  }

  static fetchAll(data) {
    const menuPath = path.join(
      path.dirname(require.main.filename),
      "data",
      "menus.json"
    );
    fs.readFile(menuPath, (err, fileContent) => {
      if (err) {
        data([]);
      }
      data(JSON.parse(fileContent));
    });
  }

  static fetchMenu(id, data) {
    const menuPath = path.join(
      path.dirname(require.main.filename),
      "data",
      "menus.json"
    );

    fs.readFile(menuPath, (err, fileContent) => {
      if (err) {
        data([]);
      }
      const menuData = JSON.parse(fileContent);
      const menuDetail = menuData.find((menu) => menu.id === id);
      data(menuDetail);
    });
  }

  static deleteMenu(id) {
    const menuPath = path.join(
      path.dirname(require.main.filename),
      "data",
      "menus.json"
    );
    fs.readFile(menuPath, (err, fileContent) => {
      if (err) {
        data([]);
      }
      const menuData = JSON.parse(fileContent);
      const updatedMenus = menuData.filter((menu) => menu.id !== id);
      fs.writeFile(menuPath, JSON.stringify(updatedMenus), (err) => {
        console.log(err);
      });
    });
  }
};

controllers/menus.js

const Menu = require("../models/menu");

exports.getAddMenu = (req, res, next) => {
  res.render("menu", { pageTitle: "メニュー追加" });
};

exports.postAddMenu = (req, res, next) => {
  const id = req.body.id;
  const title = req.body.title;
  const description = req.body.description;
  const menu = new Menu(id, title, description);
  menu.save();
  res.redirect("/");
};

exports.getAddMenus = (req, res, next) => {
  Menu.fetchAll((menus) => {
    res.render("index", {
      pageTitle: "メニュー一覧",
      menus,
    });
  });
};

exports.getMenu = (req, res, next) => {
  const id = req.params.menuId;
  Menu.fetchMenu(id, (menu) => {
    res.render("menu-detail", {
      menu: menu,
      pageTitle: "詳細画面",
    });
  });
};

exports.postDeleteMenu = (req, res, next) => {
  const id = req.body.menuId;
  Menu.deleteMenu(id);
  res.redirect("/");
};

exports.getEditMenu = (req, res, next) => {
  const id = req.params.menuId;
  Menu.fetchMenu(id, (menu) => {
    res.render("menu-edit", {
      menu: menu,
      pageTitle: "メニュー内容編集",
    });
  });
};

exports.postEditMenu = (req, res, next) => {
  const id = req.body.id;
  const updatedTitle = req.body.title;
  const updatedDescription = req.body.description;
  const updatedMenu = new Menu(id, updatedTitle, updatedDescription);
  updatedMenu.save();
  res.redirect("/");
};

routes/index.js

const express = require("express");
const router = express.Router();

const menuController = require("../controllers/menus");

router.get("/", menuController.getAddMenus);

module.exports = router;

routes/menu.js

const express = require("express");
const router = express.Router();

const menuController = require("../controllers/menus");

router.get("/menu", menuController.getAddMenu);

router.get("/menu/:menuId", menuController.getMenu);

router.post("/menu", menuController.postAddMenu);

router.post("/delete-menu", menuController.postDeleteMenu);

router.get("/edit-menu/:menuId", menuController.getEditMenu);

router.post("/edit-menu", menuController.postEditMenu);

module.exports = router;

MongoDB をインストールする

まずは、MongoDB をインストールします。

ターミナルを開いて、npm install —save mongodbを実行します。

インストール完了後、utils フォルダを作成し、その中に database.js を作成します。

database.js にmongodbを呼び出します。

const mongodb = require("mongodb");

mongodbmongoClientを使います。

const MongoClient = mongodb.MongoClient;

dbConnect関数を作ります。

const dbConnect = (callback) => {

};

dbConnectの中にMongoClientconnectを使用します。

const dbConnect = (callback) => {
  MongoClient.connect()
};

MongoDB を開き、Databases の『Connect』をクリックします。

image2

『Connect your application』をクリックします。

image3

②Add your connection string into your application code 内のコードをコピーします。

image4

コピーしたコードを、MongoClient.connect()ないに貼り付けます。

const dbConnect = (callback) => {
  MongoClient.connect(
    "mongodb+srv://Nao:<password>@cluster0.q1atv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
  )
};

貼り付けたコード内の<password>は、前回 Database Access でユーザー設定した際のパスワードを入力します。

image5

const dbConnect = (callback) => {
  MongoClient.connect(
    "mongodb+srv://Nao:abcdefgh@cluster0.q1atv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
  )
};

接続がうまくいったら、console.log()で『MongoDb Connected』と表示し、内容をコールバックするように設定します。

また、エラーが起こったら、エラーを表示するようにします。

const dbConnect = (callback) => {
  MongoClient.connect(
    "mongodb+srv://Nao:abcdefgh@cluster0.q1atv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
  )
    .then((item) => {
      console.log("MongoDB Connected");
      callback(item);
    })
    .catch((err) => {
      console.log(err);
      throw err;
    });
};

dbConnectをエクスポートします。

module.exports = dbConnect;

index.js でdbConnectを呼び出します。

const dbConnect = require("./utils/database");

index.js のapp.listenに渡すことで、実行時に MongoDB と接続するようにします。

dbConnect(() => {
  app.listen(8000, () => console.log("Server is running ..."));
});

一度、サーバーを起動してみます。

image6

無事、起動することができました。

次は、データを MongoDB に保存してみます。

utils フォルダの database.js に『_db』を作成します。

let _db;

MongoDB に接続できたら、_dbにデータベースを保存するようにします。

const dbConnect = (callback) => {
  MongoClient.connect(
    "mongodb+srv://Nao:abcdefgh@cluster0.q1atv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
  )
    .then((item) => {
      console.log("MongoDB Connected");
      _db = item.db();
      callback(item);
    })
    .catch((err) => {
      console.log(err);
      throw err;
    });
};

データベースを取得する関数を作ります。

データーベースがある場合は、_db が返され、データーベースがない場合は、『データベースがありません』と返すようにします。

const getDb = () => {
  if (_db) {
    return _db;
  }
  throw "データベースがありません";
};

getDb関数をエクスポートします。

dbConnectも修正しておきましょう。

exports.dbConnect = dbConnect;
exports.getDb = getDb;

エクスポートが変わったので、index.js のdbConnectの呼び出しも修正します。

const dbConnect = require("./utils/database").dbConnect;

models フォルダの menu.js を開きます。

Menuクラスのconstructorにある、idは必要ないので、削除します。

constructor(title, description) {
    this.title = title;
    this.description = description;
  }

controllers フォルダの menujs にあるpostAddMenuidも必要ありません。

exports.postAddMenu = (req, res, next) => {
  const title = req.body.title;
  const description = req.body.description;
  const menu = new Menu( title, description);
  menu.save();
  res.redirect("/");
};

models フォルダの menu.js にgetDBを呼び出します。

const getDb = require("../utils/database").getDb;

Menuクラスのsave内容を一旦削除します。

getDB のcollectionを呼び出します。

コレクションの名前は、『menu』にします。

これが、MongoDB のコレクション名になります。

save() {
  return getDb()
    .collection("menu")
}

データを MongoDB に送りたい場合は、insertOneを使います。

save() {
  return getDb()
    .collection("menu")
    .insertOne(this)
}

一旦、thencatchconsole.logで表示するようにします。

save() {
  return getDb()
    .collection("menu")
    .insertOne(this)
    .then((res) => console.log(res))
    .catch((err) => console.log(err));
}

これで一通り完成しましたので、実際にデータを作成してみます。

image7

image8

『メニューを追加する』をクリックしたら、MongoDB を見てみます。

MongoDB の『Browse Collections』をクリックします。

image9

menu を確認すると、

image10

先程作成したデータが反映されていました。

ブログ一覧