【Next.js】Cloud FirestoreをNext.jsに組み込みデータ取得
こちらではNext.jsのプロジェクトにFirebaseのCloud Firestoreを組み込む方法について解説をしていきます。
Firebaseでプロジェクトの作成〜コレクションの作成
Firebaseにアクセスし、Consoleへ移動しましょう。
そしてプロジェクトの追加をします。
プロジェクト名は任意で構いません。
プロジェクトが生成されると以下のようなページに遷移します。
サイドメニューの「開発」から「Database」を選択します。するとCloud Firestoreのページに遷移しました。
「データベースの作成」ボタンをクリックすると、モーダルウィンドウが出てきます。
「テストモードで開始」を選択して「次へ」を押すと、ロケーションを選ぶことができるので東京リージョンの「asia-northeast1」を選びましょう。
「完了」ボタンを押すと、データベースの作成が始まります。
ここまでの手順で以下の画面に遷移します。
次に「コレクションの開始」をクリックします。
ここからMySQLでいうところのテーブル作成となります。
今回はpostsというドキュメントIDで設定しましょう。
MySQLで例えると以下のようなコレクションを作成します。
フィールド名とValueを設定します。
そうすると設定した中身でコレクションができましたね。
あまり聞きなれない単語が多いかもしれませんが、
- コレクション = テーブル
- 一意のID = PRIMARY KEY
- ドキュメント = カラムとレコード
というイメージです。
Next.js側の設定
次にCloud FirestoreをNext.js側に組み込んでいきます。
組み込むにあたってはNext.jsの新規プロジェクトをnpm init で立ち上げてもらってもいいですし、
同じ環境下で行いたいということであれば、こちらのブランチをgit cloneしてもらっても構いません。
まずはルートディレクトリに「.env」を作成しましょう。
こちらにCloud Firestoreにおける設定情報を記入していくため、「.gitignore」を開き、「.env」を追加しておきます。
.gitignore
node_modules .next #追加 .env
Firebaseに戻り、サイドメニューの「Project Overview」を選択。
画像の垢枠の部分をクリックしましょう。
ウェブアプリの登録画面が出てくるので、こちらも任意で命名します。
その後、スクリプトタグが出てくるため以下の部分をコピーしましょう。
var firebaseConfig = { apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", authDomain: "pf-fanclub.firebaseapp.com", databaseURL: "https://pf-fanclub.firebaseio.com", projectId: "pf-fanclub", storageBucket: "pf-fanclub.appspot.com", messagingSenderId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", appId: "1:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }; // Initialize Firebase firebase.initializeApp(firebaseConfig);
そして、先ほど作成した.envにペーストするのですが、スクリプトを成形する必要があります。
.env
FIREBASE_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx FIREBASE_AUTH_DOMAIN=pf-fanclub.firebaseapp.com FIREBASE_DATABASE_URL=https://pf-fanclub.firebaseio.com FIREBASE_PROJECT_ID=pf-fanclub FIREBASE_STORAGE_BUCKET=pf-fanclub.appspot.com FIREBASE_MESSAGING_SENDER_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx FIREBASE_APP_ID=1:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
「""」や「,」などは削除し、環境変数として命名しましょう。
次にデータベース設定を行っていきます。
環境変数をNode.js下で扱うためのdotenvモジュールをインストールしておきましょう。
npm install --save dotenv
dotenvモジュールについては以下の記事に分かりやすい解説がありましたので引用します。
Node.js のプログラムから環境変数を参照するには process.env を参照します。
ユーザー設定を環境変数で行うようにしているアプリはよくあるのですが、たかが 1 つのアプリのために環境変数を設定するのは嫌だというユーザーは少なからずいます(設定がどこで行われているのかわかりにくいという理由もあります)。
dotenv モジュールを使用すると、カレントディレクトリに置かれた .env ファイルを読み込み、そこに記述されたキー&バリューのペアを process.env 経由で参照できるようになります。
つまり、ユーザはアプリの設定を、従来通り環境変数で行うこともできるし、.env ファイルでも行うことができるようになります。 環境変数を使ってアプリの挙動を変えるような実装をしている場合は、.env ファイルによる設定もサポートしておくと親切です。
環境変数の代わりに .env ファイルを使用する (dotenv) | まくまくNode.jsノート
次にfirebaseをnpm経由でインストールしていきます。
npm install --save firebase
次にルートディレクトリに以下のディレクトリとファイルを作成しましょう。
lib/db.js
import firebase from 'firebase/app' import 'firebase/firestore' let db; try { const config = { apiKey: process.env.FIREBASE_API_KEY, authDomain: process.env.FIREBASE_AUTH_DOMAIN, databaseURL: process.env.FIREBASE_DATABASE_URL, projectId: process.env.FIREBASE_PROJECT_ID, storageBucket: process.env.FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID, appId: process.env.FIREBASE_APP_ID }; firebase.initializeApp(config); // Firestoreインスタンスを作成 db = firebase.firestore(); } catch (error) { console.log(error); } module.exports = { // 本来、initializeAppによる初期化は一度きりのため、 // 初期化の結果のみを切り出してexportする db };
Firebaseで表示されたスクリプトを先ほど.envに設定した環境変数に置き換えました。
これで外部からデータベースの設定情報を閲覧されることはありません。
また環境変数の前にprocess.env.を付与することで、dotenvモジュールによってprocess.envを経由した参照になります。
次に以下のファイルをルートディレクトリに作成しましょう。
next.config.js
const webpack = require('webpack'); require('dotenv').config(); module.exports = { webpack: config => { const env = Object.keys(process.env).reduce((acc, curr) => { acc[`process.env.${curr}`] = JSON.stringify(process.env[curr]); return acc; }, {}); config.plugins.push(new webpack.DefinePlugin(env)); return config; } };
もしこの段階で
Firebase: Firebase App named '[DEFAULT]' already exists (app/duplicate-app).
といったエラーが出た場合、色々な原因があり、それに応じた解決策があるようですが、initializeAppを複数回行った覚えがないという場合には以下の方法で解決できるかもしれません。(私の場合はキャッシュが消して開発環境を再構築することで解決できました)
Firebase: Firebase App named '[DEFAULT]' already exists (app/duplicate-app).というエラーに悩まされたときに - Qiita
最後に表示部分を作成します。
pages/Posts.js
import { db } from '../lib/db'; import React from 'react' export default class Posts extends React.Component { static async getInitialProps() { // db.jsのfirebaseのDB接続ファンクション // DBのpostsコレクション内を全て取得した結果 = result let result = await new Promise((resolve, reject) => { db.collection('posts') .get() .then(snapshot => { let data = [] snapshot.forEach((doc) => { data.push( Object.assign({ id: doc.id }, doc.data()) ) }) resolve(data) }).catch(error => { reject([]) }) }) return {posts: result} } handleDelete = (id) => { console.log(id) } render() { const posts = this.props.posts return ( <React.Fragment> {posts.map(post => <div className="post" key={post.id}> <h2> {post.title} </h2> <p> {post.body} </p> <button onClick={this.handleDelete.bind(this, post.id)}>削除</button> </div> )} <style jsx>{` .post { width: 40%; border: 1px solid black; background-color: gray; margin-bottom: 10px; } `}</style> </React.Fragment> ); } }
これでFirebaseのCloud Firestoreに登録されているデータが表示されたはずです!