フューチャー技術ブログ

Firebaseでお手軽!データ管理画面をつくる

はじめに

こんにちは!2017年9月入社の柏木です。夏休み自由研究連載13日目の記事です!🏖️

昨日は【入門】私を苦しめたDynamoDBという読み応えたっぷりの記事でした。
今回は、Firebaseを使って画面を開発してみようと思います。

本記事のゴール

下記を最短経路で作ってみます。
システムアドミンの人が使うようなマスタデータ管理画面を想定して、限られたユーザーにアクセスを制限すべく、認証機能も入れました。

  • Typescript×Reactのアプリを立ち上げる
  • Firebaseのデータベース(Firestore)にデータを用意する
  • Firebase Hostingを用いてデプロイする
  • Firestoreに格納されているデータを画面に表示する
  • Firebase Authenticationで認証する

環境

  • macOS Catalina (v10.15.6)
  • Node.js (v14.8.0)

実践

Typescript×Reactのアプリを立ち上げる

環境構築の手間を劇的に削減できるcreat-react-appを利用します。今回はtypescriptで実装したいので、オプションをつけてインストールしました。npm startで画面が立ち上がります。

~
$ npx create-react-app summer-vacation --typescript
$ cd summer-vacation
$ npm start

Firebaseのデータベース(Firestore)にデータを用意する

続いてデータを作成します。
まず、Firebase上に自身のプロジェクトを作成します。Googleのアカウントがあれば、誰でもFirebaseを始めることができます。Firebaseコンソールにアクセスし、「プロジェクトを追加」から指示に沿って設定を行うと、プロジェクトの作成完了です。

次に、画面に表示したいデータを作成します。
FirebaseにはFirestoreとRealtime Database、2つのデータベース機能が用意されています。違いはこちらの記事にわかりやすく書いてありました。今回は簡易なデータ構造なのでどちらでも問題ないですが、名前がかっこいいので前者を用います。

ブラウザ上でぽこぽこデータを投入し、準備完了です!

Firebase Hostingを用いてデプロイする

ホスティングが簡単にできると話題のFirebase Hostingを用いて、下記手順でアプリのデプロイを行います。

1. Firebaseに自分のアプリを登録する

まずは、Firebaseからアプリが利用するプロジェクトの情報を取得できるようにします。
歯車アイコンから設定画面に遷移し、「アプリを追加」の作業を行います。今回はweb画面なので、プラットフォームにwebを選択し、summer-vacationのアプリ名で追加します。設定が完了するとこのように、アプリで使用するconfig情報が取得できるようになりました!

2. アプリにFirebaseの設定を組み込む

2で取得した情報を作成したReactのアプリに紐付けます。ただこの情報は公開したくないので、環境変数として渡すようにします。
create-react-appにはdotenvの機能も組み込まれているので、.envファイルをレポジトリ直下に作成し、先ほどの情報を環境変数として定義します。create-react-appのルールで、変数のプレフィックスにREACT_APP_を使用しないと認識してくれないので注意が必要です。設定を行った後は、npm start を再実行し、コンパイルし直します。

続いて、firebaseのライブラリをインストールします。

~/summer-vacation
$ npm install firebase --save

最後にFirebaseとの紐付けを行います。ここで設定した環境変数を埋め込みます。

firebase/index.js
import firebase from 'firebase';

const env = process.env;

export const firebaseConfig = {
apiKey: env.REACT_APP_API_KEY,
authDomain: env.REACT_APP_AUTH_DOMAIN,
databaseURL: env.REACT_APP_DATABASE_URL,
projectId: env.REACT_APP_PROJECT_ID,
storageBucket: env.REACT_APP_STORAGE_BUCKET,
messagingSenderId: env.REACT_APP_MESSAGING_SENDER_ID,
appId: env.REACT_APP_APP_ID,
measurementId: env.REACT_APP_MEASUREMENT_ID,
};

// Firebaseを紐付け、初期化
export const firebaseApp = firebase.initializeApp(firebaseConfig);

// Firestoreのインスタンス作成
export const firebaseStore = firebaseApp.firestore();

export default firebase;

3. デプロイコマンドを実行できるようにする

最後に、ローカル環境からデプロイできるようにします。
公式ドキュメントの手順に沿ってfirebaseコマンドラインツールをローカル環境にインストールします。
ログインし、自身のGoogleアカウントと紐づけてプロジェクト一覧を確認すると、先ほど作成したプロジェクトが表示されました!

~/summer-vacation
$ npm install -g firebase-tools
$ firebase login
$ firebase projects:list
✔ Preparing the list of your Firebase projects
┌──────────────────────┬───────────────────────┬────────────────┬──────────────────────┐
│ Project Display Name │ Project ID │ Project Number │ Resource Location ID │
├──────────────────────┼───────────────────────┼────────────────┼──────────────────────┤
│ summer-vacation │ summer-vacation-xxxxx │ xxxxxxxxxxxx │ asia-northeast1 │
└──────────────────────┴───────────────────────┴────────────────┴──────────────────────┘

firebaseコマンドで初期化のコマンドを実行すると対話式で諸々の設定を行えます。

