Firestore
からデータを取得して表示するサンプルを作成します。
Jest
でテストもします。
完成したプロジェクトは以下のリポジトリになります。
https://github.com/TAC/nuxt-firestore-example
Firestoreに接続する
まずはFirestore
へ接続します。
Firebase
のアカウント作成などは他のサイトなどで確認して下さい。
接続情報は.env/development.js
に記載して、nuxt.config.ts
で読み込むようにします。
.env/development.js
module.exports = {
apiKey: "<your apiKey>",
authDomain: "<your authDomain>",
databaseURL: "<your databaseURL>",
projectId: "<your projectId>",
storageBucket: "<your storageBucket>",
messagingSenderId: "<your api messagingSenderId>"
}
app/nuxt.config.ts
import NuxtConfiguration from '@nuxt/config'
import pkg from './package.json'
import envSet from '../.env/environment.js'
const nuxtConfig: NuxtConfiguration = {
mode: 'universal',
srcDir: 'src',
env: envSet,
...
}
設定値はprocess.env.apiKey
という形式で利用できます。
次にFirebase
と Firestore
の初期設定を行うプラグインを作成します。
app/src/plugins/firebase.ts
import firebase from 'firebase/app'
if (!firebase.apps.length) {
firebase.initializeApp({
apiKey: process.env.apiKey,
authDomain: process.env.authDomain,
databaseURL: process.env.databaseURL,
projectId: process.env.projectId,
storageBucket: process.env.storageBucket,
messagingSenderId: process.env.messagingSenderId
})
}
export default firebase
ここまでは 前回のAuthenticationの実装 でやりました。
Firestore へ接続するプラグインは以下のように記述します。
app/src/plugins/firestore.ts
import firebase from '@/plugins/firebase'
import 'firebase/firestore'
const db = firebase.firestore()
export default db
修正箇所
ユーザのニックネームを Firestore
へ保存します。
users
コレクションを作成して、ドキュメントIDには Authentication
の ユーザーUID
を利用します。
認証データをそのまま保存していた store/models/users.ts
を変更していきます。
store/models/users.ts
import {
VuexModule,
Module,
getter,
mutation,
action,
getRawActionContext
} from 'vuex-class-component'
import firebase from '@/plugins/firebase'
import db from '@/plugins/firestore'
import Users from '@/types/store/models/users'
const timestamp = firebase.firestore.FieldValue.serverTimestamp()
@Module({ namespacedPath: 'models/users/', target: 'nuxt' })
class Store extends VuexModule {
@getter user: Users = {}
get isAuthenticated() {
return !!this.user.id
}
@mutation
public SET_USER(payload: Users) {
this.user = payload
}
@mutation
public UNSET_USER() {
this.user = {}
}
@action({ mode: 'raw' })
public async set(uid: string) {
if (uid) {
const ref = db.collection('users').doc(uid)
const doc = await ref.get()
if (doc.exists) {
const data: Users = {
id: doc.id,
nickName: doc.data()!.nickName,
createdAt: doc.data()!.createdAt,
updatedAt: doc.data()!.updatedAt
}
const context = getRawActionContext(this)
context.commit('SET_USER', data)
}
}
}
@action({ mode: 'raw' })
public async unset() {
const context = getRawActionContext(this)
context.commit('UNSET_USER')
}
@action({ mode: 'raw' })
public async create(uid: string) {
if (uid) {
const ref = db.collection('users').doc(uid)
const doc = await ref.get()
if (!doc.exists) {
const nickName = ''
await ref.set({
nickName,
createdAt: timestamp,
updatedAt: timestamp
})
const data: Users = {
id: uid,
nickName
}
const context = getRawActionContext(this)
context.commit('SET_USER', data)
}
}
}
@action({ mode: 'raw' })
public async update(user: Users) {
const ref = db.collection('users').doc(user.id)
const doc = await ref.get()
if (doc.exists) {
await ref.update({
nickName: user.nickName,
updatedAt: timestamp
})
const context = getRawActionContext(this)
context.commit('SET_USER', user)
}
}
}
export default Store.ExtractVuexModule(Store)
以前は mutation
しかありませんでしたが、 action
を追加して、そこでユーザデータの登録・作成を行うようにしています。
認証のサインイン時の処理も修正します。
store/modules/auth.ts
@action({ mode: 'raw' })
public async signIn(provider) {
const context = getRawActionContext(this)
context.commit('models/users/UNSET_USER', null, { root: true })
await firebase
.auth()
.signInWithPopup(provider)
.then(result => {
if (result && result.user && result.additionalUserInfo) {
if (result.additionalUserInfo.isNewUser) {
// new user
context.dispatch('models/users/create', result.user.uid, {
root: true
})
} else {
// exists user
context.dispatch('models/users/set', result.user.uid, {
root: true
})
}
}
})
.catch(error => {
console.error(error)
throw error
})
}
result.additionalUserInfo.isNewUser
をみて新規ユーザかどうかを判定して、users
コレクションへの登録処理を実行しています。
一度認証してしまっていると上記の値が false
になってしまうので、動作確認する場合は新しいアカウントか Firebase Console で認証情報を削除すると users
コレクションが登録されると思います。
テストの作成
認証処理の時にも使った firebase-mock
を使ってテストケースを作っていきます。
app/tests/store/models/users.test.js
import db from '@/plugins/firestore'
...
describe('store/models/users.ts', () => {
beforeAll(() => {
db.autoFlush()
})
beforeEach(async () => {
// mock data
user = {
nickName: 'alice',
createdAt: '2019-10-26 00:00:00',
updatedAt: '2019-10-26 00:00:00'
}
await db
.collection('users')
.doc('alice')
.set(user)
})
plugins/firestore
の import
を追加してます。
前回の設定で firestore
の呼び出しはモックになっています。
beforeAll
でFirestoreへの処理がすぐ反映されるように autoFlush()
を実行しておきます。
beforeEach
でモックデータを登録しています。
モックなので実際にFirestoreには登録されません。
app/tests/store/models/users.test.js
describe('actions', () => {
test('set', async () => {
await store.dispatch('models/users/set', 'alice')
expect(store.getters['models/users/user']).toMatchObject(user)
})
test('unset', async () => {
await store.dispatch('models/users/unset')
expect(store.getters['models/users/user']).toMatchObject({})
})
test('create', async () => {
await store.dispatch('models/users/create', 'bob')
const checkData = {
id: 'bob',
nickName: ''
}
expect(store.getters['models/users/user']).toMatchObject(checkData)
})
test('update', async () => {
const updateData = {
id: 'alice',
nickName: 'test'
}
await store.dispatch('models/users/update', updateData)
const data = store.getters['models/users/user']
expect(data).toMatchObject(updateData)
expect(data.updatedAt).not.toBe(user.updatedAt)
})
})
追加した action
メソッドのテストを作成します。
なお、getters
や mutation
は動作を変えていないので、修正しなくても正常にテストを通過するはずです。
以上、簡単にですが Firestore
への接続とテスト方法の紹介でした!
Written with StackEdit.