Expressでプロジェクトを作成する時は基本express-generatorを使っている。yarn initからexpressをインストールしてExpressプロジェクトを作成しようとした時に、そういえばどうしてたっけ?となったので、改めてexpress-generatorがやっていることを再確認して書きとめておこうと思います。
NodeJSの基本的なことなので内容は特に難しくはないです。
bin/www
bin/wwwはサーバー起動のファイルですね。実際にyarn startのnpmスクリプトはnode ./bin/wwwとなっています。
さて、bin/wwwの中身ですが次のようになっています。
// bin/www
var app = require('../app');
var debug = require('debug')('express-app:server');
var http = require('http');
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
var server = http.createServer(app);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
アプリのメイン処理はapp.jsでモジュール化しているので、それを読み込んでvar server = http.createServer(app);でHTTPサーバーを用意しています。
実はhttpモジュールを使わなくてもapp.listen(port)でサーバーを起動することができます。ただ、socket.ioやHTTPS(実際はhttpsを使う)を利用を考えると、結局httpモジュールを使うことになるかとは思います。
process.env.PORTは本番環境で環境変数PORTにポート番号を設定していた時にそのポートでサーバーが起動するようにしています。無ければ3000番のポートで動きます。開発環境でlocalhost:3000がデフォルトなのはここによるものですね。
normalizePortは環境変数の設定が正しいか見ているだけです。文字列でもOKで、設定がダメならfalseが返ってくるのでサーバーが起動せずにエラーになるだけですね。
app.set('port', port);はちょっと用途がわからないですね。ポート番号を定数で持つようにしていますが、どこにも使ってないので必要なさそう。もしかしたらモジュール内部で必要なのかもしれないです。
server.on()で各イベントにイベントハンドラーを設定しています。
エラーイベントは、EACCES (Permission denied)とEADDRINUSE (Address already in use)の場合だけそれぞれのメッセージをログに出力し、それ以外の場合は例外を投げています。
リスナーイベントは動作しているURLを出力するようになっています。
app.js
app.jsはサーバーのメイン処理が記載されています。express-generatorを使う時もここは触るので、自動生成頼りにしている機能だけ見ていきます。
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
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.use('/', indexRouter);
app.use('/users', usersRouter);
app.use(function(req, res, next) {
next(createError(404));
});
app.use(function(err, req, res, next) {
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
コード上部はミドルウェア群の読み込みですね。
-
http-errors
- エラーオブジェクトを作ってくれるミドルウェア。
-
path
- ファイルやディレクトリのパスの扱いでよく使うやつですね。
-
cookie-parser
- ヘッダー情報からクッキーを拾ってくれる。
-
morgan
- HTTPリクエストのログを出力してくれるミドルウェア。
app.setは通常は定数の定義ですが、いくつかのプロパティは特別な意味を持ちます。viewsやview engineはその一例です。
viewsは描画ファイルを格納するディレクトリパスを設定するプロパティで、自動生成ではpath.join(__dirname, 'views')としてプロジェクト配下の/viewsディレクトリを指定しています。
view engineはレンプレートエンジンの拡張子を設定します。Expressの標準はPug(元Jade)です。
app.useは使用するミドルウェアをマウントします。読み込んだミドルウェアはここで設定しています。
express.json()はJSONを扱うミドルウェアでExpress v4.16.0から利用できます。それ以前はbody-parse.json()を使う必要があります。
express.urlencoded({ extended: false })はURL解析、クエリパラメータとかを扱います。これもExpress v4.16.0以降で利用可です。
express.static(path.join(__dirname, 'public'))は静的ファイルの置き場を指定しています。
あとがき
express-generatorがやっていることは最低限ですが毎回書くのは面倒なところを自動生成してくれます。始めにも言いましたが、一から用意しようとした時に何必要だっけ?となるのでありがたい。というかexpress-generatorで作成して必要な形に直す方が早いですね。