Angular ＋ Expressの開発環境サンプル

はじめに
---
Angular ＋ Expressの開発環境サンプルを作ったので公開。

[Angular + Express Example](https://github.com/kyv28v/angular-express-example)

DBはPostgreSQLを使用しています。
最低限の構成で、簡単なCRUD機能を実現しています。
デモ用などに簡単なWEBアプリを作りたい時とかに、ベースにできればと。

動作環境
---
動作確認したときの環境は、以下の通り。
（2021/10/24：最新の安定板node.jsで動くように修正しました。）

・node.js ~~12.16.1~~　→　16.12.0
・npm ~~6.13.4~~　→　8.1.0
・PostgreSql 12.1

機能
---
サンプル機能として、入退室時間とユーザを登録・表示する画面を作成しています。

###ログイン画面
![Angular+Express Example01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285246/739009a2-415f-f72e-a8fb-5e3b73db8242.png)

###入退室管理画面
![Angular+Express Example02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285246/4ba3bdc9-90ef-d21c-1091-36e8c9de780d.png)

###ユーザ管理画面
![Angular+Express Example03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285246/df156cc3-f94c-fa95-9494-320645993303.png)

###登録ダイアログ画面
![Angular+Express Example04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285246/d5e6fe72-e3fc-84f7-4264-876030f15874.png)

プロジェクト構成
---
フロントエンドとバックエンドをひとまとめにしたようなプロジェクト構成にしています。

・ルートフォルダ　─┬─　front（angular）
　　　　　　　　　　└─　server（express）

という構成です。
起動するのはサーバ側のnodeだけで、画面表示のリクエストもサーバ側（express）で受け付けて転送しています。

angular側も普通に`ng serve`して別サーバにすることも可能ですが、小規模なものであればフロントエンドとバックエンドを一人で実装することも多いでしょうし、このほうが軽そうなので。

実行
---
実行方法は、git の ReadMe に記載の通りです。
PostgreSQLのデータベースを準備し、テーブル・初期ユーザを登録した後、ルートフォルダからnpmコマンド（`npm install` `npm run watch`など）を叩くだけです。

事前にインストールなどが必要なのは、
・PostgreSQL
・node（npm）
くらいのはずです。
（必要があればgitも。）

開発時の起動
---
ルートにて`npm run watch`を実行すると、ソースの変更を監視して起動してくれます。
ブラウザにて、`http://localhost:3000/`にアクセスするとログイン画面が表示されるはずですので、

・ユーザID：1
・パスワード：123456

でログインしてください。

front/serverともに、ソースを修正・保存すると、自動的にビルドが走ります。
ブラウザは自動更新されませんので、必要に応じて手動でブラウザ更新してください。

デバッグ
---
サーバ側でデバッグしたいときは、VSCodeなら「Attach Node」でnodeのプロセスを選択するとブレークポイントを使用してデバッグが可能になります。
![debug-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285246/55117192-1803-4dfc-bd0a-013d5a8e5227.png)

フロント側は・・・何も用意してないので、ブラウザのデバッグ機能を使ってください。
![debug-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285246/48ad8939-a7d3-58d5-43b4-70e5de29b15e.png)

実装についての補足
---
以下、細かい機能とその実装について補足します。

###画面デザイン
画面デザインについては、プロジェクトによってそれぞれ見直しすることになると思いますので、かなり適当です。
いちおう、「Bootstrap」と「Angular Material」を使用してある程度は調整していますが、必要に応じて見直しすればいいかなと。

###ルーティング
「server/src/app.ts」にて、リクエストのルーティングをしています。

```typescript:app.ts
// Expressのルーティング（認証不要のもの）
app.use('/api/', indexRouter);
app.use('/api/common/auth', authRouter);

// トークンの正常チェック
app.use('/api/*', async (req: any, res: any, next: any) => {
  const token = req.headers['access-token'];
  if (!token) {
    res.status(401);
    return res.json({message: 'No token provided'});
  }
  try {
    const decoded = await jwt.verify(token, tokenConf.accessTokenSecretKey);
    req.decoded = decoded;
    next();
  } catch (e) {
    res.status(401);
    return res.json({message: e.message});
  }
});

// Expressのルーティング（認証が必要なもの）
app.use('/api/common/db', dbRouter);

// Angularのルーティング
app.use(express.static(path.join(__dirname, '../../front/dist')));
app.use('/*', express.static(path.join(__dirname, '../../front/dist/index.html')));
```

まずはAPI側（URIが/api/・・・のもの）の処理として、認証されていなくても実行できるAPI（ログイン用のAPIなど）を受け付けます。
その次に認証チェックを行い、エラーの場合は401を返して終了します。
認証チェックOKの場合、その次で認証前提のAPIの処理をしています。

その次で、Angular側へのルーティングをしています。

###ログイン
JWT形式のトークンで管理しています。
いちおう、アクセストークンとリフレッシュトークンを使用するようにはしていますが、管理方法はいまいちです。
・リフレッシュトークンをアクセストークンと同じようにLocalStrageで保存している。
・トークンをDBなどに管理しておらず、ログアウト時に破棄などもしていない。
・暗号化も適当。
実際に使えるようにするには、上記あたりも見直しが必要だと思います。
とりあえずはトークン生成、受け渡しのサンプルとして。

###トークンのリフレッシュ
front/src/app/common/services/http.ts
で、アクセストークンの期限が切れていたら、リフレッシュトークンによる更新を試みるようにしています。
リフレッシュトークンの期限はデフォルトで180日にしてるので、一度ログインすると180日間ログイン状態を維持する動きになります。

interceptでやるのが一般的？かもしれませんが、どうにもキレイに実装できない（Callback地獄になってしまう・・・）ので、諦めて原始的な手法（HttpClient をラップする関数、callAPI を作成）で実装しました。

###サーバ側のデータアクセス
「api/common/db」にて、とりあえず`server/src/config/sql`に定義したSQLを実行して返却する、というだけの共通的なAPIを用意しています。
実際には、それぞれ個別の処理を行うAPIを追加していくことになると思いますが、データアクセスのサンプルということで。

###ダイアログ表示
`client/src/app/view/common/dialogs/simpleDialog.component`にて、簡単なダイアログを共通的に表示できるようにしています。
これも、実際には個別にダイアログを増やしていくことになるかと思いますが、ダイアログ表示のサンプルとして。

###列挙型、的な定義について
「1：男性, 2：女性」みたいな定義を汎用的に使用したいので、
「client/src/app/common/defines/enums.ts」
でまとめて定義しています。
表示変換用のpipeとかも同ファイルに定義していて、ちょっとゴチャっとしていますが・・・まあ、細かいファイル構成はあとで自由に見直せばいいかと。

###多言語化
ngx-translateを使用して多言語化対応しています。
よく考えたら、いうほど多言語化対応が必要になることもないので、サンプルとしては不要だったかも。
英文は、google先生まかせなので、怪しいです。

###DBについて
DBはPostgreSQLを使用していますが、RDBであれば他のものに簡単に置き換えできると思います。（試してませんが。）
「server/src/utility/postgres.ts」
で一般的なコマンドをラップしてますので、そのあたりを書き換えればいけるかと。

その他、課題・雑感など。
---
・せっかくTypescriptにしてるのに、型定義をサボっているところが多いです・・・。
　特にサーバ側は、とりあえず Typescript にしたってだけの状態です。
　今だと、Nest.js みたいなフレームワークを導入したほうが幸せになれるかも？
・最低限のシンプルな構成にしたかったので、一般的なアプリケーション構成（サービス層とかコントローラ層とか）もバッサリ省略しています。
　必要に応じて分離すればいいかと。
・日付と時刻が同時に選択できるカレンダーが欲しくて、flatpickr を使ってみましたが・・・時刻入力の挙動がいまいちなような・・・。
　これなら、Angular Material の Picker を使ったほうがよかったかも。
・スマホでもそれなりに表示できるようにしましたが、グリッド表示などはいまいち・・・。
・Callbackな書き方が好きじゃないので、なるべくAsync-Awaitを使うようにしてます。
・Angular9が出たので、アップデートしてみました。
　レンダリングエンジンが変わったらしいですが、いまいちわかってません。
　ビルド時のメッセージがなんか変わったかなーくらいしか。。。
　それより、Typescript3.7対応になったので、Null条件演算子（hoge?.name みたいな書き方）が使えるようになったのが嬉しい。。。
・Heroku での実行手順を以下に投稿しました。
　[Angular ＋ Express ＋ PostgreSQLのWEBアプリをHerokuで実行する手順](https://qiita.com/kyv28v/items/527590f090c51b55edd2)
・2021/06/19追記
　本ソースをベースにして、サイドメニューやページを動的に生成するWEBアプリを作成しました。
　[Angularベースの汎用WEBアプリサンプル](https://qiita.com/kyv28v/items/3cd15cc2238fa8d46126)
・2023/01/18追記
　ExpressをNestJSに変更してみました。
　[Angular開発環境サンプル（NestJS版）](https://qiita.com/kyv28v/items/ef2ffb3057f2be5d15a7)
　[Angular ＋ NestJS 開発環境サンプル2](https://qiita.com/kyv28v/items/7ee601cc7a1a1bb20e3d)