Flamelinkとは
Flamelinkとは、FirebaseをヘッドレスCMS化できるサービスです
参考
- 今すぐ始められる!FIrebaseをブログのCMSに変える「Flamelink」を使ってみた!(2018-11-21)
- FlamelinkのクライアントをNuxtで作成してみる(2019-01-13)
- Flamelink公式のNuxt環境構築サンプル(Github)
基本的には上記の内容に則って進めていきます
また、参考サイトではDatabaseにRealtime Databaseを使用していますが、今回はCloud Firestoreを使用しています
もくじ
バージョン
$ node --version
v10.22.0
$ npm --version
6.14.6
FlamelinkとFirebaseの連携
※ここではFirebaseとFlamelinkを行ったり来たりするので、
先頭に<Firebase>、<Flamelink>を入れています
1. <Firebase> Firebaseでプロジェクトを作成しておく
この記事では割愛します
2. <Flamelink>Flamelinkのアカウント作成
3. <Flamelink>Databaseの選択
今回はCloud Firestoreを選択
Firestore側のセキュリティルールが表示されているのでコピーしておきます
4. <Firebase> Firestoreのルールを設定
Firebase > Cloud Firestore > ルールに、
先ほどコピーしたルールを貼り付けて公開します
5. <Flamelink> Storageのルールを表示
Databaseのルールと同様にコピー
7. <Firestore> Storageのルールを設定
同様にFirebase > Storage > ルールに貼り付け
8. <Flamelink> 認証の設定
Email/Passwordでの認証を有効にしてと出るので
9. <Firebase> Authenticationの設定
Firebase > Authentication > Sign-in methodから、
メール/パスワードを有効にします
10. <Flamelink> 料金プランを選択してFINISH
今回はテストなので無料のFlameを選択、確認して完了!
Flamelinkで記事の作成
1. PROJECTにログイン
初回のみパスワード設定画面が開きます
2. スキーマを作成
「NEW SCHEMA」からスキーマを作成
任意のtitle, schema IDを設定
ここで指定されたschema IDが、後程Nuxtで記事を取得する際のIDになります
「ADD FIELDS」タブから、記事のフィールドをドラッグ&ドロップで自由にカスタマイズできます
完了したら「SAVE」からスキーマを保存
3. 記事の登録
左ナビのContentメニューをクリックすると登録したスキーマがでるので「VIEW」をクリック
「NEW ENTRY」から記事を投稿
ここでは3件ほど登録しました
Firebase > Cloud firestoreを確認すると、
Flamelinkから登録したデータが追加されていることが分かると思います
「fl_」の接頭辞がついているコレクションがFlamelinkから追加されたものです
「fl_content」コレクションが記事のデータになります
FlamelinkSDKを利用してNuxtで記事取得
1. Nuxtでプロジェクト作成
npx create-nuxt-app <project-name>
一度起動して動作を確認
npm run dev
2. 必要なライブラリのインストール
ライブラリをインストールするために、package.jsonに以下を追記します
"dependencies": {
"@nuxt/typescript-runtime": "^1.0.0",
"@nuxtjs/axios": "^5.12.0",
"nuxt": "^2.14.0",
+ "firebase": "^7.19.1",
+ "firebase-admin": "^9.1.1",
+ "flamelink": "^1.0.0-alpha.24"
},
npm i
でインストール
3. NuxtでFlamelinkを使う設定
nuxt.config.js
に以下を追記
plugins: [
'@/plugins/flamelink'
],
flamelink.jsを作成
公式のflamelink.jsをコピーしてプロジェクト直下に保存します
公式はDBにRealtimeDatabaseを使っていたり、Node8を使用していたり、今回の環境と異なる点があるので一部修正します
import flamelink from 'flamelink/app'
// This example uses RTDB (Realtime Database) - replace with `cf` for Cloud Firestore
- import 'flamelink/rtdb/content'
- import 'flamelink/rtdb/storage'
+ // 今回Cloud firestoreを使用するのでimportするパッケージを修正
+ import 'flamelink/cf/content'
+ import 'flamelink/cf/storage'
// import 'flamelink/rtdb/settings'
// import 'flamelink/rtdb/navigation'
// import 'flamelink/rtdb/users'
export default ({ app }) => {
let firebaseApp
if (process.server) {
const admin = require ('firebase-admin')
if (!admin.apps.length) {
- const serviceAccount = require(process.env.FLAMELINK_PATH_TO_SERVICE_ACCOUNT)
+ // Node10でうまくいかないっぽかったので修正
+ const serviceAccount = require('../serviceAccountKey.json')
firebaseApp = admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: process.env.FLAMELINK_DB_URL,
storageBucket: process.env.FLAMELINK_STORAGE_BUCKET
});
} else {
firebaseApp = admin.app()
}
} else {
const firebase = require('firebase/app')
// require('firebase/auth')
// require('firebase/firestore')
require('firebase/database')
require('firebase/storage')
if (!firebase.apps.length) {
firebaseApp = firebase.initializeApp({
apiKey: process.env.FLAMELINK_API_KEY,
authDomain: process.env.FLAMELINK_AUTH_DOMAIN,
databaseURL: process.env.FLAMELINK_DB_URL,
projectId: process.env.FLAMELINK_PROJECT_ID,
storageBucket: process.env.FLAMELINK_STORAGE_BUCKET,
messagingSenderId: process.env.FLAMELINK_MESSAGING_SENDER_ID
})
} else {
firebaseApp = firebase.app()
}
}
+ //dbTypeをcf(Cloud firestore)に変更
- app.flamelink = flamelink({ firebaseApp, dbType: 'rtdb' })
+ app.flamelink = flamelink({ firebaseApp, dbType: 'cf' })
}
4. Firebase側の設定
サービスアカウントのjsonファイルを作成して設置
サービスアカウント情報を含むJsonファイルをFirebase側で生成、ダウンロードし、
serviceAccountKey.json
にリネームしてNuxtのプロジェクト配下に設置します。
Jsonファイルは 設定 > サービスアカウント > 新しい秘密鍵の生成
で作成できます
.gitignoreにserviceAccountKey.jsonを追加
+ # Firebase service account key
+ serviceAccountKey.json
.envファイルの作成
プロジェクト直下に .env
ファイルを作成し、以下の情報を埋めます
Firebase > 設定 > 全般から取得できます
FLAMELINK_API_KEY="<firebase-api-key>"
FLAMELINK_AUTH_DOMAIN="<firebase-auth-domain>"
FLAMELINK_DB_URL="<firebase-db-url>"
FLAMELINK_PROJECT_ID="<firebase-project-id>"
FLAMELINK_STORAGE_BUCKET="<firebase-storage-bucket>"
最終的なディレクトリ構造
5. コンポーネントから記事データを取得できるか確認
pages/index.vue
で取得テスト
export default {
components: {
Logo,
VuetifyLogo,
},
+ async asyncData({ app }) {
+ try {
+ const myposts = await app.flamelink.content.get({
+ schemaKey: 'myposts', // Flamelinkで設定したschemeID
+ populate: true
+ })
+ console.log({ myposts })
+ return { myposts }
+ } catch (err) {
+ console.log(err)
+ return { myposts: [] }
+ }
+ }
+
npm run dev
ブラウザのコンソールで記事情報が取得できているかを確認(Vueの開発ツールでもOK)
無事取得できていました
取得した記事をVuexで管理する
Flamelinkから取得した記事のデータは一覧ページと詳細ページで共通なので、Vuexでストア管理することにした
先ほどの index.vue
に記述した取得ロジックをstoreに移植する
storeの準備
store/index.ts
を作成し、state
, getters
, mutations
を用意
import { GetterTree, ActionTree, MutationTree } from 'vuex'
import { formatDate } from '~/utils/format' // *これは自前で用意した関数
import { PostData } from '~/types' // *これは自前で用意した型
export type RootState = ReturnType<typeof state>
export const state = (): {
posts: PostData[] | null
} => ({
posts: null
})
export const getters: GetterTree<RootState, RootState> = {
posts: state => state.posts
}
export const mutations: MutationTree<RootState> = {
FETCH_POST: (state, payload: PostData[]) => {
state.posts = payload
}
}
nuxtServerInitで記事の取得
nuxtServerInit というアクションがストア内に定義されて、かつ universal モードである場合は、Nuxt.js はそれをコンテキストとともに呼び出します(ただしサーバーサイドに限ります)。サーバーサイドからクライアントサイドに直接渡したいデータがあるときに便利です。
store/index.ts
にアクションを追加
Flamelink JS SDKの理解が少し足りず力技の部分もあるかもなのだが、
以下のとおり取得データを加工している
-
-
ソート用に記事作成日時データを追加
今回、date
にFlamelinkから作成日のデータが注入されるのだが、"2020-09-09T00:00:00+09:00"
のように、同日付の記事は時間の部分が固定になっていた
そのため同日作成の記事をうまくソートできないので、_fl_meta_.createdDate._seconds
から日時を取得し、createdDate
フィールドに設定
-
ソート用に記事作成日時データを追加
-
-
フォーマット化した日付を用意
1の日時データをもとにフォーマット化した日付文字列をformatedDate
フィールドに設定
-
フォーマット化した日付を用意
-
-
記事のソート
1の日時データをもとに記事データを降順にソート
-
記事のソート
export const actions: ActionTree<RootState, RootState> = {
async nuxtServerInit ({ commit }, { app }) {
const getData: {
[key: string]: PostData
} = await app.flamelink.content.get({
schemaKey: 'myposts',
populate: true
})
// データ加工
const myposts = Object.entries(getData)
.map(([k, v]) => {
const createdDate = new Date(v._fl_meta_.createdDate._seconds * 1000)
v.createdDate = createdDate // (1)
v.formatedDate = formatDate(createdDate) // (2)
return v
})
.sort((a: PostData, b: PostData) => a.createdDate > b.createdDate ? -1 : 1) // (3)
commit('FETCH_POST', myposts)
}
}
無事Vuexにぶち込まれました
画面の作成
記事一覧
pages/index.vue
- データ
myposts
に、ストアで用意したゲッター経由でデータを取得して格納 -
myposts
をv-for
でループ - リンクパラメータには
id
(記事ID)をセット
※ BlogList.vue
は日付と見出しを受け取って表示するだけのコンポーネント
<template lang="pug">
v-layout(column justify-center align-center)
v-row
v-col(v-for="item in myposts" :key="item.id" cols="12")
nuxt-link(:to="`/article/${item.id}`")
blog-list(
:title="item.title"
:date="item.formatedDate"
)
</template>
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import BlogList from '~/components/BlogList.vue'
@Component
export default class PageIndex extends Vue {
myposts = this.$store.getters.posts
}
</script>
記事詳細
pages/article/_id.vue
※ 動的ルーティングさせるのでファイル名をスネークケースに
-
index.vue
同様に記事データをストアから取得 -
this.$route.params.id
にわたってくる記事IDで該当記事データをフィルター
<template lang="pug">
div
h1
| {{ entry.title }}
div(v-html="entry.content")
</template>
<script lang="ts">
import {Component, Vue} from 'nuxt-property-decorator'
import { PostData } from '~/types'
@Component
export default class PageArticle extends Vue {
myposts = this.$store.getters.posts
get entry() {
const id = this.$route.params.id
const entry = this.myposts.find((e: PostData) => {
return e.id === id
})
return entry
}
}
</script>
GOOD JOB
雑感
記事の投稿フォーマットからFirestoreへの登録までFlamelinkが担ってくれるので、かなり簡単にブログが実装できるのを感じました
とりあえず今回は記事の取得、表示までを実装してみましたが、現段階ではブログ更新の都度 nuxt generate
して firebsae deploy
でホストさせる仕組みにとどまっています
引き続きJamstackで静的配信できるよう勉強していきたいと思います
FORK Advent Calendar 2020
14日目 GCS へのファイルアップロードをトリガーにBigQueryにデータをインポートする。 @shuhei4009
16日目 Full Static Generationを試す @AsaToBan