【まもりあいJapanのコード研究】2:ローカルでAPIサーバと管理画面を動かす【NestJS】【Firebase】【React】

プログラム全般

まもりあいJapanについてはこちら前回は技術Stackを確認しました。

今回はAPIサーバと管理画面(admin-panel)をローカル環境で動かしてみます。

色々やってたら、コードの修正も必要だったのでPullRequest出したら取り込まれました😀😀😀

1: Firebase環境の作成

ローカル環境、、、なのですが、ローカル環境から直接Firebaseにつなぎにいく構成になっています。

なので、まずはFirebaseの設定をします。

Firebase プロジェクトについて理解する

プロジェクトを作成する

Firebase を JavaScript プロジェクトに追加する

ログインした後の画面で、プロジェクト追加

名前を自由につけて

AnalyticsはとりあえずOffで大丈夫です。

Webアプリの追加

web(今回は管理画面)からつなげるので、Webアプリを追加します。

適当に名前を入力して

接続情報が表示されるのでコピっておきます。(後でも確認できるので忘れても大丈夫)

認証(Authentication)を有効にする

Firebase経由でログインできるように、認証機能を有効にします。

メール/パスワードによるログインを有効にします。

メールリンクによるログインも有効にします。

有効になりました。

Firestoreを有効にする

続いてDBのFirestoreを有効にします。

データベースの作成

ひとまずテストモードで開始

ロケーションは何でも良いっちゃ良いですが、近い場所を選んでおくのが無難でしょう。

私は東京に住んでいるので、asia-northeast1 にします。

Cloud Firestore のロケーション  |  Firebase

できました。

最初の管理ユーザーを作成

管理者としてログインした後は、管理画面から管理者の追加ができるのですが、最初の一人目は無理やり作る必要があります。(管理画面にログインできない)

というわけで、Firebaseのコンソール画面からデータを作成します。

Authentication->ユーザーを追加

自分のメールアドレスと、適当なパスワード(実際には使わないので何でもOK)を設定

登録されました。ここで、ユーザーUIDをコピっておきます。(次で使う)

続いて、Firestoreの画面に移動します。

コレクションを開始からデータを追加します。

コレクションIDは admins

  • ドキュメントID:さっきコピったユーザーUID
  • accessControlList:SUPER_ADMIN_KEY
  • email:さっき登録した自分のメールアドレス
  • userAccessKey:SUPER_ADMIN_KEY
  • userAdminRole:SUPER_ADMIN_ROLE

と登録したます。タイプは画面を参照してください。

できました。

これで設定は一通り完了です!お疲れさまでした!

2: APIサーバ起動

まずは本家のRepositoryをforkして、ローカルにcloneしてください。

mamori-i-japan/mamori-i-japan-api
REST API Server for Japanese Exposure Notification App to fight against COVID-19 a.k.a. "まもりあいJAPAN". - mamori-i-japan/mamori-i-japan-api
forkしたリポジトリを更新する方法 - Qiita
背景 チーム開発でリポジトリをfork(自分のリモートのgithubにリポジトリをコピー)することになりましたので実行したコマンドをまとめました。 やったこと fork対象のgithubのリポジトリを自分のリモートgithub...

基本的にReadMeの通り進められます。

npm install

$npm install


> fsevents@1.2.12 install /Library/WebServer/mamori-i-japan-api/node_modules/watchpack/node_modules/fsevents
> node-gyp rebuild

  SOLINK_MODULE(target) Release/.node
  CXX(target) Release/obj.target/fse/fsevents.o
  SOLINK_MODULE(target) Release/fse.node

> protobufjs@6.8.9 postinstall /Library/WebServer/mamori-i-japan-api/node_modules/protobufjs
> node scripts/postinstall


> @nestjs/core@7.0.7 postinstall /Library/WebServer/mamori-i-japan-api/node_modules/@nestjs/core
> opencollective || exit 0

                          Thanks for installing nest 🙏
                 Please consider donating to our open collective
                        to help us maintain this package.

                           Number of contributors: 457
                              Number of backers: 370
                              Annual budget: $45,334
                             Current balance: $2,441

       👉  Become a partner: https://opencollective.com/nest/donate


