Express の静的ファイルの扱いがわからなくて困ったので整理する

# 困ったこと

Express の静的ファイルの扱いについてあまり理解していなくても、試行錯誤すれば何とか問題は解決できてしまっていました。<br>
そのせいで、同じ問題が発生したときに、そのたびに毎回試行錯誤しないといけなくて、かなり効率が悪いと感じていたので、ちゃんとおさらいしてみようと思います。

# 解説

以下のようにサンプルアプリを作ってみます。<br>

![2024-05-04-14-58-40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601978/cb3fd6bf-bc72-6ed0-5bdd-eb012250dc2a.png)

```js
const express = require("express");
const app = express();

app.use(express.static("./public"));
app.use("/static/images", express.static("./assets/images"));

app.listen(3000);
```

ソースコードを分解すると次の 3 つになります。（app.listen()は除く）

- express
- app.use()
- express.static()

## そもそも Express って何だっけ？

<!-- 当たり前のように Express を使っていましたが、いざ Express のことを説明しようとしても、「Node.js のフレームワークだよ」や「ルーティングなどが簡単にできるよ」といった解像度の低いことしか言えないので、ここもちゃんと整理しておきます。<br> -->

公式サイトには以下のように記述されています。<br>

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

[Express 公式サイト](https://expressjs.com/ja/guide/using-middleware.html)より

どうやら Express の正体は基本的にはミドルウェア関数の呼び出しのようです。<br>
ただし、必ずしもミドルウェア関数の呼び出ししかしていないわけではなく

```js
app.listen(3000);
```

など、ミドルウェア関数の呼び出し以外の機能もあります。<br>が、メインの機能はミドルウェア関数の呼び出しと考えて大丈夫そうです。

## ミドルウェア関数とは

ミドルウェア関数とはリクエストとレスポンスの処理の間に実行される関数のこと。

![2024-05-03-23-50-46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601978/8c54996f-a0a4-e6f7-a4bc-136cee76a14c.png)


Express を使ってこのリクエストとレスポンスの間でどのミドルウェア関数を呼び出すかを定義します。<br>
例えば、ルーティングもミドルウェア関数の呼び出しの一つです。<br>

```js
app.get("/hello", (req, res, next) => {
  res.render("hello.ejs");
});
```

<br>
ここでは、localhost:3000/helloにアクセスする（GETリクエストを送る）と、

```js
(req, res, next) => {
  res.render("hello.ejs");
};
```

というミドルウェア関数を呼び出すよ、ということを定義しています。

## app.use()は何をしている？

app.use()の書き方は大きく別けて次の 4 パターンがあります。

|                      | ミドルウェア関数が一つ | ミドルウェア関数が複数 |
| :------------------- | :--------------------: | :--------------------: |
| **引数にパスがない** |           1            |           2            |
| **引数にパスがある** |           3            |           4            |

```js
// 1：引数がパスがない、ミドルウェア関数が一つ
// 全てのリクエストに対し実行される
app.use((req, res, next) => {
  console.log("hi");
  next();
});

// 2：引数がパスがない、ミドルウェア関数が複数
// 全てのリクエストに対し実行される
app.use(
  (req, res, next) => {
    console.log("hi");
    next();
  },
  (req, res, next) => {
    console.log("bye");
    next();
  }
);

// 3：引数がパスがある、ミドルウェア関数が一つ
// 特定のパスにのみ実行される
app.use("/hello", (req, res, next) => {
  console.log("hello");
  next();
});

// 4：引数がパスがある、ミドルウェア関数が複数
// 特定のパスにのみ実行される
app.use(
  "/world",
  (req, res, next) => {
    console.log("hello");
    next();
  },
  (req, res, next) => {
    console.log("world");
    next();
  }
);
```

すべてに共通しているのはミドルウェアがあることで、app.use()の役割はそのミドルウェアを登録して使えるようにすることです。<br>

引数にパスがなければ全てのリクエストに対して登録したミドルウェアを実行し、<br>パスがあればそのパスのみに対して登録したミドルウェアを実行します。<br>

## express.static()とは

express.static()とは、静的ファイル（画像、CSS ファイル、JavaScript ファイルなど）を提供するためのミドルウェア関数のこと。<br>
逆に言うと、express.static()がないとプロジェクトの静的ファイルは利用できません。

# 実際にアクセスしてみる

```js
const express = require("express");
const app = express();

app.use(express.static("./public"));
app.use("/static/images", express.static("./assets/images"));

app.listen(3000);
```

サーバーを起動して、URL でアクセスしてみます。

> ① http://localhost:3000/luffy.png

![2024-05-04-14-31-35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601978/9fbb89fb-d663-e45b-888e-844b2bd2c643.png)


> ② http://localhost:3000/static/images/zoro.png

![2024-05-04-14-32-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601978/8019b8db-0c5f-f25b-43bd-36ae0cd0f245.png)

> ③ http://localhost:3000/static/images-2/nami.png

![2024-05-04-16-04-59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601978/a0642438-f6bf-5868-be22-a9538e3634a2.png)

① と ② はアクセスできましたが、③ はアクセスできませんでした。<br>
③ にアクセスできなかったのは、/static/images-2 のリクエストに対するミドルウェアの呼び出しが app.use()で定義されていないからですね。

```js
const express = require("express");
const app = express();

app.use(express.static("./public"));
app.use("/static/images", express.static("./assets/images"));
// 追加
app.use("/static/images-2", express.static("./assets/images-2"));

app.listen(3000);
```

このように定義すれば、アクセスできるようになります。

> ③ http://localhost:3000/static/images-2/nami.png

![2024-05-04-16-12-51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601978/15052441-2511-ac88-9785-0c2b00d1caba.png)

# まとめ

- Express
- app.use()
- express.static()

について整理しました。<br>
実際のプロダクトのコードはこんなにシンプルではないので、応用する必要がありますが、今回は基本的なことだけをおさらいしてみました。<br>

# 備考

express.static()は app.use()を使わなくても技術的には

```js
app.get("/*", express.static("public"));
```

のように書けば可能ですが、app.use()を使うのが一般的なようです。<br>
違いは、app.use()の場合はすべての HTTP メソッド（GET, POST など）に対して適用されるが、<br>上のコードの場合は GET メソッドのみにしか適用されない、という違いがあります。