FirebaseSDKまとめ[Auth, Firestore, Storage]


どうも、改名しました。
ファンタジスタ☆みゆきです。

今回は開発中のアプリでFirebaseのAuthentication, Firestore, Storageを活用する機会があったのでそれらについて簡単に使用方法をまとめてみたいと思います。

## アプリのsetup

Firebaseを利用するにあたって、まずプロジェクトにFirebaseを追加する必要があります。
基本的には[公式ドキュメント](https://firebase.google.com/docs/web/setup)にそって設定していけば問題ありませんが以下に簡単に手順を記します。

### npmでのモジュールインストール

まずはnpmを使用してFirebaseをインストール。

```
npm install firebase
```

### firebase設定ファイルの作成

次にFirebaseの設定ファイルをプロジェクトのimportしやすいディレクトリに作成します。

```js:firebase.js
import firebase from 'firebase/app';
import "firebase/auth";
import "firebase/firestore";
import "firebase/storage";

const firebaseConfig = {
  apiKey: "API_KEY",
  authDomain: "AUTH_DOMAIN",
  projectId: "PROJECT_ID",
  storageBucket: "STORAGE_BUCKET",
  messagingSenderId: "MESSAGING_SENDER_ID",
  appId: "APP_ID",
  measurementId: "MEASUREMENT_ID"
};

// Initialize Firebase
firebase.initializeApp(firebaseConfig);

export default firebase;
```

設定ファイルの説明ですが、まずimport文で使用するFirebaseのサービスを読込ます。
次に、firebaseConfigというオブジェクトを作り、そこにプロジェクトの各値を格納します。尚、これはFirebaseのプロジェクトページから発行できます。
最後に、firebaseを出力すれば任意のファイルからfirebaseをimportして使用することが可能になります。

※またこれは少し古い書き方で、現在はESMを使用する書き方が推奨されています。

## Authentication

### FirebaseUIのsetup

![firebase-basic01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208060/81bb388c-a32a-31ad-bb77-8f0b2ef1da16.png)


Firebaseには上図のような認証用のUIコンポーネントが存在します。OAuthプロバイダーの認証もできますし、特別UIをカスタマイズする必要がなければFirebaseUIを使うのが良いと思います。

FirebaseUIはCDNかnpmモジュールかBowerコンポーネントかを使って使用することが可能です。

FirebaseUIを使うためにまずレンダリング用のhtmlファイルを作成します。

```html:login.html
    <h1>Please Login</h1>
    <div id="firebaseui-auth-container"></div>
    <div id="loader">Loading...</div>
```

次に、FirebaseUIを使用するJavaScriptを作成します。

```js:login.js
    var firebaseui = require('firebaseui');
    var ui = new firebaseui.auth.AuthUI(firebase.auth());
    var uiConfig = {
    callbacks: {
        signInSuccessWithAuthResult: function(authResult, redirectUrl) {
        // User successfully signed in.
        // Return type determines whether we continue the redirect automatically
        // or whether we leave that to developer to handle.
        console.log(authResult, redirectUrl);
        return true;
        },
        uiShown: function() {
        // The widget is rendered.
        // Hide the loader.
        document.getElementById('loader').style.display = 'none';
        },
    },
    // Will use popup for IDP Providers sign-in flow instead of the default, redirect.
    signInFlow: 'popup',
    signInSuccessUrl: './',
    signInOptions: [
        // Leave the lines as is for the providers you want to offer your users.
        firebase.auth.EmailAuthProvider.PROVIDER_ID,
        firebase.auth.GoogleAuthProvider.PROVIDER_ID,
        firebase.auth.FacebookAuthProvider.PROVIDER_ID,
        firebase.auth.TwitterAuthProvider.PROVIDER_ID,
        // firebase.auth.GithubAuthProvider.PROVIDER_ID,
        // firebase.auth.PhoneAuthProvider.PROVIDER_ID
    ],
    // Terms of service url.
    tosUrl: '<your-tos-url>',
    // Privacy policy url.
    privacyPolicyUrl: '<your-privacy-policy-url>'
    };
    
    // The start method will wait until the DOM is loaded.
    ui.start('#firebaseui-auth-container', uiConfig);
```

基本的に[公式ドキュメントのテンプレート](https://firebase.google.com/docs/auth/web/firebaseui)のまま使っています。
認証させたいOAuthプロバイダー以外はコメントアウトすればすぐにテンプレートのUIがHTML上で使えるようになります。

### ユーザーの管理

Firebaseではユーザーをサインイン・認証させたり、ログイン中のユーザーの認証情報を取得したり、サインアウトさせたりすることが可能です。

私が一番よく使うものはログインユーザの情報を取得するコードで以下のように記述できます。

```js:login.js
firebase.auth().onAuthStateChanged((user) => {
  if (user) {
    // User is signed in, see docs for a list of available properties
    // https://firebase.google.com/docs/reference/js/firebase.User
    var uid = user.uid;
    // ...
  } else {
    // User is signed out
    // ...
  }
});
```

あと、ユーザをサインアウトさせたい場合は以下のように簡単に書けます。

```js:login.js
firebase.auth().signOut()
.then(() => {
  // Signed out
})
```

## Firestore

FirestoreはFirebaseが提供するNoSQLデータベースです。アプリからはSDKやREST API、RPC APIから接続することが可能です。
データモデルは[公式](https://firebase.google.com/docs/firestore)が掲載しているものが一番分かりやすく以下のような構造をしています。

![firebase-basic02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208060/07f68a11-3b71-870e-7363-3d73f53b8aec.png)

### CRUD処理

※以下はウェブバージョン8の記法を用いています。2021年9月時点最新の記法はウェブならバージョン9となりますのでご理解ください。

### データの追加(set)

以下のようにデータを追加するコレクションとドキュメントを指定してデータを追加します。データは基本的にオブジェクトを用います。

```js
db.collection("collectionName").doc("docId").set(data);
```

また、指定するドキュメントidがない場合は以下のように記述すればFirebaseが任意のドキュメントidを設定してくれます。

```js
db.collection("collectionName").add(data);
```

### データの取得(get)

データの取得は対象がコレクションかドキュメントかによって取得の仕方が少し違います。

ドキュメントの場合は以下のように単一のデータを取得することができ、データはdoc.data()で確認できます。

```js
db.collection("collectionName").doc("docId").get()
.then((doc) => {
    if (doc.exists) {
        console.log("Document data:", doc.data());
    } else {
        // doc.data() will be undefined in this case
        console.log("No such document!");
    }
}).catch((error) => {
    console.log("Error getting document:", error);
});
```

コレクションの場合は必然的に複数ドキュメントを取得することになるので以下のようにforEachでドキュメントを一つずつ取得してあげる必要があります。

```js
db.collection("collectionName").get()
.then((querySnapshot) => {
        querySnapshot.forEach((doc) => {
            // doc.data() is never undefined for query doc snapshots
            console.log("Document data:", doc.data());
        });
    })
    .catch((error) => {
        console.log("Error getting documents: ", error);
    });
```

また、コレクションを取得する際はwhere句で取得するドキュメントを絞り込むことも可能です。
例えば、nameがAliceのドキュメントのみを取得するような場合は以下のような書き方ができます。

```js
db.collection("collectionName").where("name", "==", "Alice").get()
```

### データの更新(update)

```
db.collection("collectionName").doc("docId").update({
    key: "newValue"
});
```

### データの削除(delete)

```
db.collection("collectionName").doc("docId").delete();
```


## Storage

Firebaseはユーザが投稿する画像や動画を格納するためのStorageを用意しています。Storageにあるデータの参照や、データのアップロード・ダウンロードができるのですが本稿ではデータの参照だけ説明しますのでそれ以外の解説は[公式ドキュメント](https://firebase.google.com/docs/storage)をご参考ください。

### データの参照

まず、データをレンダリングするHTMLを作成します。

```html
<div id="myimg">
```

次にデータを参照するJavaScriptは以下のようになります。
データを表示する方法はblobで直接表示する方法とimgタグに挿入する方法があります。

```js
//ストレージのルートのリファレンスを取得
var storageRef = firebase.storage().ref();
console.log(storageRef.fullPath);
console.log(storageRef.name);
console.log(storageRef.parent);
console.log(storageRef.bucket);
//ストレージのrootにあるdata.pngのリファレンスを取得, 英語名のみ
var imgSample = storageRef.child('data.png');    
//htmlロード完了したらストレージの画像を表示してみる
window.onload = function() {
imgSample.getDownloadURL().then(function(url) {
    // `url` is the download URL for 'images/stars.jpg'

    // This can be downloaded directly:
    var xhr = new XMLHttpRequest();
    xhr.responseType = 'blob';
    xhr.onload = function(event) {
    var blob = xhr.response;
    console.log(event, blob);
    };
    xhr.open('GET', url);
    xhr.send();

    // Or inserted into an <img> element:
    var img = document.getElementById('myimg');
    img.src = url;
    console.log(url);
}).catch(function(error) {
    // Handle any errors
    console.log(error);
});

// Get metadata properties
imgSample.getMetadata().then(function(metadata) {
    // Metadata now contains the metadata for 'images/forest.jpg'
    console.log(metadata);
}).catch(function(error) {
    // Uh-oh, an error occurred!
    console.log(error);
});
};
```

## おわりに

Firebaseにはアプリ開発で必ず使う機能がとても良い感じに用意されているので、こと個人開発においては使わない手はないと思っております。
また、AWS等の諸サービスよりも上手く使えばアプリを安価に抑えられる点もFirebaseの美点であると言えるでしょう。
SDKの使い方は慣れるとそう難しくないので皆さまも是非マスターしてみてはいかがでしょうか。

## 参考

正直Firebaseの公式ドキュメントがかなり分かりやすいので開発の際は公式ドキュメントさえ読めば問題ないかと思います。
Firebaseの基礎的な日本語記事が少ないのも、公式ドキュメントの分かりやすさ故だと思っています。

1. [Firebaseをプロジェクトに追加する](https://firebase.google.com/docs/web/setup)
2. [FirebaseUI でウェブアプリに簡単にログイン機能を追加する](https://firebase.google.com/docs/auth/web/firebaseui)
3. [Cloud Firestore](https://firebase.google.com/docs/firestore)
4. [Cloud Storage for Firebase](https://firebase.google.com/docs/storage)