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に掲示板データを保存して表示することができるようになりました。