3
0

More than 1 year has passed since last update.

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

Last updated at Posted at 2020-05-05

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 でアクセスできる。プログラムが正常に起動していれば、下記のページが表示される。

余談:yarn start と package.json の関係

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

package.json
  ...
  "scripts": {
    "hogehoge": "node ./bin/www"
  },
  ...
$ yarn start
error Command "start" not found.

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

余談: yarn run

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

$ 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.jsonstart に書かれている内容は無視される。

前述のように、引数などを package.jsonstart に書いている場合は、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

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.txthttp://localhost:8000/hoge.txt でアクセスできる。

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

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

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

img
└── hoge
    └── hoge.txt

このように、public/index.htmlvideo/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) が実行されて、下記のページが表示される。

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

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.htmlpublic/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 にアクセスすると、下記のページが表示される、はず。

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

$ cd bin
$ node www

このようになる、これは 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) とすることで、__dirnamename が連結され、結果的に 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 でファイル名操作

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

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0