mongoose(Node.js上でMongoDBを扱うためのライブラリ)を使うときに少し困ったので、二つのサイトを参考にして良いとこ取りをしてみました。すべてのコードを書くと、ちょっとしたwebアプリが出来上がります。ご指摘があればコメント欄にお願いします。
1.準備
VScodeを使いました。軽くて機能も豊富なのでお勧めです。
準備は以下の通りです。無い場合も簡単に入れられます。
- mongoose
- MongoDB
- node.js
VScodeを使う場合、拡張機能は以下のものを入れておきます。
- EJS language support
- ESLint
- MongoDB for VS Code
2.構成
VScodeのexpress-generatorというツールを使うと、Expressのアプリケーションの構造の原型が自動生成されるのでそこにフォルダ、ファイルを追加していきます。次のようなコードで生成します。
>npm install -g express-generator
>express --view="ejs" mongoImplement
>cd mongoImplement
mongoImplement> npm install
mongoImplement> npm install --save mongodb mongoose
このような感じです。重要なのは5つのJSファイル(routes/users.jsは今回は使いません)と2つのejsファイルです。
3.コード
7つのファイルを載せます。分かりづらいかもしれませんがVScode上に並べると明快なはずです。。
データベースへの接続方法が少しややこしいのですが、規模の大きめなアプリを作る場合には整理整頓されていてわかりやすいってなると思いました。ときどき上のディレクトリ構造を見ながら読んでみてください。
module.exports = {
url: "mongodb://localhost:27017/omochi"
};
/*
27017はMngoDBのデフォルトのポート番号。何もいじってなければ27017で大丈夫です。
その下にデータベース名を書きます(データベースが無くても、名付けるだけで勝手に作成されます)。
エクスポート(exports)することにより、ほかの場所でも使えるようになります。
*/
const dbConfig = require("../config/db.config.js"); //上のファイルのモジュールを読み込み。相対パスはお手元の構造に合うように指定してください。
const mongoose = require("mongoose"); //mongooseを読み込み
mongoose.Promise = global.Promise;
/*
非同期処理にpromiseを使います。
Promiseについてはhttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
*/
const db = {};
db.mongoose = mongoose;
db.url = dbConfig.url;
module.exports = db; //dbをエクスポート。これで他の場所でも使えます
以上で、「db」はお手元のMongoDBのデータベース(この例だとomochi)に接続されることになります。
つづいてマスターファイルです。
const express = require('express');//constとvarはどちらでもOK
const path = require('path');
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
//ルーティング設定のため、インポート。最後の2行と関連があります。詳しくはhttps://expressjs.com/ja/guide/routing.html
const app = express();
const db = require("./models"); //先ほどのファイルでエクスポートしたdb
db.mongoose
.connect(db.url, { //urlはdb.config.jsでエクスポートした、お手元のMongoDBのデータベースへのパス
useNewUrlParser: true, //omajinai
useUnifiedTopology: true //omajinai
})
.then(() => {
console.log("Connected to the database!"); //接続に成功するとシェルに表示されます
})
.catch(err => {
console.log("Cannot connect to the database!", err); //接続に失敗するとシェルに表示されます
process.exit();
});
//then,catchは非同期処理の成功時、失敗時に呼び出される関数です。詳しくは「promise then catch」等で検索
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
//app.useは、動的なページを作成するにあたって遷移先のファイルが何であれ実行してほしいときに使います。
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public'))); //この辺の設定はexpress-generatorで自動生成。
app.use('/', indexRouter);
app.use('/users', usersRouter); //ルーターは機能ごとにファイルを分けるのに役立ちます。
module.exports = app;
これでどのファイルでもデータベースに接続できるようになりました。
続いてmongooseの特性の一つであるスキーマ定義を行います。
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const clientSchema = new Schema({
userName: { type: String, required: true },
email: { type: String, required: true }, //requiredはバリデータ。空文字をエラーにしたい場合に使います。
}, {
timestamps: true, //ある値が生成された時間を記録するもの。記録したい場合に書きます。
});
const Client = mongoose.model('Client', clientSchema);
/*Clientモジュールを定義。clientSchemaというスキーマモデルを呼び出し、"clients"というコレクションが出来上がります。
('Client'の複数形?勝手にclientsになってました。コレクションという名前はNoSQLでの用語で、MySQLなどのRDBでいうテーブルにあたります。)
*/
module.exports = Client;
スキーマ定義をまとめてファイルに書いておくと開発などもしやすそうです。
routes/indexファイルを書きます。indexファイルは先ほどのapp.jsファイルでルーティング設定していることを思い出しましょう。
const express = require('express');
const router = express.Router();
const Client = require('../models/models'); //Clientのスキーマモデルをインポート
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('newuser', { title: 'Add New User' }); // views/newuser.ejsのtitleに文字を代入。
});
/* GET New User page. */
router.get('/newuser', function(req, res) { //redirect用
res.render('newuser', { title: 'Add New User' });
});
/* GET Userlist page. */
router.get('/userlist', function(req, res) {
Client.find() //最初にインポートしておくとこれだけで使えます
.then(docs => res.render('userlist', {"userlist" : docs}))
.catch(err => res.send("There was a problem adding the information to the database."));
});
/*
引数docsはfindした検索結果です。適当な単語で大丈夫ですが、行の最後の単語と同じにします。
res.renderの中の('userlist',{"userlist" : docs} )は、userlist.ejs(一つ目の引数)に書かれたuserlist(二つ目の引数)にdocsを代入するという意味です。
*/
/* POST to Add User Service */
router.post('/adduser', function(req, res) {
const userName = req.body.username; //フロントで<input>を使うとreq.body.nameに格納されます。nameは<input>内のname="username"の箇所
const email = req.body.useremail;
// Submit to the DB
const newClient = new Client({
userName,
email,
});
/*
↑ がmongooseを使うときのデータの追加方法です。スキーマ定義してあるのでこれだけでいいのですね。
mongooseを使わない場合と違い、データ追加の命令をコールバック関数の外に書くのがポイントです。
*/
newClient.save()
.then(() => res.redirect('userlist'))
.catch(err => res.send("There was a problem adding the information to the database."));
});
module.exports = router;
最後にフロントを書きます。
<!DOCTYPE html>
<html>
<head>
<title>Add User</title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<form id="formAddUser" name="adduser" method="post" action="/adduser">
<input id="inputUserName" type="text" placeholder="username" name="username" />
<input id="inputUserEmail" type="text" placeholder="email" name="useremail" />
<button id="btnSubmit" type="submit">Submit</button>
</form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>User List</title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1>User List</h1>
<ul>
<%
var list = '';
for (i = 0; i < userlist.length; i++) {
list += '<li><a href="mailto:' + userlist[i].email + '">' + userlist[i].userName + '</a></li>';
}
%>
<%- list %>
</ul>
</body>
</html>
「<%-」と「<%=」は、htmlの要素として出力したいか、文字として出力したいかで使い分けます
最後に以下のコードで実行し、ブラウザでhttp://localhost:3000
などで検索すると二つのページを行き来できます(動作確認済み)。入力した内容はMongoDBにたまってゆきます。MongoDBCompassを使うと、視覚的に分かりやすくデータが表示されます(MySQLのようなテーブル型で、等)。
mongoImplement> npm start
以上でコードは終わりです。お疲れさまでした。MongoDB,Expressに加えてReactやpandas,scikit-learnを使うと本格的なアプリ開発になります。