Express Generator で作成されたファイルを触って Express を理解したい1：生成, yarn start, express.static, path.join

Express 4.x が対象です。`express-generator` による生成、`yarn start` の意味、`express.static()` の意味、`path.join()` の意味についてまで。記述しています。

「余談」とある項目はやや踏みこんだ内容になっています。必要に応じて読み飛ばしてくだい。(むしろ余談のほうが多いね…)

# 生成

下記のコマンドで `app_name` 以下に雛形を生成できる。

```
$ express app_name --view=pug
```

`--view=pug` を付けない場合、`views` 以下のファイルの拡張子が `.jade` になる。jade は pug に改名される予定(というか改名された)ので、今後は `.pug` に統一されていく(はず)。なので、これから `express` のアプリを作るなら `--view=pug` を付けて生成したほうが良さそう。

**注意** 2023/4 時点で pug のモジュールが古いままになっているようです。html そのままにしたい場合は下記にします。

```
$ express app_name --no-view
```

生成のコマンド名は `express` だけど、使用するには `express-generator` をインストールする必要がある。

```
$ npm install express-generator -g
```

または

```
$ yarn add global express-generator
```

# 実行

```
$ yarn start
```

ポート番号を指定した場合は、`PORT=8000 yarn start` とする。ポートを 8000 とした場合、`http://localhost:8000` でアクセスできる。プログラムが正常に起動していれば、下記のページが表示される。

<img src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F266247%2F8389c9ab-7a60-9055-def6-ccf760c1c19d.png?ixlib=rb-1.2.2&auto=format&gif-q=60&q=75&s=f25aa30c763746f5bfcaccc475857e38" width="60%"/>

## 余談：yarn start と package.json の関係

コマンドラインから `yarn start` とすると、`yarn` が `package.json` の `scripts` の中を見て、`start` に書かれているコマンドを実行する。ためしに `package.json` の中の `start` を `hogehoge` にかきかて、`yarn start` してみると…

```package.json
  ...
  "scripts": {
    "hogehoge": "node ./bin/www"
  },
  ...
```

```
$ yarn start
error Command "start" not found.
```

このように、エラーになる。そのかわり、`yarn hogehoge` で実行できる。`package.json` の `hogehoge` を `start` に戻すと、再び `yarn start` で実行できるようになる。

## 余談: yarn run

 `yarn run` とすると、`yarn` が `scripts` の中を見て、どの項目を実行したいのかと聞いてくる。

```
$ yarn run
yarn run v1.22.4
info Commands available from binary scripts: acorn, babylon, cleancss, mime, uglifyjs
info Project commands
   - hogehoge
      node ./bin/www
question Which command would you like to run?: 
```

ここで `start` と入力すると、`node bin/www` が実行される。

`express-generator` ではデフォルトで実行コマンド名を `start` としているので、`yarn start` として実行しているにすぎず`start` というコマンドが `yarn` にあるわけではない(らしい)。そのため、先のように自分でコマンド名を別のものに書き換えたり、追加することもできる。

たとえば、デバッグ時は 8000 番ポートにして、本番は 80 番にしたいみたいなときに、下記のように書いておく。

```
  "scripts": {
    "start": "node ./bin/www",
    "test": "PORT=8000 node ./bin/www"
  },
```

これで `yarn test` または `yarn run test` とすると、8000 番ポートで実行され、`yarn start` または `yarn run start` だと 80 番ポートで実行できるようになる。

`add` のような `yarn` に元からあるコマンドを `"start"` と同列に追加したい場合は、`yarn add` ではダメで `yarn run add` とする必要がある(そんなことするのかは謎)。

## 余談: node bin/www と yarn start の違い

コマンドラインから `node www/bin` としてもアプリの実行はできる。この場合、`package.json` の `start` に書かれている内容は無視される。

前述のように、引数などを `package.json` の `start` に書いている場合は、`node bin/www` としたときと `yarn start` で挙動が異なることになる。

# 生成ファイル

`express generator 4.16.1` で `--view=pug` オプション付きで生成した場合、下記のファイル構造になる。

