JavaScriptだけでWebサイト開発

node.js + Express + mongodb系 + React でサービスを作るメモ

node.js と mongodbで掲示板アプリを作ってみる

前回つくった掲示板サービスはnode.jsのhttpサーバー起動中の配列に書き込みデータを保存したため、サーバー再起動でデータが消えてしまいます。

今回はmongodbにデータを保存してみます。

1. mongodbのインストールと起動

前提となる開発環境はMacOS High Sierra

インストールして起動

$ brew install mongodb
~
$ brew services start mongodb

mongo クライアント(mongo shell)で接続確認します。

$ mongo
MongoDB shell version v3.4.9
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.9
Server has startup warnings: 
2017-11-02T12:13:12.092+0900 I CONTROL [initandlisten] 
2017-11-02T12:13:12.093+0900 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-11-02T12:13:12.093+0900 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2017-11-02T12:13:12.093+0900 I CONTROL [initandlisten] 
>

warningが出ていますが普通に使えます。アクセスコントロールを設定すれば出なくなります。

次にデータベースを作って、コレクションsampleに適当なデータを入れて確認してみる。

mongo

> use app1
> db.sample.insert({no:1,name:"ichi-bit",age:38})
WriteResult({ "nInserted" : 1 })
> db.sample.find()
{ "_id" : ObjectId("59fa915f7f36c7f99b4ac89e"), "no" : 1, "name" : "ichi-bit", "age" : 38 }

動作確認完了

2. node.jsでmongodbのdriverを使う 

npmでmongodbをインストール

$ cd app1
$ npm install mongodb

これで

app.js

const mongodb = require('mongodb');

として利用できます。

3. 掲示板アプリをmongodb利用に変更

前回のソース

app.js

const http = require('http'); // httpサーバーmodule
const hostname = 'localhost'; // ホスト名
const port = 3000; // port番号

// httpサーバーの定義
const server = http.createServer((req, res) => {

    // 全リクエストを処理
    res.statusCode = 200; // http ステータスコード
    res.setHeader('Content-Type', 'text/html; charset=UTF-8'); // HTMLを返す 日本語返すので charsetもセット

    // ルーティング (url により振り分ける)
    switch (req.url) { // req.url にリクエストされたパスが入る
        case '/':
        topPage(req,res); // トップページ用関数
        break;
        case '/up':
        upPage(req,res);  // 投稿ページ用関数
        break;
        default:
        notFoundPage(req,res); // その他ページ用関数 Not Foundページにします
        break;
    }
});

// サーバー起動
server.listen(port, hostname, () => {
    console.log(`Server running at http://${hostname}:${port}/`);
});

/////////////////////// 各ページの機能 //////////////////////////
// ヘッダ
function header(req,res) {
    res.write('<html><head><title>掲示板</title><style>* {box-sizing:border-box;}</style></head><body style="position:relative;height:100%;">');
    res.write('<header style="border:1px solid #888;padding:40px;">掲示板</header>');
    res.write('<nav><ul><li><a href="/">トップ</a></li><li><a href="/up">投稿</a></li></nav>');
}

// フッタ
function footer(req,res) {
    res.write('<footer style="position:absolute;bottom:0;width:100%;border:1px solid #888;text-align:center;padding:20px;">フッター</footer>\n'); // 共通のフッター
    res.end('</body></html>'); // res.endでもコンテンツを返せる
}

// トップページ
function topPage(req,res) {
    header(req,res);
    res.write('<h2>トップページ</h2>\n');
    res.write('<ul>');
    for(let row of posts) {
        res.write('<li>'+row+'</li>\n'); // htmlescapeは省略してます
    }
    res.write('</ul>');
    footer(req,res);
}

// 投稿ページ
var posts = []; // 掲示板データ
function upPage(req,res) {
    header(req,res);
    // リクエストメソッドで処理を変える
    // GETの処理
    if (req.method === 'GET') {
        res.write('<h2>投稿します</h2>\n');
        res.write('<form action="/up" method="post"><div><textarea name="kakikomi" style="width:80%;height:100px"></textarea><div><div><input type="submit" value="投稿"></div></form>');
        footer(req,res);
        return;
    }
    // POSTの処理
    if (req.method === 'POST') {
        // POSTデータを受け取る(共通)
        // dataイベントでPOSTされたデータがちょっとずつ来るのでdataに蓄積する
        let body = [];
        req.on('data', (chunk) => {
            body.push(chunk);
        }).on('end', () => {
            body = Buffer.concat(body).toString();
            //パースする
            const querystring = require('querystring');
            parsedBody = querystring.parse(body);

            if (parsedBody.kakikomi) {
                posts.push(parsedBody.kakikomi);
                res.write('<h2>投稿しました</h2>\n');
                res.write(decodeURIComponent(parsedBody.kakikomi)); // htmlescape省略
            }
            footer(req,res);
        });
    }
}

// その他のページ
function notFoundPage(req,res) {
    res.statusCode = 404; // http ステータスコードを返します
    header(req,res);
    res.write('<h2>ページはありません</h2>');
    footer(req,res);
}

これをこう変えます

app.js

const http = require('http'); // httpサーバーmodule
const hostname = 'localhost'; // ホスト名
const port = 3000; // port番号