> serverless@1.70.0 postinstall /Library/WebServer/mamori-i-japan-api/node_modules/serverless
> node ./scripts/postinstall.js


   ┌───────────────────────────────────────────────────┐
   │                                                   │
   │   Serverless Framework successfully installed!    │
   │                                                   │
   │   To start your first project run 'serverless'.   │
   │                                                   │
   └───────────────────────────────────────────────────┘

npm WARN mamori-i-japan-api@0.1.0 No repository field.

added 2529 packages from 1184 contributors and audited 2538 packages in 43.95s
found 2 low severity vulnerabilities
  run `npm audit fix` to fix them, or `npm audit` for details

NestJS、serverlessその他もろもろがインストールされます。

次に、.env.template ファイルをコピーして .env ファイルを作成し、中身を書き換えます。

ローカル環境ではserverlessは使わないので、Firebase関連の設定を埋めます。設定はFirebaesのコンソールのプロジェクトの設定から取得できます。

ここと、

ここからダウンロードできる鍵ファイルに書いてあります。

こんな感じ。

BACKEND_APP_PORTは、初期状態だとadmin-panelとかぶっているので、適当に変えます。

AWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYは、ローカルでは使わないのですが、何かいれないとエラーで起動しないので適当な値を入れます。

起動します。

$ npm run start

> mamori-i-japan-api@0.1.0 start /Library/WebServer/mamori-i-japan-api
> nest start

{"context":"RoutesResolver","level":"info","message":"AppController {}:"}
{"context":"RouterExplorer","level":"info","message":"Mapped {, GET} route"}
{"context":"RoutesResolver","level":"info","message":"AdminsController {/admins}:"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/admins/users, GET} route"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/admins/users, POST} route"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/admins/users/:userId, GET} route"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/admins/users/:userId, DELETE} route"}
{"context":"RoutesResolver","level":"info","message":"PrefecturesController {/admins}:"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/admins/prefectures, GET} route"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/admins/prefectures, POST} route"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/admins/prefectures/:prefectureId, GET} route"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/admins/prefectures/:prefectureId, PATCH} route"}
{"context":"RoutesResolver","level":"info","message":"AuthController {/auth}:"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/auth/login, POST} route"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/auth/admin/login, POST} route"}
{"context":"RoutesResolver","level":"info","message":"UsersController {/users}:"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/users/me/profile, GET} route"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/users/me/profile, PATCH} route"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/users/me/health_center_tokens, POST} route"}
{"context":"RouterExplorer","level":"info","message":"Mapped {/users/me/diagnosis_keys, DELETE} route"}
{"context":"NestApplication","level":"info","message":"Nest application successfully started"}

http://localhost:3001/ にアクセスして、Hello World! が表示されればOKです!

3: 管理画面起動

mamori-i-japan/mamori-i-japan-admin-panel
Admin Panel for Japanese Exposure Notification App to fight against COVID-19 a.k.a. "まもりあいJAPAN". - mamori-i-japan/mamori-i-japan-admin-panel

まずはforkしてローカルにcloneしてください。

その際、ブランチはdevelopブランチを指定してください。(developブランチが最新)

まずは npm install します

$ npm install


> fsevents@1.2.12 install /Library/WebServer/mamori-i-japan-admin-panel/node_modules/jest-haste-map/node_modules/fsevents
> node-gyp rebuild

  SOLINK_MODULE(target) Release/.node
  CXX(target) Release/obj.target/fse/fsevents.o
  SOLINK_MODULE(target) Release/fse.node

> fsevents@1.2.12 install /Library/WebServer/mamori-i-japan-admin-panel/node_modules/watchpack/node_modules/fsevents
> node-gyp rebuild

  SOLINK_MODULE(target) Release/.node
  CXX(target) Release/obj.target/fse/fsevents.o
  SOLINK_MODULE(target) Release/fse.node

> fsevents@1.2.12 install /Library/WebServer/mamori-i-japan-admin-panel/node_modules/webpack-dev-server/node_modules/fsevents
> node-gyp rebuild

  SOLINK_MODULE(target) Release/.node
  CXX(target) Release/obj.target/fse/fsevents.o
  SOLINK_MODULE(target) Release/fse.node

> core-js@3.6.5 postinstall /Library/WebServer/mamori-i-japan-admin-panel/node_modules/@firebase/polyfill/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"

Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library!

