Angular + Firebase で ブログ機能作る時にやったこと

こんにちは、[あかい](https://twitter.com/Ver1000000000)です！

本記事は Angular Advent Calendar 2019 の2日目の記事です！！

---

フリーランスで活動するにあたって、
継続的なコンテンツの更新を行うために、ポートフォリオサイトにブログ機能を持たせたいと考えました。

Ninja、FC2、楽天、アメーバ、はてな…… 世の中には高品質なブログサービスがたくさんあります。
しかし、これらのサービスをそのまま利用すると、別のドメインでブログを管理することになるので、
SPAの性質上、デザインや機能の統合が難しくなります。

そこで*Firebase/Firestoreを利用したサーバーレスアーキテクチャによるブログ実装*を、
イチから行うことを決意いたしました。

*Firestore*はリアルタイムデータベースがウリなので
ブログにしちゃうのはなんとなく勿体無い気もしますが、
これがなかなか実装していて面白かったです。

完成したブログはこちらです↓

- https://ver1000000.com/blog

---

AngularとはなんぞやとかFirebaseとはなんぞやなど、導入について今回は書かないので、
その辺は各自でお調べ下さい！！

以下のページが役に立つと思います！

- [angularfire/install-and-setup.md at master · angular/angularfire](https://github.com/angular/angularfire/blob/master/docs/install-and-setup.md)

# 環境、パッケージのインストール
Angularのバージョンは8.2.8で、FirebaseはBlazeプランを利用しています。
ブログ機能の構築を行うために、以下のパッケージを導入しました

- [@angular/fire](https://www.npmjs.com/package/@angular/fire)
    - AngularでFirebaseを操作するためのパッケージ。 なくてもFirebaseは使えると思うが、設定周りとかこっち使ったほうがスムーズになるはず
- [firebase](https://www.npmjs.com/package/firebase)
- [ng-in-viewport](https://www.npmjs.com/package/ng-in-viewport)
    - ブログ記事無限スクロールの実装に利用
- [firebase-tools](https://www.npmjs.com/package/firebase-tools)

# Firestore設計

以下のような2つのドキュメント（SQLでいうテーブル）を用意しました。

```ts
interface BlogEntry {
  id: string;
  slug: string; // 記事のパーマネントURLに利用する文字。
  title: string;
  body: string; // markdownをhtmlに変換した文字列。 ここをinternalHTMLにいれるのでXSSに注意！
  markdown: string;
  categories: string[];
  createdAt: firestore.Timestamp;
  updatedAt: firestore.Timestamp;
  expiresAt: firestore.Timestamp | boolean;
}

// FirestoreではSQLでは基本のcountなどの集計関数が使えないので、
// 記事の更新削除をフックして数情報を更新してやる
interface BlogCounter {
  categories: { [categoryName: string]: number };
  years: { [year: number]: number }
}
```



# やったことやってないこと（今後やりたいこと）

- [x] 管理者（私）のログイン機能
- [x] 記事のCRUD
- [x] マークダウンでの記事作成  
  - *[Firebase Functions](https://firebase.google.com/products/functions/?hl=ja)* でCRUDをフックし、
    *[Marked](https://github.com/markedjs/marked)* でマークダウンをhtmlにし、
    *[Highlight.js](https://github.com/highlightjs/highlight.js/)* でコードのハイライトを行っています
- [x] カテゴリ機能  
  - *Firebase Functions*でカテゴリごとの記事数や種類を更新することで実現しています
- [x] 無限スクロールによるページング  
  - Firebaseはページングが貧弱で「3つ前のデータを取得する」とかに難儀するので、
    過去を振り返る必要のない無限スクロールにしました
- [x] パーマリンク作成（記事の詳細ページ）
- [x] 絞り込み機能
- [x] レスポンシブ対応
- [x] 読み込み中の可視化
- [x] SSR(Server-Side-Rendering)
  - [x] SNSでのシェア機能
  - [x] 記事ごとのOGP設定
- [ ] RSS配信  
  - トラックバック機能は……いまどきいらないね
- [ ] コメント/いいね的機能
- [ ] テキスト検索機能
    - AlgoliaやらElasticSearchやら
- [ ] 画像投稿/画像貼り付け
- [ ] Lighthouseとかでアクセシビリティの担保

（↑の中で「ここを掘り下げてほしい」というものがあればコメントお願いします、書きます）

# 所見
- Firebaseは無料プランでもめちゃくちゃ遊べる
  - 私は今回、Blaze（従量課金制）プランにしたのですが、上記のことはほぼすべてSpark（無料）プランで行えます。
- RDBめちゃおもしろい
- 無限スクロールとの親和性がすごい
- SSRはドキュメントが最新に追いついていなかったりしてめちゃくちゃ大変
- SQLで当たり前のことでも、NoSQLだと勝手が違うということがたくさんある

# 参考
- [angular/angularfire: The official Angular library for Firebase.](https://github.com/angular/angularfire)
- [Firebase と Nuxt.js で個人ブログを作った話 - Mobile Factory Tech Blog](https://tech.mobilefactory.jp/entry/2018/12/08/094245)
    - 「ブログをつくるぞ〜Firebaseで設計どうすんだ〜？」となったあと、この記事がかなり参考になりました
- [Angular Universalについて語るスレ - General - Discourse | Angular Japan User Group](https://angular-japan.discourse.group/t/topic/20)

---

明日は@ringtail003さんです！ よろしくおねがいします！