```
project_dir
├── app.js          # アプリのメインファイル
├── bin            
│   └── www            # yarn start 時に node bin/www として実行されるファイル
├── package.json       # ライブラリ等の依存関係やバージョン情報を格納したファイル
├── public          # static なファイルを置くフォルダ
│   ├── images         # http://localhost:8000/images 
│   ├── javascripts    # http://localhost:8000/javascripts 
│   └── stylesheets    # http://localhost:8000/stylesheets
│       └── style.css  # http://localhost:8000/stylesheets/style.css
├── routes          # router (ミドルウェア) 置き場
│   ├── index.js       # http://localhost:8000/ (トップページ)
│   └── users.js       # http://localhost:8000/users
└── views           # テンプレートファイル置き場
    ├── error.pug      # エラー時のテンプレート
    ├── index.pug      # index.js 用のテンプレート
    └── layout.pug     # index.pug や error.pug に読みこまれるテンプレート
```

## static ファイル

下記の内容で `index.html` を作り、`public` 直下に置く。

```.html
<!DOCTYPE html>
<head></head>
<body>
    <h1>Hello World</h1>
</body>
</html>
```

この状態で `PORT=8000 yarn start` すると、`http://localhost:8000` で表示される内容は下記のようになる。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/d6548b35-556d-2134-fd4f-74a0f5a1197f.png)

`public` 以下にファイルを置くと、ブラクザから直接そのファイルにアクセスできるようになる。ただし、アクセスするパスは `/public` ではなく `/` になる。

たとえば、上記のように `public/index.html` を作成した場合、http://localhost:8000/public/index.html ではなく、http://localhost:8000/index.html としてアクセスできる。

http://localhost:8000 のように、ファイル名を指定しない場合は、デフォルトで `index.html` が読みこまれる (ファイルが存在していれば)。

このような動作をするのは、`app.js` に下記のコードがあるため。

```.js
app.use(express.static(path.join(__dirname, 'public')));
```

この記述で、`public` 以下のディレクトリにあるファイルは、`http://localhost:8000/` 以下にあるものとして、そのままクライアントに返される。

**単純な web サーバとして使うだけなら、public 以下にファイルを置くだけで十分に機能する。**

## 余談：static ディレクトリの変更、追加

ディレクトリ名を `public` 以外にすることもできるし、複数のディレクトリを指定することもできる。

```.js
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'video')));
app.use(express.static(path.join(__dirname, 'img/hoge')));
```

この場合、いずれのディレクトリにあるフィルも、URL としては `/` にあるものとしてアクセスされる。`dat/img/hoge.txt` も`http://localhost:8000/hoge.txt` でアクセスできる。

static に指定されている複数のディレクトリに同名のファイルがある場合は、`app.js` 内で、より先にある行の記述が優先される。たとえば、上のコードの状態で実行したとして、

```
public
├── images
└── index.html
    └── style.css

video
├── index.html
└── hoge.txt

img
└── hoge
    └── hoge.txt
```

このように、`public/index.html` と `video/index.html` が存在している場合に、`http://localhost:8000` にアクセスした場合、`public/index.html` がブラウザに返される。同様にして、`http://localhost:8000/hoge.txt` へアクセスした場合は `video/hoge.txt` が返される。

## 余談：同じパスを示す app.use() は先にあるものが優先される

**先に書いた行が優先**というルールは、他の `app.use()` の記述にも適用される。

```.js
var indexRouter = require('./routes/index');
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
```

`app.use('/', indexRouter)` という記述は、`http://localhost:8000` にアクセスがあった時に、`./routes/index.js` の中を実行することを意味している。`public/index.html` が存在しなければ、`app.use('/', indexRouter)` が実行されて、下記のページが表示される。

<img src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F266247%2F8389c9ab-7a60-9055-def6-ccf760c1c19d.png?ixlib=rb-1.2.2&auto=format&gif-q=60&q=75&s=f25aa30c763746f5bfcaccc475857e38" width="60%"/>

しかし、`public/index.html` が存在していると、先にある `static` の記述のほうが優先されて、`public/index.html` の内容が表示される。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/d6548b35-556d-2134-fd4f-74a0f5a1197f.png" width="60%"/>

`app.use()` の順序を逆にすると、優先順位も逆になる。

```.js
var indexRouter = require('./routes/index');
app.use('/', indexRouter);
app.use(express.static(path.join(__dirname, 'public')));
```

