今回は、Cloud Firestore(以後、Firestoreと呼びます)にNode.jsアプリケーションからのファイル操作方法についてまとめていきます。
公式ドキュメントを参考に実装すればあまり迷うことはありませんが、自身の備忘録としてまとめていきますので、参考程度に見ていただければと思います。
また、他にもFirebase関連の記事を書いていますので、参考にしていただければと思います。
Firebaseについて
Firebaseは、Googleが提供するモバイルアプリおよびWebアプリケーションの開発プラットフォームです。いわゆるBaaS(Backend as a Service)の代表的な一つで、バックエンドの機能を提供してくれるサービスとも言えます。開発者はFirebaseを使用して、リアルタイムデータベース、認証、ストレージ、ホスティング、クラウド関数などの機能を簡単に統合することができます。Firebaseはクラウドベースのサービスであり、スケーラビリティに優れ、セキュリティと信頼性が高いのが特徴です。また、シンプルなAPIと直感的なダッシュボードを提供するため、開発者は迅速にアプリケーションを構築・展開・運用できます。
Cloud Firestoreについて
概要
Cloud Firestore は、Firebase と Google Cloud からのモバイル、ウェブ、サーバー開発に対応した、柔軟でスケーラブルなデータベースです。Firebase Realtime Database と同様に、リアルタイム リスナーを介してクライアント アプリ間でデータを同期し、モバイルとウェブのオフライン サポートを提供します。これにより、ネットワーク レイテンシやインターネット接続に関係なく機能するレスポンシブ アプリを構築できます。Cloud Firestore は、その他の Firebase および Google Cloud プロダクト(Cloud Functions など)とのシームレスな統合も実現します。
https://firebase.google.com/docs/firestore?hl=ja
普段よく使っているRDB(リレーショナル・データベース)ではなく、NoSQLのサーバーレスデータベースになる点が重要
主な特徴
- 柔軟性
Cloud Firestoreは階層型データ構造で柔軟なデータモデルを提供。ドキュメント内にコレクションやネストオブジェクトを持てる。 - 高機能なクエリ
個別ドキュメント取得や複数ドキュメント取得にクエリ利用。連鎖フィルタと並べ替え組み合わせ、デフォルトでインデックス付き。 - リアルタイムアップデート
データ同期で接続端末のデータ更新。1回限りの取得クエリも効率的に実行可能。 - オフラインサポート
アクティブなデータキャッシュ。オフライン時も書き込み、読み取り、クエリ実行可能。オンライン時に同期。 - スケーラビリティ設計
Google Cloudの強力な機能提供。マルチリージョンデータ複製、強整合性、バッチ処理、トランザクションサポートなど。大規模アプリと過酷なワークロードに対応。
実装の流れ
開発環境
- Node.js:
v18.15.0
- npm:
v9.5.0
- TypeScript:
v5.1.6
- OS:
MacOS Monterey
構築手順
基本的な進め方については下記ドキュメントを参考にしていますので、詳細が気になる方は参照してください。
また、具体的なファイルの操作方法について参照したい場合は、「ファイル操作の実装」だけを見て頂ければと思います。
Firebaseプロジェクトのセットアップ
まずはFirebase上でプロジェクトを作成しない限りは前に進めないため、下記を参考にプロジェクトを作成する。
すでに作成済みの場合は、このステップは不要となる。
データベースの作成
公式ドキュメントの手順に沿ってデータベースを作成していきます。
- データベースを作成
- 本番環境モードかテストモードを選択 ※今回はテストモード
- ロケーションの選択
- Firestoreの有効化
Firestoreのコンソール画面が表示されたら準備完了
ローカルに実行環境構築
実行環境の準備
今回は、Functionsの時のように初期化等は不要なので、適当なNode.js & TypeScriptの土台となるアプリケーションを構築します。以下の参考記事を載せておきます。
Firebase Admin SDKのパッケージをインストール
$ npm install --save firebase-admin
Firebaseプラットフォームをバックエンドで使用する際に利用される公式の開発キットです。このSDKを使用することで、サーバーサイドの環境(Node.jsなど)からFirebaseプロジェクトにアクセスし、データベースへの書き込みや認証、クラウド機能の実行などFirebaseの機能を利用できる。
今回は、Storageに対してバケット取得やファイル操作のために利用する。
認証情報(JSON)の取得
ここからが本題です。
まずはStorageと疎通するための認証情報を取得します。
コンソール上で秘密鍵のダウンロード
- Firebaseコンソールにアクセスし、作成したプロジェクトに移動する
- 歯車マークから「プロジェクトの設定」をクリックし、「サービスアカウント」タブへ移動(画像参考)
- 「新しい秘密鍵の生成」ボタンをクリックする
- 表示内容を確認し、「キーを生成」ボタンをクリックする
- ダウンロードが終われば完了
秘密鍵を特定場所へ移動
秘密鍵関連は.ssh配下に置いているので移動させますが、参照できればどこでもOK
$ mv sample-project-xxxxxxx-xxxxxx.json ~/.ssh/
Firebase Admin SDK の初期化
この時点での完成系はこちら
import { cert, initializeApp } from 'firebase-admin/app';
import { load } from 'ts-dotenv';
const env = load({
MY_FIREBASE_CREDENTIALS: String,
});
initializeApp({
credential: cert(env.MY_FIREBASE_CREDENTIALS),
});
const db = getFirestore();
Firebase Admin SDK の各種サービスを使うためには、initializeApp
メソッドを呼び出してインスタンス化しておく必要があり、その際に必要なのが先ほどのダウンロードした認証情報(JSON)となる。
そのため、環境変数にMY_FIREBASE_CREDENTIALS
として格納し、認証情報を保持する必要がある。
MY_FIREBASE_CREDENTIALS=/Users/username/.ssh/sample-project-xxxxxx-xxxxxx.json
データ操作の実装
まずはFirestoreに任意のデータを追加する処理を実装してみます。collection
にコレクション名(=テーブル名)を指定し、doc
に一意なIDを指定します。
コレクションが存在しない場合は、新たなに作成してくれるため、事前に作成する処理を実行する必要はありません。
また、docにIDを指定しない場合は、ランダム文字列をIDとして自動生成してくれます。
import { cert, initializeApp } from 'firebase-admin/app';
import { getDownloadURL, getStorage } from 'firebase-admin/storage';
import { load } from 'ts-dotenv';
import * as path from 'path';
const env = load({
MY_FIREBASE_CREDENTIALS: String,
});
initializeApp({
credential: cert(env.MY_FIREBASE_CREDENTIALS),
});
const db = getFirestore();
const data = {
name: 'gorida',
age: 100,
isActive: true,
blogs: [
{
title: 'blog title v1',
content: 'blog content v1',
},
{
title: 'blog title v2',
content: 'blog content v2',
},
]
};
(async () => {
const docRef = db.collection('users').doc('100000');
await docRef.set(data);
})();
データ操作処理の一覧
ここからはよく使用するであろうデータ操作のメソッドとその使い方を紹介します。
とはいえ、公式ドキュメントに詳しく書いてあり、今回紹介するメソッド以外にもたくさんあるので、詳しくは参照してください。
特定データの取得
const snapshot = await db.collection('users').doc('100000').get();
console.log(snapshot.data())
{
blogs: [
{ title: 'blog title v1', content: 'blog content v1' },
{ title: 'blog title v2', content: 'blog content v2' }
],
name: 'gorida',
isActive: true,
age: 100
}
全てのデータを取得
const snapshot = await db.collection('users').get();
snapshot.forEach(doc => console.log(doc.data()))
条件指定によるデータの取得
const snapshot = await db.collection('users').where('isActive', '==', true).get();
snapshot.forEach(doc => console.log(doc.data()))
データの更新
await db.collection('users').doc('100000').update({ isActive: false });
データの削除
await db.collection('users').doc('100000').delete();
データの一括登録
const docs = [
{ id: '100001', name: 'user1', age: 20, isActive: true },
{ id: '100002', name: 'user2', age: 30, isActive: true },
];
const batch = db.batch();
docs.forEach(doc => {
const userRef = db.collection('users').doc(doc.id)
batch.set(userRef, doc)
})
await batch.commit()
データの一括更新
const batch = db.batch();
docs.forEach(doc => {
const userRef = db.collection('users').doc(doc.id)
batch.update(userRef, { isActive: false })
})
await batch.commit()
データの一括削除
const batch = db.batch();
docs.forEach(doc => {
const userRef = db.collection('users').doc(doc.id)
batch.delete(userRef)
})
await batch.commit()
サブコレクションの登録
users
コレクションの下にサブコレクションとしてblogs
を登録するパターン
const docs = [
{ id: '100001', name: 'user1', age: 20, isActive: true, blogs: [{title: 'title1', content: 'content1'}] },
{ id: '100002', name: 'user2', age: 30, isActive: true, blogs: [{title: 'title2', content: 'content2'}] },
];
(async () => {
const batch = db.batch();
docs.forEach(doc => {
const userRef = db.collection('users').doc(doc.id)
batch.set(userRef, {
name: doc.name,
age: doc.age,
isActive: doc.isActive,
})
doc.blogs.forEach(blog => {
const blogRef = userRef.collection('blogs').doc()
batch.set(blogRef, blog)
})
})
await batch.commit()
})();
まとめ
改めて今回はCloud Firestore へのデータ操作方法についてまとめてみました。ドキュメントも充実していましたので、そこまで迷うことなく実装できました。データ操作の実装はどこでも使えますし、NoSQLに慣れるための練習としては使いやすく、無料枠も大きいため非常にオススメです。
ここまでご覧いただきありがとうございました。