Nyaaanを支える技術
こちらはCrieitの個人開発サービスに用いられている技術 Advent Calendar 2018の2日目の記事です。
前日はなべさんの日本語のフリーフォントを一度に試せる「ためしがき」に用いられている技術についてでした!
11/5にNyaaanというWebサービスをリリースしました。
そこで
- どのような技術を使ってるのか
- どういう知見が得られたのか
を書いていこうと思います!
どういうサービスかは以下の記事を読んでいただけたらと思います!
使用している技術
ざっくりな構成でいうと
- フロントエンド
- Nuxt.js
- Webホスティング
- Netlify
- バックエンド
- Firebase
です。
Nuxt.js
Nuxt.jsはVue.jsアプリケーションを作るためのフレームワークです。
ちょっと大きめのVue.jsアプリを作る際に必要なVuexやVue Routerなどが入ってるのでNuxtをインストールすればすぐにWebアプリが作れます。
今回はSSR(サーバーサイドレンダリング)を行わないため、SPAモードで利用しています。
Netlify
Webホスティングはさまざまな種類がありますが、今回はNetlifyを選択しました。
選択した理由としては・・・
- ビルドを勝手にやってくれる(
npm run build
がビルドコマンドよ!ってNetlifyに登録しとくだけでいい) - ビルドしたらデプロイもやってくれる
- 他のホスティングサービスと比較して無料枠の制限が少ない
Firebase
APIやDBが必要になったのですが、すべてFirebaseで行いました。
主要なFirebaseサービスはHostingとML Kit以外使いました。
それぞれの用途としては・・・
- Authentication
- Twitter認証のため利用
- Firestore
- ユーザ投稿データやユーザデータを保存
- Functions
- Storage
- ユーザのアイコンの保存で利用
得られた知見
Firestoreのクエリーのクセ
FirestoreはNoSQLです。RDBのような柔軟なクエリーを書くことは出来ません。
その制限にひっかかってちょっと苦しんだ事例があるので共有します。
Nyaaanでは「有効期限」という機能があり
これを設定すると1時間または1日経つと、鳴き声(つぶやきみたいなもの)が見れなくなるというものです。
その鳴き声テーブルはこんな感じになってます(本当はもっとカラムあります)
カラム | 型 |
---|---|
ID | string |
鳴き声 | string |
本音 | string |
有効期限 | timestamp |
作成時間 | timestamp |
Nyaaanにはタイムラインがあり、鳴き声は作成時間の降順でソートしたいです。
なので・・・
// カラム名はわかりやすく日本語にしてます mewRef.where("有効期限", ">", now).orderBy("作成時間", "desc").limit(10).get()
としたかったのですがこれはNG。
範囲フィルタ(>や>=)を使う場合は、orderByの最初の要素はその範囲フィルタで使用したカラムしか指定できません。
Cloud Firestore でのデータの並べ替えと制限
RDBならこういうのは普通にできますが、NoSQLはこういう制限があるのかと勉強になりました。
結局どうしたかというと、後述するバッチ的なもので有効期限をチェックして削除してます。
FirebaseでCron的なことをしたいとき
上述したバッチ的なものの話です。
サービスを作っていくとバッチが必要になる場面があるとおもいます。
なにかしらの処理を定期的に動かすとなるとCronを使ったりJenkinsを使ったり色々あると思います。
これらはサーバーが必要になりますよね。
すでに持ってる人はそれを使えば良いんですが、僕はお金をかけたくないのでどうにかしてサーバーレスにしたいです。
さて、FirebaseのFunctionsはHTTPSフックで起動することが出来ます。
つまりcronでcurlを定期実行すればバッチみたいなことができるわけです!
そして世の中には外形監視のために定期的に指定したURLを叩いてくれるサービスがあります。
cron-job.orgやUptime Robotがそれです。
これを利用してFunctionsを定期的にトリガーするようにしてバッチ処理をサーバーレスで実現しました。
懸念点として、Functionsの実行を外部からできるようにしてしまっているのでセキュリティ的に問題ないのか?という点ですが
今回の場合はリクエスト時にGETパラメーターでアクセストークンを渡しており、そのアクセストークンじゃないとアクセス拒否するようにコーディングしています。
それでも攻撃されて突破されるリスクもなくはないので、センシティブなことをしたい場合はやめたほうがいいと思います。
Functionsの様々なトリガー
Fucntionsのトリガーは色々あります。
トリガー | 内容 |
---|---|
HTTPS | URLを叩くだけ |
onCall | クライアントから直接呼び出し |
Authentication | ユーザの登録/削除で呼び出し |
Firestore | ドキュメントの登録/更新/削除で呼び出し |
Storage | オブジェクトの登録/更新/削除で呼び出し |
Pub/Sub | Cloud Pub/SubのPublishで呼び出し |
NyaaanではHTTPSとAuthenticationのトリガーを使用してます。
特にAuthenticationトリガーは便利で、以下のようにFunctionsに書くだけで簡単に書くことが出来ます。
// ユーザが削除されたら実行されるFunction exports.removeUser = functions.region('asia-northeast1').auth.user().onDelete(function(user, context) { ~~~ });
Nyaaanではユーザ削除されると今までの鳴き声が消えるようになってます。
クライアントでそこまでやると重いので、クライアントではAuthenticationのユーザだけ削除してレスポンスをすぐ返し、
FunctionsのAuthenticationトリガーで非同期で削除処理を走らせてます。
Netlifyのリダイレクト設定
Nuxt.jsのSPAモードを使用していて、Netlifyでホスティングしていたのですが
ルートパス以外を直接呼び出すと404エラーになってしまいます。(例: https://nyaaan.haramishio.xyz/top)
SPAではルートパス以外でアクセスされたらindex.htmlへリダイレクト処理をする必要があります。
Netlifyはリダイレクトの機能があり、デプロイするディレクトリのトップに _redirects
を配置しそこにリダイレクト設定を記述するとリダイレクトしてくれます。
/* /index.html 200
この話をもうちょいちゃんと説明したのがこちらの記事になります。
Netlifyを使ってたらルートパス以外が404になった話とその解決方法
最後に
ということでNyaaanの使用技術とそこから得た知見を書きました!
この記事でみなさんの開発のヒントになれれば幸いです!
さて、明日はこのアドベントカレンダーが開催されているCrieitの運営者のだらさんです!