n_hachiのメモ

メモです。

Express web framework (Node.js/JavaScript)の勉強 part4、 directory-structure

The generated projectを読み進める。
日本語版の資料をかいつまんだような内容なので、詳細を知りたい人はこっちを見ることを勧める。

ディレクトリ構造

expressおよび関連するコマンドを実行することで以下のようなディレクトリ構造になる。
注意:一部コマンドの出力結果は省略している。

$ express express-locallibrary-tutorial --view=pug --git

   create : express-locallibrary-tutorial/
   create : express-locallibrary-tutorial/public/
   create : express-locallibrary-tutorial/public/javascripts/
   create : express-locallibrary-tutorial/public/images/
   create : express-locallibrary-tutorial/public/stylesheets/
   create : express-locallibrary-tutorial/public/stylesheets/style.css
   create : express-locallibrary-tutorial/routes/
   create : express-locallibrary-tutorial/routes/index.js
   create : express-locallibrary-tutorial/routes/users.js
   create : express-locallibrary-tutorial/views/
   create : express-locallibrary-tutorial/views/error.pug
   create : express-locallibrary-tutorial/views/index.pug
   create : express-locallibrary-tutorial/views/layout.pug
   create : express-locallibrary-tutorial/.gitignore
   create : express-locallibrary-tutorial/app.js
   create : express-locallibrary-tutorial/package.json
   create : express-locallibrary-tutorial/bin/
   create : express-locallibrary-tutorial/bin/www

   change directory:
     $ cd express-locallibrary-tutorial

   install dependencies:
     $ npm install

   run the app:
     $ DEBUG=express-locallibrary-tutorial:* npm start

$ cd express-locallibrary-tutorial
$ npm install

up to date, audited 123 packages in 2s

6 packages are looking for funding
  run `npm fund` for details

3 low severity vulnerabilities

To address all issues, run:
  npm audit fix --force

Run `npm audit` for details.

$ tree -F
.
├── app.js
├── bin/
│   └── www*
├── node_modules/
│   ├── たくさんのサブディレクトリおよびファイル(表示は省略)
│   ︙
│
├── package.json
├── package-lock.json
├── public/
│   ├── images/
│   ├── javascripts/
│   └── stylesheets/
│       └── style.css
├── routes/
│   ├── index.js
│   └── users.js
└── views/
    ├── error.pug
    ├── index.pug
    └── layout.pug

405 directories, 4784 files

nodemon

今後express-locallibrary-tutorial以下を編集していくのだが、編集するたびアプリを起動し直すのは面倒である。
この煩わしさを解消するnodemonというツールがある
下記コマンドを実行し、nodemonをインストールする。

npm install -g nodemon

もし開発環境以下(?)にインストールしたい場合は-gの代わりに--save-devを使う。
nodemonを使ってアプリを起動すると、ファイル編集時に自動で反映してくれるためアプリの再起動の手間が省ける。

nodemon ./bin/www

package.json

package.jsonはアプリの依存情報を保持する
nodemonをインストールした直後のpackage.jsonの内容を以下に示す。
タイミングによってバージョン番号は変化するので、次同じコマンドを実行しても下記と同じ結果になるとは限らない(と思う)。

{
  "name": "express-locallibrary-tutorial",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www",
  },
  "dependencies": {
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1",
    "nodemon": "^2.0.7",
    "pug": "2.0.0-beta11"
  }
}

npm run-script

参考:npm-run-script | npm Docs
npm run-scriptコマンドによってpackage.json"scripts"オブジェクトに記載されたコマンドを実行できる。
上記の場合、"scripts"というオブジェクトの中にキー"start"、バリュー"node ./bin/www"が存在しているため、npm run-script startと入力するとnode ./bin/wwwが実行される。
引数なしの場合は、以下のように利用可能なコマンドが表示される。

$ npm run-script
Lifecycle scripts included in express-locallibrary-tutorial:
  start
    node ./bin/www

