nuxt3 + firebase v9(Firestore, Authentication) を試してみる。
はじめに
Nuxt3 が発表されて、プライベートの開発で Nuxt2 を使用することが多かったので試してみようと思います。
同じく最近 firebase の v9 が発表されて、移行する必要があるらしいので、ついでにこちらも試していきます。
目標
- Nuxt3 のプロジェクトの作成
 - Firebase の Cloud Firestore からのデータ取得
 - Firebase Authentication を用いた Twitter ログインの実装
 
前提
- Firestore のコレクション(
articles)の作成と、初期データの挿入 - Authentication は Twitter を使用したものを実装する予定ですが、その初期設定
 
以上の 2 つに関しては今回は説明を省かせていただきます。
nuxt3 関連のセットアップと動作確認
プロジェクトの作成と動作確認
$ npx nuxi init nuxt3-firebase-test
$ cd nuxt3-firebase-test
$ npm run dev
http://localhost:3000/にアクセスすると、トップページが表示されます。
初期セットアップ
pages のみでの運用に切り替える
app.vueを削除してpages/index.vueの作成します。
<template>
    <div>
        <p>hello</p>
    </div>
</template>
$ npm run dev
再度起動し、http://localhost:3000/にアクセスし、動作確認を行います。
eslint 等の設定
nuxt3 では eslint の設定が CLI で行えないので手動で行っていきます。
{
  "private": true,
  "scripts": {
    "dev": "nuxi dev",
    "build": "nuxi build",
    "start": "node .output/server/index.mjs"
  },
  "devDependencies": {
    "@nuxtjs/eslint-config": "^7.0.0",
    "@nuxtjs/eslint-module": "^3.0.2",
    "@typescript-eslint/eslint-plugin": "^5.7.0",
    "@typescript-eslint/parser": "^5.7.0",
    "eslint": "^8.4.1",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-nuxt": "^3.0.0",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-vue": "^8.2.0",
    "nuxt3": "latest",
    "prettier": "^2.5.1",
    "typescript": "^4.5.4",
    "typescript-eslint": "^0.0.1-alpha.0"
  }
}
module.exports = {
    env: {
        browser: true,
        es2021: true,
    },
    extends: [
        'plugin:vue/vue3-recommended',
        'prettier',
        'plugin:@typescript-eslint/recommended',
        'plugin:nuxt/recommended',
    ],
    parserOptions: {
        ecmaVersion: 13,
        parser: '@typescript-eslint/parser',
        sourceType: 'module',
    },
    plugins: ['vue', '@typescript-eslint', '@typescript-eslint/eslint-plugin'],
    rules: {
        'no-unused-vars': 'off',
    },
};
module.exports = {
    trailingComma: 'es5',
    tabWidth: 4,
    semi: true,
    singleQuote: true,
};
$ npm install
上記を実行して完了です。
TypeScript の Strict type checks を有効にする
初めから TypeScript を用意してくれているのは嬉しいのですが、なぜだか Strict type checks (厳格モード)が有効になっていないです。例えば、以下のようなコードは Strict type checks が有効になっていれば noImplicitAny によりエラーを検出してくれるはずなのですが、現状の設定ではそうはなってくれません。
参考:Nuxt3 の新しい機能 - TypeScript の Strict type checks を有効にする
import { defineNuxtConfig } from 'nuxt3'
export default defineNuxtConfig({
  typescript: {
    strict: true
  }
})
SSR 対応
デフォルトでは SSR ではなく SPA になっているので、nuxt.config.ts で SSR オプションを true にします。
参考:Nuxt 3 を Google App Engine 本番環境 で SSR + キャッシュ運用してみる - SSR 対応
import { defineNuxtConfig } from 'nuxt3'
export default defineNuxtConfig({
  typescript: {
    strict: true
  },
  ssr: true,
})
firebase の設定
firebase init
nuxt で firebase を使う場合@nuxtjs/firebase を使えるが 、リポジトリの README にあるように firebase v9 は対応していないので今回は手動で実装することにします。
This module does not support the new modular syntax from Firebase v9+.
ライブラリのインストール
npm install --save firebase
firebase init
firestore の接続テスト
firebase の初期化処理
import { initializeApp } from 'firebase/app';
const firebaseConfig = {
    apiKey: [API_KEY],
    authDomain: [AUTH_DOMAIN],
    projectId: [PROJECT_ID],
    storageBucket: process.env.[STORAGE_BUCKET],
    messagingSenderId: [MESSAGING_SENDER_ID],
    appId: [APP_ID],
    measurementId: [MEASUREMENT_ID],
};
const firebase = initializeApp(firebaseConfig);
export default firebase;
composables にアクセス部分を実装する
コレクションarticlesとその中のデータは firebase コンソール上でよしなに作っておいてください。
import {
    getFirestore,
    collection,
    query,
    where,
    getDocs,
} from 'firebase/firestore';
import { firebaseInit } from './firebaseInit';
export const useFirestore = () => {
    const db = getFirestore(firebaseInit());
    const GetArticles = async () => {
        const q = query(
            collection(db, 'articles'),
        );
        const querySnapshot = await getDocs(q);
        querySnapshot.forEach((doc) => {
            console.log(doc.data());
        });
    };
    return {
        GetArticles,
    };
};
import {
    getAuth,
    signInWithPopup,
    TwitterAuthProvider,
    OAuthCredential,
    UserCredential,
} from 'firebase/auth';
export const useAuth = () => {
    const loginWithTwitter = async () => {
        const auth = getAuth();
        const provider = new TwitterAuthProvider();
        const result = await signInWithPopup(auth, provider).catch((error) => {
            const credential = TwitterAuthProvider.credentialFromError(error);
            console.log(error, credential);
        });
        const credential =
            result != null
                ? await TwitterAuthProvider.credentialFromResult(result)
                : null;
        if (result != null && credential != null) {
            console.log(result, credential);
        }
    };
    return {
        loginWithTwitter,
    };
};
<script setup lang="ts">
const { getArticles } = useFirestore();
const { userInfo, loginWithTwitter } = useAuth();
</script>
<template>
    <div>
        <p @click="getArticles">GetArticles</p>
        <p @click="loginWithTwitter">loginWithTwitter</p>
    </div>
</template>
各ボタンをクリックするとコンソールに表示されると思います。
終わりに
nuxt2 と比べ、自動 import が効いている分、かなり簡潔な書き方にすることができたと思います。
今回が typescript を初使用のため、ベストな書き方ができていないと思いますので是非コメント等にて、指摘していただきたいです。
参考
Discussion