サービスについて
Skillfulsというプログラマのスキルのレベルを記録して、その情報を元にスキルマップを簡単に作ることができるWebサービスです。作ったきかけは、僕自身が様々な技術を扱ってきて、自分のどのスキルがどのくらいできるのだろう?という疑問を可視化して解決できないかと思ったことです。
また、個人のスキルをデータとして蓄積すると、それを活用して開発チームのメンバーのスキルマップを簡単に作れると気づき、スキルマップ作成機能もつけてみました。
スキルマップはチーム内で技術の得意、不得意を一覧で見ることができ、技術選定の参考や特定の技術に詳しい人に質問しやすくなったりするようです。
↓スキルを記録 公開ページ
↓複数人の情報をまとめてスキルマップに 公開ページ
技術構成
Nuxtとnestjsを採用し、TypeScriptで型共有ができるようにモノレポな構成になっています。
Nuxtとnestjsのpackage.jsonは独立しているので、完全な1プロジェクトというわけではありません。
フロントエンド
フロントエンドを構成する代表的なものは以下のとおりです。
- Nuxt.js
- TypeScript
- Vuetify
- Firebase
- authentication
- storage
- analytics
使い慣れたNuxt.jsをメインにして、バックエンドと型共有をするためにTypeScriptを導入しました。vuex-module-decoratorsも導入しています。ClassComponentは使わず、OptionAPIで開発しました。将来的にVue3に対応したときにComposiitonAPIで書き換えようと思っています。
認証は最初は自分で実装しようと思っていましたが、JWTの期限の設定などを考慮すると大変になってきたので、定番のFirebase authenticationを使いました。
それと同時に、スキルのロゴ画像をユーザがアップロードするときにFirebase storageを使うことにしました。
この画像のアップロードの際に画像のサイズを調整する必要があって、Functionsなどを使ってやる手もありますが、フロントエンドでcompressorjs を使って最大サイズ、品質、画像形式をPNGに変換してアップロードするという処理をしています。
日時はサーバ上でUTCで記録しているので、@nuxtjs/dayjs を使ってフォーマットしています。dayjsは非常に使いやすく、表示フォーマトから7日前などといった計算まで簡単に行うことが出来ます。
デスクトップとモバイルでUIコンポーネントを変更
モバイルでの利用はあまり想定していませんが、左ナビゲーションをモバイルでも表示されているとかなり使いにくいので、@nuxtjs/deviceでスマホとタブレットではボトムナビゲーションを表示するようにしています。
これはYouTubeのUIを参考にしました。
バックエンド
バックエンドはTypeScriptであれば何でも良かったのですが、ExpressやSailsなども試して、nestjsがExpressよりもディレクトリ構成などが決まっており、Sailsに比べて自由度も高かったため採用しました。
他の代表的なものは以下のとおりです。
- nestjs
- typeorm
- class-validator
- mysql
- firebase-admin
- nanoid
- swagger-ui
nestjsは初めて使いましたが、リレーショナルDBと使うときにはtypeormやclass-validatorなどがあるとかなり便利なような気がします。
スキルマップのユニークIDを生成するときにnanoidを使っています。短い文字列が生成でき、手軽に使うことが出来るのでオススメです。
openAPIのドキュメントを生成するために@nestjs/swaggerを導入しており、コントローラーにデコレーターを付けることで自動生成してくれます。それと同時にswager-uiを使うとAPIを試すときにも便利です。JWTをヘッダに付けてくれたり、以下の画像のようにクエリの入力項目を表示してくれます。
インフラ
インフラは以下の構成図の通りで、GCEにnestjs,MySQL,Nginxをデプロイしています。NginxはCloudflare間を独自証明書でSSL化するために入れています。Nuxtはstaticで配信したかったのですが、APIをProxyするためにHerokuにデプロイしています。ついでに一部のページだけをSSRしています。本当はVercelを使いたかったのですが、何故かプロキシが動作しなかったので諦めました。
GCE
なるべく長く続けたいサービスなので、維持費を安くしようと思い試行錯誤していましたが、結果的にGCEにDBとAPIサーバを乗っけるのが手軽だと思いました。
最初はCloudRunとCloudSQLを使おうと考えていましたが、CloudSQLが最低でも1,000円くらい近くかかるので、CloudRunのオートスケーリングを諦めました。
また、GCEは一時的に停止すれば、インスタンスタイプを変更できるので、ユーザが多くなれば手動で垂直スケーリングすることができます。
GCEはalways freeのf1-microが有名ですが、流石に重かったので2コア,1GBで月額6.12ドルのe2-microを使っています。
Heroku
Herokuの無料プランでもCloudflareを使うとルートドメインで無料SSLが使えることがわかりました!
普通にCNAMEをルートドメインに設定するだけで実現できました。
CloudflareはCNAME のフラット化という独自の技術でルートドメインでもCNAMEが使えるようにしているようです。
Cloudflareの説明
CNAME のフラット化とは
CNAME のフラット化では、Cloudflare は、参照する CNAME をフォローし、CNAME レコードではなくその IP アドレスを返します。
主に 2 つの利点があります。
CNAME のフラット化により、Cloudflare Web サイトの所有者は、ドメインのルートに CNAME を追加できます。これは、DNS 仕様では許可されていません。
CNAME のフラット化により、CNAME での DNS 解決が最大 30% 高速化されます。
CNAME のフラット化について詳しくは、当社のブログ を参照してください。
microCMS
よくあるアプリ内の”お知らせ”機能やGCEのAPIが落ちたときにインシデントメッセージを配信するためにmicroCMSを使っています。以前から国産ヘッドレスCMSということで気になっていたので導入してみましたが、とても簡単に使えて機能もCMSとして十分でした。
最後に
フロントとバックエンドを同時開発した個人プロジェクトは初めてだったのですが、個人的に興味があったSPAでモノレポの構成で開発できて面白かったです。nestjsは初めて使ったので、手探り状態でしたが、ドキュメントや日本語の情報も結構あって助かりました。
まだ全機能を実装できていませんが、最低限使えるようになったのでβ版として公開してみました。もしよかったら使ってみてください!