この記事は、今更ながらExpress.jsを勉強しなおした時のログです。
具体的なやり方は、いろいろと優秀なドキュメントがありますゆえ、あまり書いていません。
一方で、判断に迷ったり困ったところをメモしています。
(似たようなパッケージがあって、どれを使えばいいのかの比較など)
とりあえずの環境構築までを書いています。
Express.jsを使っている方は、目次を適当に見て、気になったところだけ見ていただけると何か見つかるかもしれません。
Express.jsを勉強中という方は、ざっと読み通しておくと、なにか救われるかもしれません。
くらいの温度感で書いています。
Express.jsって
Webアプリフレームワーク。
StateOfJavaScript2021を見た感じ、JavaScript系のWebフレームワークでは、一番使われているっぽい。
なぜ学習するのか?
ウェブアプリを作るときに、Laravelを使うことが多かった。
ただ、ミニマムにAPI用のバックエンドサーバーを作りたいとき、Laravelはいくらか豪華すぎるように思えていた。
あと、使う言語をTypeScriptに統一できれば幸せだと感じていた。
そのため、Express.jsを習得することにした。
(今思えば、Firebaseをいじり倒す、でも良かったかもしれない)
学習する前の状態
- Express.jsは過去に仕事で何度か触ったことがあった
- フロントエンドエンジニアとしてNode.jsにはよくお世話になっている
- ただし、サーバーとしてのNode.jsはよくわからぬところも多い
- TypeScriptとかはよく書く
- ApacheやNginxでサーバーを立てたりもある
くらい。
MacBookPro M1Proチップ。
Node.jsのv16が入っている状況。
Express.jsを把握するのが目的なので、ひとまずDockerとかは使わずにいく。
まず初めにしたこと
Express.js本家のサイトに目を通した。
https://expressjs.com/ja/
非常にシンプルなので、1時間あったら大体読める。
大枠を把握した。
オススメ。
あとは、ハンズオンNode.jsを並行して読んでいた。
https://www.oreilly.co.jp/books/9784873119236/
Node.jsを中心に、JavaScriptの歴史からEventEmitterとか基礎知識、Express.jsやサクッとNext.jsも登場していて、良書だった。
オススメ。
あとは、てきとうに作業フォルダを作った。
なんとなくどんなことができるか大枠は掴めた。
余談:それはNode.jsの機能かExpressの機能か
どこまでがNode.jsの機能で、どこからがExpress.jsが提供しているものか分かりにくいから難しい、と感じていた。
例)path
モジュールはnode.jsに付随する機能なのか?
など、node.jsが提供しているものか、expressが提供しているものか、わかりにくいところが最初難しいと感じていた。
ここらへんの疑問は、ハンズオンNode.jsでだいたい解消されたのは助かった。
(Node.jsを知ったことで、Expressの担ってる役割が見えてきた)
あと、Node.jsとExpressのドキュメントに、モジュールがいろいろ載ってあるので、それをたぐってもいい。
Expressのインストール、generatorに任せた
Expressの公式サイトより。
https://expressjs.com/ja/starter/installing.html
二つ方法があるようだ。
-
npm init
した後で、パッケージとしてexpressを入れる - Expressジェネレーターを使う
2を使うと、いい感じに雛形を用意してくれるらしい。
どんな感じに書くのか知りたいので、Expressジェネレーターを使った。
(サクッといい感じに最低限必要なファイルを作ってくれるから助かった)
ちょっとハマったのだが、
npm install express-generator -g
でグローバルにインストールしようとしたら、書き込み権限がないみたいな感じに怒られた。
いろいろnpmのアップデートなど試して、
sudo npm install express-generator -g
なら通りそうだったが、別にグローバルにインストールしたいわけじゃない。
ちょっと考えて、
npx express
でジェネレーターを使った。
オプションをいろいろ試して、吐き出されるディレクトリ構成を見つつ、とりあえずAPI用のものをサクッと立てたければ、
npx express --no-view --git
でええんじゃないかと思った。
Expressの基本的な機能、ルーティングとミドルウェアとテンプレートエンジンできてる?
なにができるかは公式ドキュメントの通りなので割愛。
ガイドを読みながら手を動かせば理解できた(使えるとは言っていない)。
https://expressjs.com/ja/guide/routing.html
キモとなるのは「ルーティング」と「ミドルウェア」の二つ、という感触を得た。
- ルーティングでPOSTやGETなどリクエストを捌く
- ミドルウェアで、任意のリクエストに対し、処理を実行したり返したりする
あとは、テンプレートエンジンも重要な要素だろう。
が、今回作りたいのはAPIサーバーなのでざっと読み通すだけにしておく。
bin/wwwファイルとapp.jsはそれぞれ何をしている?
binディレクトリ内に、wwwというファイルがあった。
package.jsonのscriptsによると、まずはじめにnodeでwwwというファイルを読んでいるらしい。
中身を見ると、どうやらサーバーを起動する処理が集まっていた。
一方で、Expressにまつわる記述は、app.jsに集まっていた。
アプリケーション自体はapp.jsで構築して、wwwでそれを呼び出し、サーバーを組み立てているっぽいことを把握。
wwwを使うのは、慣習的なモノだろうか?(Laravelでも見かけたし)
あとはルーティングとかはroutesディレクトリに。
ビューはviewsディレクトリに。
静的に配信するファイル群は、publicディレクトリに。
ここら辺は見たままだ。
デバッグについて、Express自身のログも出せるよ
DEBUG=express:* node index.js
でExpressのデバッグログも出せるらしい。
随時、活用していく。
余談だが、デバッグクライアントを起動できるインスペクタ--inspect
スイッチが、node.jsについていることを知った。
https://nodejs.org/ja/docs/guides/debugging-getting-started/
デプロイについて
デプロイ時にはプロセス・マネージャーを一緒に入れるといいらしい。
あとでしっかり調べる。
https://expressjs.com/ja/advanced/pm.html
開発しやすいよう、自動で再起動してくれるnodemonも入れておく
node www
にて、サーバーを起動した後、コードを変更して保存しても変更が適応されない。
変更を反映するためには、nodeサーバーの再起動が必要なのだった。
それをええ感じにやってくれるのがnodemon
というパッケージらしい。
npm i -D nodemon
でインストールした後、
package.jsonのscripts
に
”dev”: “nodemon www”
を追加した。
開発の際は、npm run dev
でサーバーを起動することにする。
TypeScriptってExpress.jsで使えるの?
昔、Node.jsがTypeScriptに対応していないから、Denoを使いたい。
みたいな話を聞いたことがあった。
が、ExpressでTypeScriptを使う記事は検索すればいっぱい出てくる。
使えてるやないかーい笑
ここらへんの情報を収集し、整理した。
- 普通に@types/nodeとか@types/expressは存在する
- 昔2018年ごろ?はもっと面倒だったぽい
- CommonJSとESModuleらへんの面倒に巻き込まれてる?
- TypeScriptで書く場合はビルド時に変換されるのでESModuleでもよさげ?
今、Node.jsは頑張ってESModuleも使えるようにしているらしい。
(mjsという拡張子について調べるとよさげ)
あとは、requireは同期型ロード、importは非同期にロードされ実行されることを理解した。
改めて、TypeScriptをExpress.jsに入れる
これは検索すれば山ほど記事が出てくるので、ざっくりとだけ書いておく。
- typescriptとか@types/nodeとか必要なパッケージをインストール
-
tsc --init
でtsconfig.jsonを作成 - 適当にtsconfig.jsonの設定を変えたり、package.jsonのscriptsを整理したり
途中、`tsc -p’オプションをよく見かけて、気になったから調べた。
使用するtsconfig.jsonを指定できるらしい。
tsconfig.production.jsonとか使ってやったり、複数のtsconfig.jsonが出てくるときに使うといいとのこと。
なお、tsconfigの推奨設定は、以下にまとまっていたし、そのまま使うのもありだろう。
https://github.com/tsconfig/bases
TypeScriptをどうビルドして実行するか? 本番環境、開発環境
ts-node
というパッケージを使うと、JITで変換しながら実行してくれるらしい。
https://github.com/TypeStrong/ts-node
--transpile-only
オプションをつけたら本番でもオーバーヘッドを気にせず使えるとのこと。
とはいえ、なんか気持ちが悪いので、本番環境では普通にビルドして実行したい。
(そして時間のあるときにオーバーヘッドを検証したい)
ググった結果、distに吐き出させて実行しているのが多い印象だった。
先人にならい、tsc src/index.ts
で、dist
ディレクトリにビルドしたものを出力してもらうようにした。
なお、どこにビルドを出力するかは、tsconfig.jsonのoutDir
で設定できる。
https://www.typescriptlang.org/tsconfig
この際、ディレクトリ構成を変換し、app.ts
をはじめ.ts
ファイルは、src
ディレクトリの中に置くようにした。
また、distディレクトリはrimraf
パッケージを使い、ビルド時にクリーンするようにした。
TypeScriptでの開発中のサーバー再起動をどうするか?
nodemonでやっていたように、TypeScriptでもファイルを編集した際に、サーバーを再起動するようにしたい。
方法は大きく分けて二つあるようだった。
- nodemonの起動でts-nodeを起動させる方法
- nodemon.jsonの設定ファイルを使う
- コマンドで起動させる
- ts-node-devのパッケージを使用する
- 例
ts-node-dev --respawn index.ts
- 例
どちらも同じくらい使われていそうだった。
実行が簡単そうな、ts-node-dev
パッケージを使うことに。
jsファイルをtsファイルに置き換えていく
jsファイルと共存させる場合は、tsconfig.jsonのallowJs
をtrue
にしておくこと。
(なぜかts-node
で動かしてるときは動いた。ビルドしたら無視された読み込まれず怒られた(´・ω・`))
また、require
を全てimport
に置き換えた。
例えば、
hoge.ts
のmodule.exports = hoge;
は、
import hogeru from ‘./hoge’
などでいけるっぽい。
(module.exportsとかの変換がざっくり理解なのそのうち解決する)
付随して、ESModuleのimportにおいて、たまにtsconfig.ts
のesModuleInterop
の設定でハマる人がいるらしいとメモしておく。
また「型がありません」などと怒られるたびに、大人しくtypesを検索して入れた。
だいたい、先人が作ってくださっているおかげで助かった。
あとは、get
メソッド等の引数reqやresなどに型をつけていく。
var
もconst
を使うようにした。
現在のpackage.jsonのscripts
大体こんな感じになった。
{
"clean": "rimraf dist",
"dev": "ts-node-dev --respawn src/app.ts",
"start": "npm run clean && tsc && node dist/app.js",
"build": "npm run clean && tsc"
}
なおnpm-run-all
というパッケージで、複数のnpm-scriptsの実行を簡単に記述できるようになる。
が、そこまで複雑じゃないのでそのまま書いた。
Expressのlistenでサーバーを立つらしいが、GeneratorのwwwファイルではcreateServerで立てているのどういうこと?
const app = express();
app.listen(3000, () => {
console.log("Server started on port 3000");
});
としてサーバーを待機させている記事が多かった。
が、express-generatorが生成したwwwファイルでは、
var app = require('../app');
var http = require('http');
var server = http.createServer(app);
server.listen(port);
というふうに起動していた。
どう違うのか気になって調べたところ、どうもapp.listen
の中身は、http.createServer
などなどで、ええ感じに実行してくれているらしい。
ということで、app.listen
を使っていくことにする。
取り急ぎのセキュリティ
npm helmetで検索すると、helmetつけましょうね、というのがわかる。
HTTPのheadersをええ感じにしてくれるとのこと。
helmetをミドルウェアとして組み込んでおく。
というところで、最低限セットアップできた感があるので、ここで一度休憩する。
あとはMySQLとかとくっつけながら触り倒していく。
お疲れ様でした。