以下npm run-script -hの実行結果からわかるようにrun-scriptrunというエイリアスを持っている。
入力文字の短さからnpm runを使ったほうが良いだろう(npm run-scriptのほうがnpm runより良いケースってあるのだろうか?)。

$ npm run-script -h
npm run-script <command> [-- <args>]

aliases: run, rum, urn

scripts

package.json内のscriptsを以下のように編集する

"scripts": {
  "start": "node ./bin/www",
  "devstart": "nodemon ./bin/www",
  "serverstart": "DEBUG-express-lcallibrary-tutorial:* npm run devstart"
}

"start"で始まる行の末尾にカンマ(,)が追記されていることに注意する。
以降はnpm run serverstartと実行することでnodemonでアプリが実行される

/bin/www

ファイル/bin/wwwはアプリケーションのエントリポイントである。
下記コードによって、後述するapp.jsを呼び出す。

#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('../app');

残りのコードでapp.jsを使用するHTTPサーバの設定、ポートの設定、リスニングやサーバエラーのレポートを開始するらしい。

/app.js

expressアプリケーションオブジェクト(appという名前が慣習である)を生成、アプリケーションおよびミドルウェアの設定、appをエクスポートする。
上記した/bin/wwwはエクスポートされたappを使用する。

var express = require('express');
...
var app = express();
...
module.exports = app;

app.jsの詳細を見ていく。
最初にいくつかのnodeライブラリを取り込む。

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

その後require()を使い、routesディレクトリからモジュールを取り込む。
これらファイルには、特定のURLへのリクエストを受け取った場合の処理が書かれている。

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

次にappオブジェクトをexpressモジュールを使い作成し、view(template)エンジンを設定する。
エンジンの設定は2段階に分かれる。
最初に値'views'を使ってテンプレートが保存されているフォルダを指定する。
その後、値'view engine'を使いテンプレートライブラリを指定する。
参考:Using template engines with Express

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

続いてapp.use()を使いミドルウェアライブラリをリクエストハンドリングの連鎖に加える。

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);

最後はエラーとHTTP404レスポンスを返却するためのミドルウェアを追加する。

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// 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');
});

以上でexpressアプリケーションオブジェクト(app)の設定は完了する。
最後にmodule.exportsappを追加することで、/bin/wwwからアクセスできるようにする。

module.exports = app;

Router

/routes以下にはusers.jsindex.jsがあるが基本的に処理が同じなので、ここではindex.jsのみを見ていく。
最初にexpressモジュールをロードしexpress.routerオブジェクトを取得する。

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource!!');
});

module.exports = router;

Views(templates)

views(template)は/viewsディレクトリに格納され、拡張子.pugを持つ。
Response.render()メソッドは特定のテンプレートを描画するために使われ、レスポンスとして結果を送信する。
以下/routes/index.jsの例ではルートレンダーはテンプレートindex(index.pug)を使い、テンプレート変数titleを渡している。

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

上記コードに対応するテンプレート/views/index.pugを以下に示す。
ここでは数変titleの箇所に値Expressが挿入される。

extends layout

block content
  h1= title
  p Welcome to #{title}

まとめ

個人的にこの章は今までメインでやってきたこと(組み込み寄り)と比べて多くの差異を感じた。
特に重要に違いを感じた点を以下にまとめる。

  • npm expressでアプリケーションの雛形が作成されること。
  • app.jsではルーターやビューの設定、その他ライブラリのインポートなどを行うこと。
  • routesディレクトリ以下にはルーター(router)が配置され、ルーターは特定のパスへの要求を受理したときに対応する処理へのルーティングを担当すること。
  • 変数resrenderメソッドを使うとテンプレートを使って描画する。
  • viewsディレクトリにはビュー(view)のテンプレートが配置され、これらは描画の際に使われること。

これらは繰り返し実装しないと身につかなさそうだ。