~/summer-vacation
$ firebase init
...
✔ Firebase initialization complete!

今回カスタマイズしたのは下記項目です。
ビルド実行時にreact-scriptsコマンドがモジュールを出力するのがbuildディレクトリなので、public directoryはbuild配下を指定します。

  • summer-vactionのプロジェクトを利用
  • Hostingのサービス、Cloud Firestoreを利用
  • public directoryに buildを指定
  • SPAとして利用

この状態でビルドし、デプロイします。
コマンドに表示されたURLにアクセスすると、画面が表示されました!

~/summer-vacation
$ npm run build
$ firebase deploy
...
✔ Deploy complete!
Hosting URL: https://example.web.app

Firestoreに格納されているデータを画面に表示する

いよいよFirestoreにアクセスしてみましょう!
先ほどインストールしたfirebaseのライブラリを使用して、Firestoreに格納したユーザーの名称を表示します。
React HooksのuseEffectを使って、初期描画後にデータ取得するメソッドを実行します。

App.tsx
  const [loading, setLoading] = useState(true);
const [users, setUsers] = useState<firebase.firestore.DocumentData[]>([]);

useEffect(() => {
const searchUsers = async() => {
// Firestoreのコレクションを指定してデータ取得。今回は全量を検索
const res = await fireStore.collection('users').get();
if (res.empty) return [];
const userList: firebase.firestore.DocumentData[] = [];
// DocumentData型にはmapメソッドが定義されていないため、forEachのループでデータを加工
res.forEach(doc => {
userList.push(doc.data());
})
setUsers(userList);
}

searchUsers();
setLoading(false);
}, []);

なんとライブラリ組み込みのメソッドを使用するだけで、簡単にFirestoreとの疎通、データ取得が実現できてしまいました。とってもお手軽ですね!

Firebase Authenticationで認証する

最後に、自身以外のアクセスをコントロールする仕組みも追加しておきます。
Firebaseの認証機能を簡単にクライアントに実装できるreact-firebaseuiを利用します。

1. Firebase Authenticationで認証方法を登録する

コンソールの設定画面から、「ログイン方法を設定」します。

私はGoogleアカウントの認証を使用することにしました。

2. アクセスを制御する仕組みを実装する

最初に、ホワイトリストとなるメールアドレスを環境変数に追加します。

.env
REACT_APP_VALID_MAIL_ADDRESSES=firebaseapp@example.com

次に画面にログイン機能を実装します。認証画面を用意してくれるライブラリを使い、手間を省きます。

~/summer-vacation
$ npm install --save react-firebaseui

先ほどと同じApp.tsxに実装を組み込みます。

App.tsx
const [myAccount, setMyAccount] = useState<firebase.User>();

useEffect(() => {
const searchUsers = async() => {
...
}

firebase.auth().onAuthStateChanged((user) => {
setLoading(false);
if (!user) return;
if (user.email !== process.env.REACT_APP_VALID_MAIL_ADDRESSES) return;
setMyAccount(user);
searchUsers();
});
}, []);

npm start で起動すると、アカウント認証画面が表示されました!

これで、Googleアカウントのメールアドレスが.envに定義したものと異なる場合、アクセスを弾くことができます。

コードの全量はこちらです。

App.tsx
import React, { useState, useEffect } from 'react';
import './App.css';

// firebase functions
import firebase from 'firebase';
import {fireStore} from './firebase/index'
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth';


function App() {
const [loading, setLoading] = useState(true);
const [users, setUsers] = useState<firebase.firestore.DocumentData[]>([]);
const [myAccount, setMyAccount] = useState<firebase.User>();

const uiConfig = {
signInFlow: 'popup',
signInSuccessUrl: '/',
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
],
};


useEffect(() => {
const searchUsers = async() => {
// Firestoreのコレクションを指定してデータ取得
const res = await fireStore.collection('users').get();
if (res.empty) return [];
const userList: firebase.firestore.DocumentData[] = [];
// DocumentData型にはmapメソッドが定義されていないため、forEachのループでデータを加工
res.forEach(doc => {
userList.push(doc.data());
})
setUsers(userList);
}

firebase.auth().onAuthStateChanged((user) => {
setLoading(false);
if (!user) return;
if (user.email !== process.env.REACT_APP_VALID_MAIL_ADDRESSES) return;
setMyAccount(user);
searchUsers();
});
}, []);

return (
<div className="App">
<header className="App-header">
{loading ? (
<p>
LOADING.....
</p>
) : !myAccount ? (
<p>
ログインが必要です
<StyledFirebaseAuth uiConfig={uiConfig} firebaseAuth={firebase.auth()} />
</p>
) :
users.map((user, index) => {
return <p key={index}> {user.name}</p>
})
}
</header>
</div>
);
}

export default App;

最後に

認証機能を含めた画面の開発がいとも簡単にできてしまいました。(環境構築のコマンドは確認含めたったの11行!)
Firebaseは本当に偉大でした。
アプリ作るってなんだか大変そう…と思ってる方の印象が少しでも変われば幸いです。
使いこなせばもっといろんなことができそうなので、引き続き触ってみたいと思います!
以上です。

参考