Expressフレームワークの基本的な使い方(Node.jsその2)

Express は、Web アプリケーションとモバイル・アプリケーション向けの一連の堅固な機能を提供する最小限で柔軟な Node.js Web アプリケーション・フレームワークです

https://expressjs.com/ja/
目次
  1. 初期設定とExpressのインストール
  2. ルーティング処理
  3. 静的ファイルの返却
  4. 動的ファイルの返却
  5. ミドルウェアについて
  6. Express Generator

初期設定とExpressのインストール

プロジェクトフォルダを作り、package.json作成
*package.jsonのエントリーポイント(”main”)は、index.jsから、慣例的に使われているapp.jsに変更しました

npm init -y

設定値(本番環境と開発環境では異なる設定を使うことが多々あります)は環境変数に保存して利用できます
環境変数はprocess.envオブジェクトに格納されます

Node.jsで定義済み又は慣例的に決まっている環境変数
コードで利用(例:process.env.NODE_PATH)
実行する(例:NODE_ENV=production node && node index.js)

変数名説明
PORTポート番号
NODE_PATH「;」で分割されたディレクトリパスリストを指定して、モジュール検索先を指定
NODE_ENV‘production’ または ‘development’ を指定する
NODE_DEBUGデバッグ出力したいモジュール名をカンマ区切りで指定します

環境変数のかわりに「.env」ファイルを作り環境で異なる値をまとめておくことができます