// httpサーバーの定義
const server = http.createServer((req, res) => {

    // 全リクエストを処理
    res.statusCode = 200; // http ステータスコード
    res.setHeader('Content-Type', 'text/html; charset=UTF-8'); // HTMLを返す 日本語返すので charsetもセット

    // ルーティング (url により振り分ける)
    switch (req.url) { // req.url にリクエストされたパスが入る
        case '/':
        topPage(req,res); // トップページ用関数
        break;
        case '/up':
        upPage(req,res);  // 投稿ページ用関数
        break;
        default:
        notFoundPage(req,res); // その他ページ用関数 Not Foundページにします
        break;
    }
});

// サーバー起動
server.listen(port, hostname, () => {
    console.log(`Server running at http://${hostname}:${port}/`);
});

/////////////////////// 各ページの機能 //////////////////////////
// ヘッダ
function header(req,res) {
    res.write('<html><head><title>掲示板</title><style>* {box-sizing:border-box;}</style></head><body style="position:relative;height:100%;">');
    res.write('<header style="border:1px solid #888;padding:40px;">掲示板</header>');
    res.write('<nav><ul><li><a href="/">トップ</a></li><li><a href="/up">投稿</a></li></nav>');
}

// フッタ
function footer(req,res) {
    res.write('<footer style="position:absolute;bottom:0;width:100%;border:1px solid #888;text-align:center;padding:20px;">フッター</footer>\n'); // 共通のフッター
    res.end('</body></html>'); // res.endでもコンテンツを返せる
}

// トップページ
function topPage(req,res) {
    header(req,res);
    res.write('<h2>トップページ</h2>\n');

    //////////////  ② mongodbからデータを取得する ///////////////////////
    getKakikomi((posts) => {
        res.write('<ul>');
        for(let row of posts) {
            console.log(row);
            res.write('<li>'+row.kakikomi+'</li>\n'); // htmlescapeは省略してます
        }
        res.write('</ul>');
        footer(req,res);
    });
}

// 投稿ページ
var posts = []; // 掲示板データ
function upPage(req,res) {
    header(req,res);
    // リクエストメソッドで処理を変える
    // GETの処理
    if (req.method === 'GET') {
        res.write('<h2>投稿します</h2>\n');
        res.write('<form action="/up" method="post"><div><textarea name="kakikomi" style="width:80%;height:100px"></textarea><div><div><input type="submit" value="投稿"></div></form>');
        footer(req,res);
        return;
    }
    // POSTの処理
    if (req.method === 'POST') {
        // POSTデータを受け取る(共通)
        // dataイベントでPOSTされたデータがちょっとずつ来るのでdataに蓄積する
        let body = [];
        req.on('data', (chunk) => {
            body.push(chunk);
        }).on('end', () => {
            body = Buffer.concat(body).toString();
            //パースする
            const querystring = require('querystring');
            parsedBody = querystring.parse(body);

            if (parsedBody) {
                //////////////  ② mongodbに保存する ///////////////////////
                postKakikomi(parsedBody,() => {
                    res.write('<h2>投稿しました</h2>\n');
                    res.write(decodeURIComponent(parsedBody.kakikomi)); // htmlescape省略
                    footer(req,res);
                });
            }
        });
    }
}

// その他のページ
function notFoundPage(req,res) {
    res.statusCode = 404; // http ステータスコードを返します
    header(req,res);
    res.write('<h2>ページはありません</h2>');
    footer(req,res);
}


/////////////////////// ③ mongodbデータの機能定義 ////////////////////////////
// mongodb利用
const mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
var DB;
MongoClient.connect('mongodb://localhost:27017/app1', function(err, db) {
  console.log("Connected successfully to server");
  DB = db;
});

// 書き込み全件取得
var getKakikomi = (callback) => {
    DB.collection('posts').find().toArray((err,docs) =>{
        callback(docs)
    });
}

// 書き込みを保存
var postKakikomi = (json, callback) => {
    DB.collection('posts').insertOne(json,(err, result) =>{
        console.log('inserted');
        callback();
    });
}

①〜③の箇所が今回編集したところです。

やったこと

  • ①mongodbから書き込みデータを全件取得を実行してデータを表示
  getKakikomi((posts) => {
      res.write('<ul>');
      for(let row of posts) {
          console.log(row);
          res.write('<li>'+row.kakikomi+'</li>\n'); // htmlescapeは省略してます
      }
      res.write('</ul>');
      footer(req,res);
  });
  • ②formのbodyを保存する処理
              postKakikomi(parsedBody,() => {
                  res.write('<h2>投稿しました</h2>\n');
                  res.write(decodeURIComponent(parsedBody.kakikomi)); // htmlescape省略
                  footer(req,res);
              });
  • ③mongodbへの機能をまとめて定義
    • mongodb データベースapp1へ接続しDBをコネクションプールとして使用
// mongodb利用
const mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
var DB;
MongoClient.connect('mongodb://localhost:27017/app1', function(err, db) {
  console.log("Connected successfully to server");
  DB = db;
});
  • function getKakikomi で posts コレクションをfindして返す機能定義
// 書き込み全件取得
var getKakikomi = (callback) => {
    DB.collection('posts').find().toArray((err,docs) =>{
        callback(docs)
    });
}
  • function postKakikomi で posts コレクションにデータを追加する機能定義
// 書き込みを保存
var postKakikomi = (json, callback) => {
    DB.collection('posts').insertOne(json,(err, result) =>{
        console.log('inserted');
        callback();
    });
}

ブラウザで確認

  • 前回と全く同じ動きをします
  • サーバーを再起動してもデータは保存されています。

4. まとめ

これでmongodbに掲示板データを保存して表示することができるようになりました。

©ichi-bit