🍦

nuxt3 + firebase v9(Firestore, Authentication) を試してみる。

2021/12/20に公開

はじめに

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の作成します。

pages/index.vue
<template>
    <div>
        <p>hello</p>
    </div>
</template>
$ npm run dev

再度起動し、http://localhost:3000/にアクセスし、動作確認を行います。

eslint 等の設定

nuxt3 では eslint の設定が CLI で行えないので手動で行っていきます。

package.json
{
  "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"
  }
}
.eslintrc.js
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',
    },
};
.prettierrc.js
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 を有効にする

nuxt.config.ts
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 対応

nuxt.config.ts
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+.

https://github.com/nuxt-community/firebase-module

ライブラリのインストール

npm install --save firebase
firebase init

firestore の接続テスト

firebase の初期化処理

composables/firebaseInit.ts
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 コンソール上でよしなに作っておいてください。

composables/useFirestore.ts
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,
    };
};
useAuth.ts
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,
    };
};
index.vue
<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 を初使用のため、ベストな書き方ができていないと思いますので是非コメント等にて、指摘していただきたいです。

参考

https://v3.nuxtjs.org/

https://zenn.dev/ytr0903/articles/81c8bed9a60702

https://zenn.dev/azukiazusa/articles/nuxt3-new-features

GitHubで編集を提案

Discussion