dotenvモジュールを使うと(npm install dotenv
カレントディレクトリに置かれた .env ファイルを読み込み、そこに記述されたキー&バリューのペアprocess.env 経由で参照できます
*APIキーやデータベースのパスワード等.env ファイルにまとめてGitHubのパブリックに上げる場合「.gitignore」に追加してリモートにpushしないファイルとして指定します

require('dotenv').config();

必要があれば、VSCodeでデバッグ実行するための設定ファイル(launch.json)を作成

  1. デバッグしたいファイルを開いて、左横のアイコン「実行とデバッグ」をクリックします
  2. 「launch.jsonファイルを作成します」をクリックします
  3. 「Node.js」を選択します
    選択した項目で自動でlaunch.jsonを作成してくれます
  4. launch.jsonファイルに”envFile”も追加すると実行するときに環境変数を与えて実行します
    .envファイルががあるとき

“program”はデバッグ実行時に起動するプログラムのパスを指定します
*${workspaceFolder}は、VSCodeで開いているフォルダのパス

"program": "${workspaceFolder}/app.js",
"envFile": "${workspaceFolder}/.env"

必要があれば:ブロジェクトにESLintを設定する
*VSCodeのESLintプラグインはインストール済み

$ npm install eslint --save-dev

#ウィザードに沿って設定ファイルを作成します。
$ npx eslint --init

作成された.eslintrc.js"extends": "eslint:recommended"(推奨のルール)と必要に応じて、"rules": {}を追加します
個人的にconsoleは使いたいので

module.exports = {
    "env": {
        "browser": true,
        "commonjs": true,
        "es2021": true,
        "node": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
    "ecmaVersion": 12
    },
    "rules": {
        "no-console": "off"
    }
};

Expressのインストール

npm install express --save

app.jsファイルを作成

ターミナルからnode app.jsを実行した、ブラウザからlocalhost:3000(127.0.0.1:3000)にアクセスするとHello Worldが表示されます
app.listenがサーバー起動処理で、app.listenの第一引数はポート番号の指定です

// Expressサーバーパッケージを読み込み
const express = require("express");
const app = express();
//環境変数が取得できない場合は3000ポートを使う
const port = process.env.PORT || 3000

//ルーティング
app.get("/", (request, response) => {
  response.end("Hello World");
});

// Expressサーバー起動 3000はポート
app.listen(port, () => {
  console.log('3000ポートでサーバー起動');
});

nodemonをインストールします
nodemonはデフォルトでプロジェクトフォルダ内の全JavaScriptファイルやJSONファイル(node_modulesフォルダは除く)を監視して、コードを変更して保存するたびにサーバーアプリケーションが自動で再起するようにします

nodemonは、ディレクトリ内のファイルの変更が検出された場合にnodeアプリケーションを自動的に再起動することで、node.jsベースのアプリケーションの開発を支援するツールです。

nodemonはコードや開発方法に追加の変更を必要としません。nodemonを使用するには、スクリプトを実行する際にコマンドラインでnodeという単語を置き換えてください。

npm install nodemon --save-dev

package.jsonのscriptsセクションに登録されているstartコマンドの内容を以下のように書き換えます
npm start(コードを変更するたびにサーバー起動する必要がなくなります)

"scripts": {
  "start": "nodemon app.js"
},

*現在は npmでインストール時の –saveは不要ですが、念の為^^;

Webサーバーはクライアントから送られてくるリクエストを受け付け、クライアントへレスポンスを返却します

ルーティング処理

リクエストに対して、処理を振り分けします

METHOD:リクエストメソッドを指定(get・post ・put・ delete)
PATH : URLルーティングを指定
*完全一致
*正規表現にすることもできます
参考:https://expressjs.com/ja/guide/routing.html
コールバック関数 : PATHにマッチしたときの動作を指定(レスポンスに何を返すか等)

app.METHOD(PATH, コールバック関数)

備考
コールバック関数(関数の引数に渡す関数)、現在実行している処理とは異なるタイミングで実行されます(非同期処理)
(ネットワーク通信やファイルの読み書きなど)時間がかかる処理は、キュー(関数実行の待ち行列)に非同期で実行したい関数として登録し、自身は次の処理に進みます
非同期処理にはコールバック関数が引数として指定されていたり、Promise形式で処理結果を返す場合などがあります

requestはブラウザからのリクエストに関するオブジェクト
responseはブラウザに返すレスポンスに関するオブジェクトです

// ルーティング設定
app.get('/', (request, response) => {
  
});

responseメソッド
responseオブジェクトのメソッドは、レスポンスをクライアントに送信し、リクエストとレスポンスのサイクルを終了します

  • response.download() :ファイルのダウンロードのプロンプトを出します
  • response.end() :レスポンスプロセスを終了します
  • response.json() :JSON レスポンスを送信します
  • response.jsonp() :JSONP をサポートする JSONレスポンスを送信します
  • response.redirect(): リクエストをリダイレクトします
  • response.render() :ビュー・テンプレートをレンダリングします
  • response.send() :渡した値に対応したcontent-typeを設定してレスポンスを送信します
    *オブジェクトを渡しすと内部でjsonに変更されます
  • response.sendFile :ファイルを送信します
  • response.sendStatus() :レスポンスのステータスコードを設定し、レスポンス本文として送信します

パスパラメーター
URLパス指定の際に「:(コロン)」に続けて任意の文字列を指定すると、その指定した文字列にあたる部分のパスの内容がrequest.paramsオブジェクトのプロパティとして登録された状態でルーティング処理に入ります
プロパティの名前は、URLパス指定の際に「:(コロン)」に続けて指定した文字列となります
request.params.idというプロパティが登録されます

app.get('/blog/:id', (request, response) => {
   console.log(request.params.id)
});

クエリパラメーター
GETリクエストの際に、urlの末尾に「?」に続くキーと値「key=val」形式のデータですが、?以降パラメーターはルーティングのパスには影響を与えません(無視されます)
(例:http://127.0.0.1:3000/blog/?page=3)
値を取得するには

request.query.key(キー名)

備考
複数のURLパラメータを送信する場合は?page=3&hoge=valのように&で繋げます
日本語のようなマルチバイト文字列を送信する場合はURLエンコードで「%と英数字」だけで構成された文字列に変換してURLパラメータに指定します
ちなみに、POSTパラメータの解析には、以前はbody-parserのインストールが必要でしたが、Expressバージョン4.16.0からはexpress.urlencodedで解析できます
POSTリクエストでは、request.queryではなくrequest.bodyに入ります

静的ファイルの返却

ファイルをそのままレスポンスとして返却します

express.static関数の戻り値のミドルウェアを利用します
*ミドルウェアの詳細は後で

express.static(root, [options])

プロジェクトフォルダ内にpublicフォルダを作成します

ブラウザから静的ファイル(CSSやJavaScriptや画像など)へのアクセスがあった場合に、publicフォルダを検索して見つかった場合はファイルの中身を返すようにします
*publicフォルダから相対的なファイルを検索するため、publicはURLの一部に含まれません
例:http://localhost:3000/css/style.css

app.use(express.static('public'));

express.static関数でURLの一部プレフィックス(hoge)を作成する
例:http://localhost:3000/hoge/css/style.css
*express.static関数に指定するパスは、プロセスを起動するディレクトリに対して相対的です
別のディレクトリから実行する場合は、絶対パスを使用する方が安全です

const path = require("path");

app.use('/hoge', express.static(__dirname + '/public'));

第二引数の [options]について

動的ファイルの返却

HTMLファイル(HTMLの雛形)にソースコードを埋め込み、HTMLファイルを生成して、レスポンスとして返却します

テンプレートエンジン(EJS)設定します
*テンプレートエンジンにはその他、PugやJadeなどがあります

npm install ejs --save

プロジェクトフォルダ内にviewsフォルダを作り、その中にindex.ejsファイルを作成

指定したテンプレートエンジンはExpressの内部で必要な時に自動的にrequireするので、別途をrequireする必要はありません
response.renderの第二引数に渡したオブジェクトの内容は、テンプレート内で実行するJavaScriptから参照できます
第一引数は、テンプレートファイルのパス(viewsからの相対パス)です

app.set('view engine', 'ejs');

app.get('/', (request, response) => {
  response.render('./index.ejs')
});
//第二引数がテンプレートに渡すデータ
response.render(path,data)

テンプレートの基本構文

  • <% ~ %>はJavaScriptコードを実行
  • <%= ~ %>は値の出力(エスケープ処理あり)
  • <%- ~ %>は値の出力(エスケープ処理なし)
    *基本的にはインクルードに限定して使う
  • <%# ~ %>はコメント

include()を使い、共通する部分のファイルを切り出し、切り出したファイルをテンプレートで読み込んで利用出来ます
第一引数:インクルードしたいテンプレートのファイルパス(拡張子の「.ejs」は省略できる)
第二引数:インクルードするテンプレートに引き渡すデータ
戻り値は、生成されたHTML

<%- include('./_partial', {param:'param'}) %>

app.localsオブジェクトに登録された値や関数は、テンプレートから参照できます

app.locals.title = 'My App'

console.log(app.locals.title)
//  'My App'

テンプレートエンジンで使える値や関数を渡す
ちなみに res.render(path,data)のdataは locals.dataと同じ

res.locals.method =()=>{}

ミドルウェアについて

Expressはリクエストのルーティングとレスポンスの整形をします
ミドルウェアは、リクエストやレスポンスに追加処理をする関数です

ミドルウェア関数
next() 関数は次の処理を呼ぶために必要です
next()に引数を渡して実行するとエラーを発生させます

function (req, res, next) {
 // 次の処理を呼ぶためのnext()
  next()
}

//エラー処理がある場合
function ( err, req, res, next) {
 // 次の処理を呼ぶためのnext()
  next()
}

ミドルウェアはapp.useに引き渡して利用します
*全てのリクエストに対して処理されます

app.use(function (req, res, next) {
 // 処理内容
  next()
})

/user/:idのパスにミドルウェアの関数をマウントしています。
/user/:id パス上のリクエストに対して実行されます

app.use('/user/:id', function (req, res, next) {)
  // 処理内容
  next()
})

ミドルウェアを書く場所は重要です
上から順番に実行されます

express.Router
機能単位でルーターをモジュールとして作成しミドルウェアとして使います
router.use()でルーターレベルのミドルウェアをロードします

ルーターファイルを作成します(routes/index.js)

const express = require('express')
//ルーティング処理用のオブジェクトを作成
const router = express.Router()

//useからの相対パスになるので この場合はhome/index
router.get('/index', (req, res) => {
   response.render('./index.ejs')
})
module.exports = router;
//app.jsに追加
//切り出したrouterを読み込みますrequire('./routes/index.js')
app.use('/home', require('./routes/index.js'))

*useの第一引数のパスは前方一致(**で始まるデータを探す検索)です
省略した場合は'/'と同じ

Express Generator

express-generator を使うとスケルトンを素早く作成できます

npm i express-generator -g

expressコマンドを使えるようにります(オプションで、ejs・hbs・pugの指定などが出来ます)

ディレクトリー構造は

.
├── app.js
├── bin
│   └── www
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes
│   ├── index.js
│   └── users.js
└── views
    ├── error.pug
    ├── index.pug
    └── layout.pug