【node.js】expressで必ずやる設定などまとめ
はじめに
expressでWebアプリを開発してみて次回以降もやるであろうなという作業をまとめておく
といっても自分の趣味レベルで作ったものなので大規模にも考慮してとかは全然できていません。
とはいえせっかくやったのでできる範囲で案件で使えるようにと考えながらやりました
開発環境作成
プロジェクトの初期化と必要なモジュールをインストール
mkdir -p dev-app/public; cd dev-app/public; npm init; npm install express http body-parser ejs path domain express-session config express-domain-middleware log4js mysql connect-mongo mongoose date-utils sprintf-js pm2 --save;
必要なモジュールについては各々だいぶ違ってくると思うけど自分が実際に触ってみて毎回入れるだろうなというもの
express、http ・・・Webサーバーでつかう
body-parser ・・・Postパラメータを扱うのにつかう
ejs ・・・テンプレートエンジンとしてつかう
express-session ・・・セッション管理で使う
connect-mongo mongoose ・・・セッションの保存先をmongodbとするのに使う
config ・・・定数を管理するのに使う
log4js ・・・ログを管理するのに使う
mysql ・・・DBにMySQLを使用するので使う
pm2 ・・・Webサーバーのプロセス監視に使う
必要なディレクトリをつくる
まぁ、自分ルールですが。。。
cd dev-app; mkdir public/config public/controllers public/libs public/models public/views; mkdir logs
最終的に以下のようなプロジェクトの構成を想定しています
開発の場合(本番はappフォルダとする)
. ├── dev-app ├── public │ ├── app.js │ ├── config # 設定ファイルまとめる │ ├── controllers # コントローラまとめる │ ├── libs # 独自のモジュールとかまとめる │ ├── models # モデルまとめる │ ├── views # テンプレートまとめる │ ├── node_modules │ ├── package.json │ ├── process.json │ └── logs ├── access.log ├── error.log ├── system.log
設定ファイルを作る
DBの接続先など環境によって異なる設定ファイルと全環境共通の定数などのファイルになります
config/default.json ・・・全環境共通
config/develop.json ・・・開発環境
config/production.json ・・・本番環境
config/develop.json
{ "server": { "port": 3000 } "db": { "host": "localhost", "user": "dbuser", "password": "dbpassword", "database": "dbname", "timezone": "utc", "dateStrings": "date" }, "mongodb": { "dsn": "mongodb://localhost:27017/dev-app" }, "log": { "appenders": [ { "category": "access", "type": "dateFile", "filename": "/path/to/dev-app/logs/access.log", "pattern": "-yyyy-MM-dd" }, { "category": "app", "type": "dateFile", "filename": "/path/to/dev-app/log/system.log", "pattern": "-yyyy-MM-dd" }, { "category": "error", "type": "dateFile", "filename": "/path/to/dev-app/dev-app07/log/error.log", "pattern": "-yyyy-MM-dd" }, { "type": "console" } ] } }
共通処理を作る
今のところはMySQLとログ処理あたりが毎回使うだろうということでコレ
libs/db.js
var app = require("express")(); var mysql = require("mysql"); var config = require("config"); var sprintf = require("sprintf-js").sprintf; var log = require('../libs/log.js'); var date = require('date-utils'); var express = require("express"); module.exports = { connection: null, connect: function() { this.connection = mysql.createConnection(config.db); this.connection.connect(); }, errorHandler: function(err) { if (err) { throw new Error(err); } }, query: function(query, params, callback) { log.app(query + "\n[" + params + "]"); this.connection.query(query, params, callback); }, getOne: function(query, params, callback) { var self = this; this.query(query, params, function(err, rows) { self.errorHandler(err); callback(rows.length < 1 ? null : rows[0]); }); }, getAll: function(query, params, callback) { var self = this; this.query(query, params, function(err, rows) { self.errorHandler(err); callback(rows); }); }, insert: function(table, data, callback) { var self = this; var query = "insert into " + table + " set ?"; this.query(query, data, function(err, result, fields) { self.errorHandler(err); callback(result.insertId); }); }, update: function(table, data, id, callback) { var fields = "", params = []; for (var k in data) { fields += k + " = ?,"; params.push(data[k]); } fields = fields.substr(0, fields.length - 1); params.push(id); var self = this; var query = "update " + table + " set " + fields + " where id = ?"; this.query(query, params, function(err, result) { self.errorHandler(err); callback(result); }); } };
libs/log.js
var log4js = require('log4js'); var config = require("config"); log4js.configure(config.log); var logApp = log4js.getLogger("app"); var logAccess = log4js.getLogger("access"); var logError = log4js.getLogger("error"); exports.access = log4js.connectLogger(log4js.getLogger("access"), { level: log4js.levels.INFO }); exports.app = function(message) { logApp.info(message); }; exports.error = function(message) { logError.info(message); };
メイン処理を作る
アプリ本体の実装です。
何度も使ったわけではないので違くなる可能性もあるかもしれませんが。まぁ同じようなコードになるんじゃないかと思っています
404ページやエラーページなどもここで実装。
app.js
// Node.js各種モジュール読み込み var app = require("express")(); var http = require("http").Server(app); var path = require("path"); var domain = require("domain"); var config = require("config"); var session = require("express-session"); var mongoose = require("mongoose"); var mongoStore = require("connect-mongo")(session); var bodyParser = require('body-parser'); // オリジナルモジュール読み込み var db = require("./libs/db.js"); var log = require('./libs/log.js'); // アクセスログ開始 app.use(log.access); // テンプレートエンジン設定 app.set("views", path.join(__dirname, "views")); app.set("view engine", "ejs"); // セッション管理 mongoose.connect(config.mongodb.dsn); app.use(session({ secret: "secret key", resave: true, saveUninitialized: true, store: new mongoStore({ mongooseConnection: mongoose.connection }) })); // DB接続 db.connect(); // POSTパラメータ取得できるように app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); // 500エラー(Node.js) app.use(function(req, res, next) { var reqd = domain.create(); reqd.on("error", function(err) { showServerError(res, err); }); reqd.run(next); }); // URL振り分け app.use("/", require("./controllers/index.js")()); app.use("/detail", require("./controllers/detail.js")()); // 404エラー app.use(function(req, res, next) { showNotFound(res); }); // 500エラー(ミドルウェア) app.use(function(err, req, res, next) { showServerError(res, err); }); // HTTPサーバー開始 http.listen(config.server.port, function() { console.log("listening on *:" + config.server.port); }); // NotFound処理 var showNotFound = function(res) { res.status(404); res.render("404"); }; // サーバーエラー処理 var showServerError = function(res, err) { log.error(err); res.status(500); res.render("500"); };
Controller/Model/Viewの各ひな形
実際に機能を追加するときはこんな感じのファイルを作ってゆくイメージ。
内容はDBからデータを取得して表示するだけです。
controllers/index.js
var express = require("express"); var test = require("../models/test.js"); module.exports = function() { var router = express.Router(); router.get("/", function(req, res, next) { test.getAll() .then(function(rows) { res.render("index", { items: rows }); }); }); return router; };
model/test.js
var db = require("../libs/db.js"); var sprintf = require("sprintf-js").sprintf; module.exports = { getAll: function() { return new Promise(function(resolve, reject) { var query = sprintf( "select * from test" ); db.getAll(query, [], function(row) { resolve(row); }); }); } };
開発用と本番用の起動スクリプトを作る
開発ではwatchオプションをつけてファイルが更新されたら再起動。
本番ではファイルを同期しおわったタイミングで再起動するスクリプトで。
process.json
{ apps: [ { // 開発 name: "dev-app", script: "./app.js", watch: true, env: { "NODE_ENV": "develop" }, },{ // 本番 name: "app", script: "./app.js", env: { "NODE_ENV": "production" } } ] }
開発起動
cd /path/to/dev-app; ./node_modules/pm2/bin/pm2 start process.json --only dev-app;
本番起動
cd /path/to/app; ./node_modules/pm2/bin/pm2 start process.json --only app
pm2にはデプロイ機能とかもあるみたいだけど今のところは
rsyncなりgitのリポジトリからPULLするなりで大丈夫かな。。。
ちゃんと考えろと言われそうだけど。。。