※当方駆け出しエンジニアのため、間違っていることも多々あると思いますので、ご了承ください。また、間違いに気付いた方はご一報いただけると幸いです。
Node.js Express MongoDBを用いたアプリ開発
受託開発でNode.js Express MongoDBを用いたアプリ開発を行うことになりました。
開発の中で得られた知見を、支障のない範囲で記録していきたいと思います。
アプリの内容はさておき、チームで開発するため、まずはDockerを用いて開発環境を構築を行います。
構築する環境
- docker-composeを用いて、アプリコンテナ、mongodbコンテナを作成する。
- アプリコンテナとmongodbコンテナの接続テスト(mongoose)を用いて、テストモデルにデータ保存。
- mongo compassからデータの挿入を確認
- mongodbに認証を設ける。作成するユーザーの権限は"root","read","owner"
- データベースのパスワード等は環境変数で管理(gitignoreにてgitの管理から外す。)
※注意点
<アプリ名> <パスワード>となっているところは、各自で適当なものを記入してください。
そのまま記載するとエラーとなります。
今回は、パスワードは全て共通としています。
初期インストールするパッケージ一覧
"bcrypt"
"body-parser"
"connect-flash"
"cookie-parser"
"debug"
"ejs"
"express"
"express-ejs-layouts"
"express-generator"
"express-session"
"express-validator"
"http-errors"
"http-status-codes"
"method-override"
"mongoose"
"morgan"
"nodemon"
"passport"
"passport-local-mongoose"
下準備
最初に用意するファイル群
.
├── .env
├── .gitignore
├── data
│ └── db //空ディレクトリ
├── docker-compose.yml
├── docker_app
│ └── Dockerfile
├── secret_file
│ ├── db.env
│ └── db_init
│ └──mongo_init_user.js
└── src
├── controllers
│ └── initTestsController.js
├── models
│ └── init_test.js
└── package.json
env.
MONGO_INITDB_ROOT_USERNAME=root
MONGO_INITDB_ROOT_PASSWORD=<パスワード>
MONGO_INITDB_DATABASE=<DB名>
gitignore.
node_modules/
data/
.env
secret_file/
docker-compose.yml
version:'3'services:app:build:./docker_appcontainer_name:<アプリ名>_app_cntports:-"8080:3000"restart:alwaysworking_dir:/apptty:truevolumes:-/etc/passwd:/etc/passwd:ro-/etc/group:/etc/group:ro-./src:/appenv_file:-./secret_file/db.envcommand:bashnetworks:-<アプリ名>-networkdepends_on:-mongomongo:image:mongo:latestcontainer_name:<アプリ名>_db_cntports:-"27018:27017"restart:alwaysenvironment:MONGO_INITDB_ROOT_USERNAME:${MONGO_INITDB_ROOT_USERNAME}MONGO_INITDB_ROOT_PASSWORD:${MONGO_INITDB_ROOT_PASSWORD}MONGO_INITDB_DATABASE:${MONGO_INITDB_DATABASE}volumes:-./data/db:/data/db-./secret_file/db_init/:/docker-entrypoint-initdb.denv_file:-./secret_file/db.envcommand:-mongodnetworks:-<アプリ名>-networknetworks:<アプリ名>-network:external:true
Dockerfile.
FROM node:12
WORKDIR /app
RUN npm install
db.env
DB_USER=owner
DB_PASS=<パスワード>
DB_NAME=<アプリ名>_db
mongo_init_user.js
letusers=[{user:"read",pwd:"<パスワード>",roles:[{role:"read",db:"<アプリ名>_db"}]},{user:"owner",pwd:"<パスワード>",roles:[{role:"dbOwner",db:"<アプリ名>_db"}]},{user:"readWriteUser",pwd:"<パスワード>",roles:[{role:"readWrite",db:"<アプリ名>_db"}]}];for(leti=0,length=users.length;i<length;++i){db.createUser(users[i]);}
initTestsController.js
"use strict";constInitTest=require('../models/init_test');consttest=()=>{letinitTest=newInitTest({name:"Taro",age:20})initTest.save((error,data)=>{if(error){console.log(error);}console.log(data);})}module.exports={test};
init.test.js
"use strict";constmongoose=require("mongoose");constinitTestSchema=newmongoose.Schema({name:String,age:Number});module.exports=mongoose.model("InitTest",initTestSchema);
package.json
{}
環境作成
コンテナ作成
ホスト.
docker-compose build
下記の様な警告がでるが問題ない。
npm WARN saveError ENOENT: no such file or directory, open '/app/package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open '/app/package.json'
npm WARN app No description
npm WARN app No repository field.
npm WARN app No README data
npm WARN app No license field.
ホスト.
#コンテナを一時的に起動コンテナ内に入る(--rmで停止後削除する。コンテナ起動後、bashに入る)
docker-compose run --rm app /bin/bash
appコンテナ内.
# express-generatorでアプリケーションのひな形を生成
npx express-generator --view=ejs
下記の用に即abortingで拒絶されたら再度実行
destination is not empty, continue? [y/N]
aborting
二回目は質問で待ってくれるので y でエンター
destination is not empty, continue? [y/N] y
下記の様な各種ファイルが作成される。
create : public/
create : public/javascripts/
create : public/images/
create : public/stylesheets/
create : public/stylesheets/style.css
create : routes/
create : routes/index.js
create : routes/users.js
create : views/
create : views/error.ejs
create : views/index.ejs
create : app.js
create : package.json
create : bin/
create : bin/www
package.jsonに下記の内容を上書き
package.json
{"name":"uniq_app","version":"0.0.0","private":true,"scripts":{"start":"nodemon ./bin/www"},"dependencies":{"bcrypt":"^5.0.0","body-parser":"^1.19.0","connect-flash":"^0.1.1","cookie-parser":"~1.4.4","debug":"~2.6.9","ejs":"^3.1.5","express":"~4.16.1","express-ejs-layouts":"^2.5.0","express-generator":"^4.16.1","express-session":"^1.17.1","express-validator":"^6.7.0","http-errors":"~1.6.3","http-status-codes":"^2.1.4","method-override":"^3.0.0","mongoose":"^5.11.9","morgan":"~1.9.1","nodemon":"^2.0.6","passport":"^0.4.1","passport-local-mongoose":"^6.0.1"}}
app.jsに下記の内容を上書き
app.js
constcreateError=require('http-errors');constexpress=require('express');constpath=require('path');constcookieParser=require('cookie-parser');constlogger=require('morgan');constmongoose=require("mongoose");constindexRouter=require('./routes/index');constusersRouter=require('./routes/users');constinitTestController=require('./controllers/initTestsController');constapp=express();mongoose.connect(`mongodb://${process.env.DB_USER}:${process.env.DB_PASS}@mongo:27017/<アプリ名>_db`,{userNewParser:true});mongoose.set("useCreateIndex",true);// view engine setupapp.set('views',path.join(__dirname,'views'));app.set('view engine','ejs');app.use(logger('dev'));app.use(express.json());app.use(express.urlencoded({extended:false}));app.use(cookieParser());app.use(express.static(path.join(__dirname,'public')));app.get('/initTest',initTestController.test);/* 初期テストルーティング */app.use('/',indexRouter);app.use('/users',usersRouter);// catch 404 and forward to error handlerapp.use(function(req,res,next){next(createError(404));});// error handlerapp.use(function(err,req,res,next){// set locals, only providing error in developmentres.locals.message=err.message;res.locals.error=req.app.get('env')==='development'?err:{};// render the error pageres.status(err.status||500);res.render('error');});module.exports=app;
パッケージのインストール
appコンテナ内.
npm install
一度コンテナを抜ける
appコンテナ内.
#コンテナを抜ける(この仮コンテナは削除される)
exit
コンテナを再起動
ホスト.
docker-compose up
下記クリックで接続確認
http://localhost:8080/
mongooseとの接続確認
下記クリックでデータが作成されるか確認
http://localhost:8080/initTest
下記の様なログが出力されれば成功(docker-compose upしたターミナルで)
uniq_app_cnt | _id: 5fec5a6213a2fd002d89acca,
uniq_app_cnt | name: 'initTestUser',
uniq_app_cnt | age: 20,
uniq_app_cnt | __v: 0
uniq_app_cnt | }
uniq_app_cnt | 【ログ】--接続成功--【ログ】
mongodbの権限周り確認
ターミナルを別タブで開き、dbコンテナに入る
docker exec -it <アプリ名>_db_cnt bash
mongo
//mongodbに接続
show dbs
//何も表示されない。(認証でロックされていることを確認)
use admin
//adminデータベースへ接続
db.auth("root", "<パスワード>")
//1 と返れば認証成功
db.system.users.find().pretty()
//作成されたユーザー確認
以下の様な、データベースに対する各権限者が作成されていたらオッケー。
{"_id":"admin.root","roles":[{"role":"root","db":"admin"}]}{"_id":"uniq_db.read","roles":[{"role":"read","db":"<データベース名>_db"}]}{"_id":"uniq_db.owner","roles":[{"role":"dbOwner","db":"<データベース名>_db"}]}{"_id":"uniq_db.readWriteUser","roles":[{"role":"readWrite","db":"<データベース名>_db"}]}