node.js + Express + mongoose で掲示板アプリを作ってみる
前回の「node.js+mongodbで掲示板アプリを作ってみる」ではnode.jsとmongodbのnative driverだけでapp.jsのみで実装しました。
- 独自ルーティング
- デザインもHTMLも混在
- dbのドキュメント構造がわかりにくい
- その他Webアプリケーションで共通の処理手順をそのつど書く
ため、ソースに手を加えるたびに煩雑になってきます。
目次
- 目次
- 0.MVCモデルの掲示板アプリを作る
- 1. 新しいnodeをインストール
- 1. Expressアプリケーションのプロジェクトを作る
- 2. mongooseをインストールして使えるようにする
- 3. mongoose接続とSchema定義を追加する
- 4. 投稿画面を追加
- 5.トップページを書き込み一覧表示にする
- 6. まとめ
0.MVCモデルの掲示板アプリを作る
今回はExpressとmongooseを使用してきれいな構造&少ない手順にします。
Express node.jsのスタンダードなwebアプリケーションフレームワーク
mongoose node.jsとmongodbのためのODMパッケージ
前提となる開発環境はMacOS High Sierra
1. 新しいnodeをインストール
新しくプロジェクトを開始するときはやったほうがいいでしょう
$ nodebrew install-binary stable
Fetching: https://nodejs.org/dist/v9.0.0/node-v9.0.0-darwin-x64.tar.gz
######################################################################## 100.0%
Installed successfully
$ nodebrew use 9.0.0
$ nodebrew migrate-package 8.8.1
$ npm upgrade
やったこと
- nodebrewをつかって新しいバージョンのnodeをインストール
- インストールされた最新を use
- 以前使用したバージョンのグローバル環境に入れていたパッケージを新しいバージョンに migrate
- パッケージをアップグレード
1. Expressアプリケーションのプロジェクトを作る
npm で express-generator が入っているとexpressコマンドで簡単にひな形をつくってくれます。
$ express -h Usage: express [options] [dir] Options: --version output the version number -e, --ejs add ejs engine support --pug add pug engine support --hbs add handlebars engine support -H, --hogan add hogan.js engine support -v, --view <engine> add view <engine> support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade) -c, --css <engine> add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css) --git add .gitignore -f, --force force on non-empty directory -h, --help output usage information
オプション含めてどんなviewエンジンを利用するか決めます。今回は標準のpugを使います。 てきとうなディレクトリで
$ express -v pug kakiapp create : kakiapp create : kakiapp/package.json create : kakiapp/app.js create : kakiapp/public create : kakiapp/routes create : kakiapp/routes/index.js create : kakiapp/routes/users.js create : kakiapp/views create : kakiapp/views/index.pug create : kakiapp/views/layout.pug create : kakiapp/views/error.pug create : kakiapp/bin create : kakiapp/bin/www create : kakiapp/public/javascripts create : kakiapp/public/images create : kakiapp/public/stylesheets create : kakiapp/public/stylesheets/style.css install dependencies: $ cd kakiapp && npm install run the app: $ DEBUG=kakiapp:* npm start $ cd kakiapp $ npm install ~ $ DEBUG=kakiapp:* npm start > kakiapp@0.0.0 start /Users/haranaga/node/kakiapp > node ./bin/www kakiapp:server Listening on port 3000 +0ms
やったこと
- expressプロジェクトのファイルを指定したディレクトリ内に全部そろえてくれる
- 作られたディレクトリに移動してローカルに依存パッケージをnpm install
- サーバーを起動
ブラウザでの動作
- localhost:3000/ にアクセスすると「Express Wellcome to Express」と表示される
できたファイル群
├── app.js アプリ本体 ├── bin/ 起動スクリプト置き場所 ├── node_modules/ 依存パッケージ ├── package-lock.json いつもの ├── package.json いつもの ├── public/ 静的ファイルを公開する場所 ├── routes/ コントローラーの置き場所 └── views/ Viewテンプレートの置き場所
これでnode.js+Expressの環境が整いました。
生成されたアプリケーションは / アクセスで上記の表示 /users へのアクセスで
respond with a resource
とtextを表示するだけのシンプルなものです。
デフォルトで設定される機能
- path: ローカルディレクトリのパス文字列のコントロール
- serve-favicon: faviconの設定を任せる
- morgan: ログ記録
- cookie-parser: cookie制御
- body-parser: POST Bodyを読み込んでくれる
app.js
var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var index = require('./routes/index'); var users = require('./routes/users'); var up = require('./routes/up'); var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'pug'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', index); app.use('/users', users); // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app;
2. mongooseをインストールして使えるようにする
プロジェクトのディレクトリ内で
$ cd kakiapp $ npm install mongoose --save
3. mongoose接続とSchema定義を追加する
ソースを変更
app.js
var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var index = require('./routes/index'); var users = require('./routes/users'); var up = require('./routes/up'); // 投稿画面 var app = express(); //// 1. mongoose connection var mongoose = require('mongoose'); // mongoose 利用 mongoose.Promise = global.Promise; mongoose.connect('mongodb://localhost/app1', {useMongoClient: true}); // 接続 //// end 1 //// 2. mongoose model 定義 // Schema 定義 var postSchema = mongoose.Schema({ kakikomi: String, name: String }); // model var Post = mongoose.model('Post',postSchema); // Post modelを router moduleで共有する app.set('Post',Post); //// end 2 // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'pug'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', index); app.use('/users', users); //// 3. 投稿画面 app.use('/up', up); //// end 3 // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app;
やったこと コメント //// 1 〜 3 を追加しました。
- mongoose でmongodb接続
- mongoose スキーマ定義と掲示板データを保存するコレクションのモデル定義
- route /up を投稿画面として追加
4. 投稿画面を追加
先にapp.js
でapp.use('/up',up);
を追加しました。
├── routes │ ├── index.js │ ├── up.js * 投稿画面コントローラー │ └── users.js └── views ├── error.pug ├── index.pug ├── layout.pug ├── up.pug * 投稿フォーム └── up_post.pug * 投稿後ページ
これらを追加します
routes/up.js
var express = require('express'); var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { res.render('up'); }); router.post('/', (req,res,next) => { var Post = module.parent.exports.get('Post'); var post = new Post(); post.kakikomi = req.body.kakikomi; post.name = req.body.name; var promise = post.save(); promise.then((doc) => { res.render('up_post',doc); }); }); module.exports = router;
やったこと
- /up/ の GETリクエストで view/up.pug を表示する
- /up/ への POSTリクエストで mongodbの postsコレクションへデータを追記して追加したデータをviewに渡して、view/up_post.pug を表示する
投稿フォーム
views/up.pug
extends layout block content h2 投稿フォーム form(action="/up" method="POST") p 内容: textarea(name="kakikomi" style="width:100%;height:60px;") p 名前: input(type="text" name="name") div(style="margin-top:30px;") button(type="submit") 投稿
views/up_post.pug
extends layout block content h2 投稿しました p #{kakikomi} p #{name}
ブラウザでの動作
- /up で フォームを表示
- 投稿ボタンで「投稿しました」画面を表示
5.トップページを書き込み一覧表示にする
トップページ / を書き込まれたデータの一覧表示画面にします.
コントローラー routes/index.js
と ビューの views/index.pug
を編集します
routes/index.js
var express = require('express'); var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { var Post = module.parent.exports.get('Post'); Post.find().sort({_id:-1}).exec((err, posts) => { res.render('index', { title: '書き込み一覧', posts: posts }); }); }); module.exports = router;
views/index.pug
extends layout block content h2= title style ul {list-style:none;} .name {font-size:0.8em;color:#555;} .box {border:1px solid #888;padding:20px;margin-bottom:40px;} ul each post in posts li .box div #{post.kakikomi} p(class="name") #{post.name}
やったこと
- / のGETリクエストでmongodbのコレクションpostsから全件データを_id降順で取得します。
- 取得したデータをviewのposts変数に渡して views/index.pugを表示します
ブラウザでの動作
- トップページにアクセスすると先に投稿した書き込みを一覧表示します。
6. まとめ
- express-generator パッケージのコマンドライン express で雛形を作成
- mongodbを利用したオブジェクトモデリング mongoose をnpmインストール
- app.js にmongoose利用の設定と接続
- app.js に mongoose Schemaの Post モデルを定義(kakikomiフィールドとnameフィールド)
- 投稿画面用に /up ルートを追加
- 投稿画面機能を routes/up.jsに フォーム表示とPOSTでデータ保存の処理
- トップページ / を 書き込み一覧ページとしデータを取得して表示する
検討事項
プロジェクトの各機能をファイルとディレクトリに分けて構造化するにあたって、セッション共通の依存性注入(Dependency Injection)をexpressでどのように実現するかいろいろと方法があるようです。
今回はエントリーポイントの app.js内で mongoose.model を app.set()し、呼び出される各モジュールで module.exports.get()して参照する方法で実装しました。
以上でnode.js + Epress + mongoose を使って簡単な掲示板サービスができました。