2018-12-03に更新

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
    • Twitter APIに問い合わせる処理やユーザ削除機能で利用
  • 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.orgUptime 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の運営者のだらさんです!

Originally published at blog.haramishio.xyz

Morix💪😼✨

副業募集中😀 #AWS #GCP #golang #Python #Vim #技術ブログ #SRE #しがないラジオ #インフラ勉強会 欲しいもの: https://t.co/wZbbiO4e3d

Crieitはαバージョンで開発中です。進捗は公式Twitterアカウントをフォローして確認してください。 興味がある方は是非記事の投稿もお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか
関連記事

コメント