現役エンジニアがNode.jsを解説! 〜Expressのミドルウェア〜

公開日:2022-09-18
JavaScript
Node.js

https://www.youtube.com/watch?v=WeqY1r9T2to

この講座はYouTubeで動画形式でも用意しています。合わせてご覧ください。

目標

Expressのミドルウェアを理解する。

Webフレームワークにおけるミドルウェアとは

WebフレームワークがHTTPリクエストを受け取り、個々の処理へ到達する前に共通処理を行う仕組みです。

Expressにおけるミドルウェア

https://expressjs.com/ja/guide/using-middleware.htmlhttps://expressjs.com/ja/guide/using-middleware.html

Express は、それ自体では最小限の機能を備えたルーティングとミドルウェアのWebフレームワークです。Express アプリケーションは基本的に一連のミドルウェア関数呼び出しです。

ExpressというWEBフレームワークはルーティングとミドルウェアから成り立っていて、ミドルウェアの組み合わせでフレームワークが完成しています。

Expressでのミドルウェアの使用例

https://expressjs.com/ja/guide/using-middleware.htmlhttps://expressjs.com/ja/guide/using-middleware.html

アプリケーション全体でミドルウェアを使用する際の例です。

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public')));

app.useというメソッドの中にミドルウェアを代入することで使用可能です。
上記の例の場合を上から順に説明します。

  • Express内でJSONを解析できるようにするためのミドルウェア
  • WEBで使われるURLエンコードというエンコードの仕組みを使えるミドルウェア
  • 静的ファイル(CSS、JavaScriptなどの動的ではないファイル)を配信する際の設定が適用されるミドルウェア

上記の例のようにどのミドルウェアを使用するかapp.useを用いて宣言的に選択できます。
またアプリ全体では無く、細かくルーター、パス・エラー処理などの単位での適用も可能です。

Expressミドルウェアを自作する

https://expressjs.com/ja/guide/writing-middleware.htmlhttps://expressjs.com/ja/guide/writing-middleware.html

先ほどまでのミドルウェアの例はExpress内で定義されているものになります。
アプリの開発していく上で共通処理が有効な手段になります。

自作のミドルウェア関数 myLogger

// ミドルウェア関数 myLogger
const myLogger = (req, res, next) => {
  console.log("LOGGED");
  next();
};

最後に next メソッドを呼んでいる点が重要です。これは後続処理につなげるかどうかを判断する関数になります。

このミドルウェアを適用する場合を説明します。
下はHello World!を返す前にmyLoggerのミドルウェアを適用する例になります。
定義したmyLogger関数をGETの第二引数に渡すことで適用ができます。

app.get('/', myLogger, (req, res) => {
  res.send('Hello World!')
})

これではパス単位での設定であるためGET/にのみmyLoggerが適用されている状態です。

POST/にも適用させる場合には同じように第二引数にmyLoggerを渡します。

app.post('/', myLogger, (req, res) => {
  res.send('Got a POST request')
})

このようにミドルウェアを適用するタイミングを細かく設定できます。

アプリケーション全体に適用させたい場合は、最初の説明のようにapp.useを用いればOKです。

app.use(myLogger);

ミドルウェア関数を作成し、どの範囲で適用させるか選択することで共通処理を行えます。

以下のようにミドルウェアを何個も重ねることができます。

app.get('/', myLogger, myLogger,(req, res) => {
  res.send('Hello World!')
})

myLoggerを重ねるのはあまり意味がないのです。しかし、他のミドルウェアと組み合わせて上げることで同じようなコードを何度も書かずに済むと言った利点があります。

このようにミドルウェアが重なっていく場合にnextを呼ぶことで次のミドルウェアに処理が移ります。
ミドルウェアの中にif文があり、「ある条件の時は次の処理に進まず、次のミドルウェアに進む」ような時にはnextを呼ぶ事で次の処理に進むイメージです。

自作のミドルウェア関数 requestTime

const requestTime = (req, res, next) => {
  req.requestTime = Date.now();
  next();
};

第一引数のreqにrequestTimeというプロパティを追加して、そこに Date.now() で現在時刻を取得し代入しています。
reqresという変数はそれぞれrequestとresponseの略で、リクエストが来た際の各ミドルウェアの処理の中で共通して使われる変数です。
何らかの値を取得して後続処理へ伝えたい場合に使われる書き方です。
今回は時刻をとっていますが、会員制のサイトなどでユーザーの会員IDや名前などの情報を取得するミドルウェアの場合にも使用されます。

自作のミドルウェア関数 myCustomLogger

const myCustomLogger = (options) => (req, res, next) => {
  if (options) {
    console.log(`LOGGED ${options}`);
  } else {
    console.log("LOGGED");
  }
  next();
};
app.get('/', myCustomLogger("hogehoge"), (req, res) => {
  res.send('Hello World!')
})

動的な、何かしら設定を渡してあげて処理を分岐させたい場合の書き方がコチラになります。
上記では、optionsの値をif文を使って分岐して、何かあればconsole.logの出力にoptionsを含めます。
このように関数を返す関数を作ることで、ミドルウェア適用時に値を渡せる、設定可能なミドルウェアが作れます。

自作のミドルウェアを複数適用する場合

次のようにミドルウェアを適用したい順に、引数に渡していけば良いです。

app.get('/', myLogger, requestTime, myCustomLogger("hogehoge"), (req, res) => {
  console.log(req.requestTime);
  res.send('Hello World!')
})

ライブラリとして利用可能なExpressミドルウェア

https://expressjs.com/en/resources/middlewarehttps://expressjs.com/en/resources/middleware

よく使われるような処理のミドルウェアはExpressの公式ページで紹介されています。

例)HTTPリクエストロガー morgan

https://expressjs.com/en/resources/middleware/morgan.htmlhttps://expressjs.com/en/resources/middleware/morgan.html

const morgan = require("morgan");
app.use(morgan());

上記のように設定し、HTTPリクエストをログで出力できアクセス解析などの用途で使用できます。

まとめ

今回はExpressのミドルウェアについて詳しく説明しました。
次回はMySQLについて説明していきます。