Expressでnode_modulesのbootstrapをクライアント側から静的ファイルとして読み込む



# 方法
アプリ起動の起点となるjsファイル（多くの人は`app.js`が該当するかと）に以下のような1行を加える。
```javascript:JavaScript
app.use('/bootstrap', express.static(path.join(__dirname, 'node_modules/bootstrap/dist')));
```
こうすることで`node_modules/bootstrap/dist`下のファイルに対して`/bootstrap`から始まるパスを指定すればアクセスすることができるようになる。
```html:HTML
<!-- Bootstrap core CSS -->
<link href="/bootstrap/css/bootstrap.min.css">
<!-- Bootstrap JS Bundle with Popper -->
<script src="/bootstrap/js/bootstrap.bundle.min.js"></script>
```
# 何をしているのか（解説）
Expressの`app.use()`は、特定のパスに特定のミドルウェア（関数やモジュール）をマウントさせるメソッドである。

そして、引数にパスを指定しない場合はデフォルトで`/`（ルート）が指定されるので、リクエストが行われるたびに、そのミドルウェアが実行される。
> ###### app.use([path,] callback [, callback...])
> Mounts the specified middleware function or functions at the specified path

（[公式ドキュメント](https://expressjs.com/en/4x/api.html#app.use)より）

`express.static()`に関しては、指定したパスにあるファイルを静的ファイルとして提供するメソッドと認識しておけば問題なさそう。

## express-generatorの例
`express-generator`で生成した雛形の`app.js`を参照すると、より何をしているかの理解がしやすい。
```javascript:app.js
/* 前半省略 */

// リクエストのたびに実行したいもの（ロガー等）はパス指定なし
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// public内にある静的ファイルもこの呼び方
app.use(express.static(path.join(__dirname, 'public')));

// ルーターは特定のパスを指定して後続のコールバック関数として呼んでいる
app.use('/', indexRouter);
app.use('/users', usersRouter);

/* 後半省略 */
```

# 実践検証
実際に上記の方法で動くのかをハンズオン形式で検証。

## 環境
[Node.js](https://nodejs.org/en)
```
$ node -v
v20.9.0
```
npm
```
$ npm -v
10.0.0
```
## 1.express-generatorで雛形を生成
今回の作業ディレクトリを`express-bootstrap-test`として以下のコマンドを順に実行。
```
$ npx express-generator --view=ejs express-bootstrap-test

$ cd express-bootstrap-test

$ npm install

$ npm start
```
http://localhost:3000/ にアクセスしていつものの画面が表示されることを確認する。

## 2.Bootstrapのインストール
```
$ npm install bootstrap
```

## 3.app.jsの修正
`app.use()`の1行を下記のように`app.js`に追加。
```javascript:app.js
/* 前半省略 */

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('/bootstrap', express.static(path.join(__dirname, 'node_modules/bootstrap/dist')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

/* 後半省略 */
```

## 4.index.ejsの編集
`veiews/index.ejs`を修正。
BootstrapのCSSとJSが機能していることを確認するために、モーダルウィンドウを作成してみる。

```html:index.ejs
<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
    <!-- BootstrapのCSS読み込み -->
    <link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
  </head>
  <body>
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>

    <!-- 公式ドキュメントからモーダルのボタンとウィンドウをコピペ -->

    <!-- Button trigger modal -->
    <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
      Launch demo modal
    </button>

    <!-- Modal -->
    <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
          </div>
          <div class="modal-body">
            ...
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary">Save changes</button>
          </div>
        </div>
      </div>
    </div>
    <!-- BootstrapのJS読み込み -->
    <script src="/bootstrap/js/bootstrap.bundle.min.js"></script>
  </body>
</html>
```
※Qiitaがejsに対応していないので若干表示がおかしくなっているのは許してください

## 5.アプリを起動して確認
```
$ npm start
```

http://localhost:3000/ にアクセスした時に、Bootstrap特有の青ボタンがあり、押すとアニメーションモーダルが表示されれば成功。

# おわりに
昨今は専らREST APIの作成に使われているExpressなので正直Bootstrapの読み込み方法なんて需要はなさそうでしたが、まあ自分が困ったことあったので誰かしらの役には立つだろうと言うことで…。

また、解説のために改めて公式ドキュメントを読むと学びがたくさんあって興味深かったです（KONAMI）。

Express好きなので、Express単体でのWebアプリケーションの作成方法や、流行りのReactとの組み合わせ方なんかについて今後も記事を書いていけたらいいなと思います。

# 参考
- [Serving static files in Express - Express](https://expressjs.com/en/starter/static-files.html)
- [express.static() - Express](https://expressjs.com/en/4x/api.html#express.static)
- [app.use() - Express](https://expressjs.com/en/4x/api.html#app.use)