この順序の場合、'public/index.html' が存在していても、`app.use('/', indexRouter)` の記述が優先される。

`public/index.html` を置くと、このような混乱が起こるので、分かっていてやるのでなければ避けたほうが良いと思う。もし分かってやってるなら、むしろ `app.use('/', ... )` の記述を消したほうが良い、また `public` ディレクトリ以下に、他の `app.use()` で使っているパス名と同じパスがあると、同様の混乱がおこりやすいため、それも避けたほうが良い(と思う)。

## 余談：path.join(__dirname, '') の意味

下記の `index.html` を `public/index.html` に置いておき、

```
<!DOCTYPE html>
<head></head>
<body>
    <h1>Hello World</h1>
</body>
</html>
```

`app.js` 内の、

```.js
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
```

この記述を、

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

app.use('/', indexRouter);
```

このように変更し、アプリケーションのフォルダ内で `yarn start` してみる。ブラウザで `http://localhost:8000` にアクセスすると、下記のページが表示される、はず。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/d6548b35-556d-2134-fd4f-74a0f5a1197f.png" width="60%"/>

しかし、bin ディレクトリに入ってから、下記のようにしてアプリケーションを実行し `http://localhost:8000` をブラウザで開くと…

```
$ cd bin
$ node www
```

<img src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F266247%2F8389c9ab-7a60-9055-def6-ccf760c1c19d.png?ixlib=rb-1.2.2&auto=format&gif-q=60&q=75&s=f25aa30c763746f5bfcaccc475857e38" width="60%"/>


このようになる、これは `public/index.html` が無視されて `app.use('/', indexRouter);` が実行されていることを意味する。

なぜこうなるかというと、**node コマンドは実行したフォルダをカレントディレクトリとして認識する**からのようだ。つまり、`bin` ディレクトリ内で node を実行すると、`public` ディレクトリを探す起点(ルート)が `bin` ディレクトリになってしまい、`bin/public` を探しにいってしまうから、ということ。

`bin` ディレクトリ内に `public` ディレクトリが存在しない場合、`app.use(express.static('public'));` の記述は無視されて(マッチせずに) `app.use('/', indexRouter);` のほうが実行される。これは `bin` が実行時のカレントディレクトリになり、`express.static('public')` という記述は、`bin/public` ディレクトリを指すようになるためである。

試しに、次のように書きかえると、`bin` ディレクトリ内で `node www` を実行しても、`http://localhost:8000` へアクセスしたときのブラウザには "Hello World" が表示されるようになる。

```.js
app.use(express.static('../public'));
```

ここで、下記のように `path.join()` を使った記述に戻すと、`bin` ディレクトリ内で `node www` とした場合でも "Hello World" と表示されるようになる。

```.js
app.use(express.static(path.join(__dirname, 'public')));
```

これは、`app.js` (`path.join()` が記述されているファイル)があるディレクトリが `__dirname` に自動的に入れられるため。つまり、`path.join(__dirname, name)` とすることで、`__dirname` と `name` が連結され、結果的に `app.js` のあるディレクトリを起点(ルート)として `public` ディレクトリを探してくれる。

アプリのフォルダ以外からアプリを起動することは、そうそう無いと思うのだけど、たまーにそういうことをしてハマることがある。特段の事情がない限り、**実在するファイルやフォルダのパスを指定するときは `path.join(__dirname, name)` としたほうが良い**。

一方で、__dirname を `app.use` の第一引数や、外部参照するURLのパスの指定に使うとだいたいバグになる(分かっていたらやらないはず…)。

後述するように、`routes` ディレクトリ内にあるファイルで `path.join(__dirname, name)` を使用したときは、`routes` ディレクトリがカレントディレクトリ (`__dirname`) になる。`app.js` のあるディレクトリではない。これはよく間違う。

また、`app.use(path, ... )` の一つ目の引数で指定する `path` は、実在するファイルのパスを指定する引数ではない。この引数で `path.join()` を使うのは(分かってやっているのではない限り)誤りである。

## 余談：path でファイル名操作

`path` は `join()` 以外にも、ファイル名を操作する便利な機能がいろいろある。詳しくは以下を参照のこと。

- https://nodejs.org/api/path.html