The project needs your help! Please consider supporting of core-js on Open Collective or Patreon:
> https://opencollective.com/core-js
> https://www.patreon.com/zloirock

Also, the author of core-js ( https://github.com/zloirock ) is looking for a good job -)


> core-js@2.6.11 postinstall /Library/WebServer/mamori-i-japan-admin-panel/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"


> core-js-pure@3.6.5 postinstall /Library/WebServer/mamori-i-japan-admin-panel/node_modules/core-js-pure
> node -e "try{require('./postinstall')}catch(e){}"


> protobufjs@6.8.9 postinstall /Library/WebServer/mamori-i-japan-admin-panel/node_modules/protobufjs
> node scripts/postinstall


> core-js@3.6.5 postinstall /Library/WebServer/mamori-i-japan-admin-panel/node_modules/react-app-polyfill/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"


> styled-components@4.4.1 postinstall /Library/WebServer/mamori-i-japan-admin-panel/node_modules/styled-components
> node ./scripts/postinstall.js || exit 0

(node:30622) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGINT listeners added to [process]. Use emitter.setMaxListeners() to increase limit
Use styled-components at work? Consider supporting our development efforts at https://opencollective.com/styled-components
npm WARN mamori-i-japan-admin-panel@0.1.0 No repository field.

added 2438 packages from 1142 contributors and audited 2442 packages in 63.901s
found 2 vulnerabilities (1 low, 1 high)
  run `npm audit fix` to fix them, or `npm audit` for details


   ╭────────────────────────────────────────────────────────────────╮
   │                                                                │
   │       New minor version of npm available! 6.9.0 → 6.14.5       │
   │   Changelog: https://github.com/npm/cli/releases/tag/v6.14.5   │
   │               Run npm install -g npm to update!                │
   │                                                                │
   ╰────────────────────────────────────────────────────────────────╯

次に、 .env.local ファイルを作成して、firebaseの設定をいれます。

Firebaseの設定は、APIサーバと同じあたりから取得できます。

REACT_APP_API_HOST=http://localhost:3001
REACT_APP_FIREBASE_API_KEY=XXXXXX
REACT_APP_FIREBASE_AUTH_DOMAIN=XXXXXX
REACT_APP_FIREBASE_DATABASE_URL=XXXXXX
REACT_APP_FIREBASE_PROJECT_ID=XXXXXX
REACT_APP_FIREBASE_STORAGE_BUCKET=XXXXXX
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=XXXXXX
REACT_APP_FIREBASE_APP_ID=XXXXXX
REACT_APP_FIREBASE_MEASUREMENT_ID=

起動

$ npm start

Compiled successfully!

You can now view mamori-i-japan-admin-panel in the browser.

  Local:            http://localhost:3000
  On Your Network:  http://192.168.0.104:3000

Note that the development build is not optimized.
To create a production build, use npm run build.

画面が自動的に起動するはずですが、出てこなったら localhost:3000 をブラウザから開いてください。

4. 動かしつつ微調整

管理画面からログインを試します。

初期ユーザーとして登録した自分のメールアドレスを入れると、認証用のメールが届くはずです。

問題なければリンクからログインができます!

が、APIサーバがエラーを吐いているはずです。

{"context":"AllExceptionsFilter","level":"warn","message":{"status":500,"requestID":"7969c478-041f-4a11-9567-1dd2f69a41e8","responseObject":{"statusCode":500,"timestamp":"2020-05-27T23:37:53.254Z","url":"/admins/users","error":"Error","message":"Error: 9 FAILED_PRECONDITION: The query requires an index. You can create it here: https://console.firebase.google.com/v1/r/project/mamoriai-japan-dev/firestore/indexes?create_composite=XXXXXX"}}}

Firestoreでは複数のwhere句があるようなSQLを実行するには、Indexの作成が必要となります。

Cloud Firestore でのインデックス管理  |  Firebase

Indexを作成するためのリンクは、エラーログに出ていますので(優しい)、リンクをブラウザで開けばOKです。

数分かかりますが、ステータスが「有効」になればOK。

他にも画面をぽちぽち触ると同様のエラーが出るはずなので、同じ対応をすればOKです。

こんな感じで管理者の追加ができます。

エラーの対処

と、すんなりいっているようですが、実際は試行錯誤していて、ここまでの手順にいたるまでに発生したエラーと対象方法です。

APIサーバでconfig足りないよエラー

Error: Config validation error: "AWS_ACCESS_KEY_ID" is not allowed to be empty. "AWS_SECRET_ACCESS_KEY" is not allowed to be empty
    at Function.forRoot (/Library/WebServer/mamori-i-japan-api/node_modules/@nestjs/config/dist/config.module.js:44:27)
    at Object.<anonymous> (/Library/WebServer/mamori-i-japan-api/dist/shared/shared.module.js:21:41)
    at Module._compile (internal/modules/cjs/loader.js:776:30)

ローカルだから必要ないぜ〜と思ってたのですが、必須チェックが走っているので何かいれればOK。(エラーメッセージ通り)

mamori-i-japan/mamori-i-japan-api
REST API Server for Japanese Exposure Notification App to fight against COVID-19 a.k.a. "まもりあいJAPAN". - mamori-i-japan/mamori-i-japan-api
Documentation | NestJS - A progressive Node.js framework
Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines ele

NestJSではこんな風にconfigにvalidationかけられるんですね〜。便利。

portかぶってる

apiも管理画面も初期設定が3000ポートなので、かぶっていて起動しないよエラー

? Something is already running on port 3000. Probably:
  node /Library/WebServer/mamori-i-japan-api/dist/main (pid 31744)
  in /Library/WebServer/mamori-i-japan-api

どっちをずらしても良いのですが、管理画面側が .env の設定で変えられるようになってたのでずらして解決。

corsエラー(PRして本家取り込み済)

Access to XMLHttpRequest at 'http://localhost:3001/admins/users' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

なんか1ヶ月に1回くらいこれと戦ってる気がする、、、

Nest.jsの場合、一行追加すればOKでした。素晴らしい。

[feature]add cors setting for local environment by daisuke-fukuda · Pull Request #168 · mamori-i-japan/mamori-i-japan-api
Now, I'm trying to execute this project. I found that it is require cors settings to call api from local admin-panel. main.ts is entry point for local serve
Documentation | NestJS - A progressive Node.js framework
Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines ele

firestoreが無効になる

{"context":"AllExceptionsFilter","level":"warn","message":{"status":500,"requestID":"216ddf1f-dd71-428c-88f3-3a9eb69e5dfd","responseObject":{"statusCode":500,"timestamp":"2020-05-23T02:34:39.414Z","url":"/auth/admin/login","error":"Error","message":"9 FAILED_PRECONDITION: The Cloud Firestore API is not enabled for the project mamori-i-japan-api-test"}}}

Firebaseのコンソールでも、データが表示されたりされなかったり。

Firebase Cloud Firestoreの導入時に発生したエラー:Error: 9 FAILED_PRECONDITION: The Cloud Firestore API is not enabled for the project - Qiita
Firebaseのコンソールにて、Firestoreのデータベースの作成で失敗 Firestoreのスタートガイドには以下のように記載されています。 Firebase コンソールを開き、新しいプロジェクトを作成します。 [...

こちらを参考に、APIを有効にしましたが、しばらくは大丈夫なのですが5分くらい立つと再発する。

謎すぎるので、Firebaseに問い合わせたのですが、2日後くらいに確認したら治ってました。Firebaseの障害だったのか、、、謎。

管理画面でFirebaseの設定が.tsファイルに直書き(PRして本家取り込み済)

エラーではないですが、Firebaseの設定が.tsファイルに直書きする手順になっていました。(gitの管理対象ファイルなので、間違ってコミットするとセキュリティ的にあまりよろしくない)

というわけで .env に書くようにPR出して取り込まれました。

local firebase config move to env.local by daisuke-fukuda · Pull Request #155 · mamori-i-japan/mamori-i-japan-admin-panel
Hi, now I'm trying to execute this project at my local environment. I found that it is not good writing firebase config to application code ( which is under

まとめ

というわけで、APIサーバと管理画面をローカルで動かすことができました。

ちょいちょいエラーにあたりましたが、一つ一つの解決方法はシンプルでしたので、楽な構成だなと思いました。

次はServerlessを使って、デプロイをしてみようと思います。

コメント

タイトルとURLをコピーしました