Firebase 教學 - Node.js 操作 Firestore
當我們可以透過 Node.js 在後端運行 Firebase 之後,就能完成許多好玩的應用,這篇文章將會描述如何透過 Node.js 操作 Firestore,最後還會把程式碼部署到 Google Cloud functions,實現透過 http request 串接 Firestore 的應用範例。
延伸閱讀:
安裝 Node.js 相關模組
Firestore 在 Node.js 裡的用法跟網頁前端大同小異,在網頁前端僅需載入對應的 JavaScript,而 Node.js 裏頭僅需引用對應的模組即可,安裝套件的指令如下,首先建立並初始化一個 Node.js 專案。
$ npm init
接著安裝 firebase 模組以及 firestore 模組。
注意,Firestore 模組必須運行在 Node.js 10 以上的版本,可以透過 nvm 進行 Node.js 的版本更新。
$ npm install --save @google-cloud/firestore
使用時 require firestore 模組,宣告一個變數進行 firestore 初始化,並設定 Firestore 的 Project ID。
const Firestore = require('@google-cloud/firestore');
const firestore = new Firestore({
projectId: 'XXXXXXX'
});
然而 Firestore 並沒有像 Realtime Database 那樣的方便 ( 換句話說就是安全性較低 ),如果要使用 Node.js 操作,還得具備指定的金鑰才能正常運行,申請金鑰很簡單,進入 Google Cloud Platform,選擇 Firestore 的專案 ( 專案名稱就是建立 Firestore 時的專案名稱 ),左側選單選擇「API 和服務 > 憑證 > 建立憑證」,點選 「服務帳戶金鑰」。
進入後下拉選單選擇「App Engine default service account」,金鑰類型選擇「json」。
點選建立後會下載一個 json 檔案,將這個 json 檔案和操控 Firestore 的 Node.js 放在相同目錄下,基本的 Firestore 設定就差不多完成了,可以開始撰寫 Node.js 程式。
透過 http request 操作 RealTime Database
透過 Node.js 內建的http 模組
,可以建立個接收 http request 的 server,搭配內建的url 模組
,就能將收到的資料訊息轉換為物件格式,比較需要注意的地方有兩個:
- 第一、排除 favicon.ico:實作的過程中發現接收 http request 會包含 favicon.ico,為了避免後續的問題,使用簡單的邏輯判斷排除。
- 第二、重設 firebase.initializeApp:由於 firebase.initializeApp 預設名稱為 default,亦可透過第二個參數指定名稱,在同一組程式內名稱不能重複,如果需要重複初始化,除了產生新的名稱之外,就得使用
delete()
的方法移除初始化,這樣就能不斷接收 http request 不斷讀取或寫入資料庫了。( 如果不這麼做,重複發送 http request 會產生Firebase App named '[DEFAULT]' already exists
的錯誤訊息 )
下方的程式碼執行後,就會啟動一個本地端的 server,這時只要在網址列輸入https://網址?projectid=XXX&type=set&data=XXX
就能將資料寫入到預設 collection 為 default、doc 名稱為 default 的資料庫裡,在網址列輸入http://網址?XXX&type=get
就能讀取 collection 為 default 的資料庫資料。
此處我的 Firestore 資料庫使用「Firebase 教學 - Node.js 操作 Realtime Database」一文中的資料庫。
const http = require('http');
const url = require('url');
const Firestore = require('@google-cloud/firestore');
const server = http.createServer((req, res) => {
if (req.url !== '/favicon.ico') { // 因接收時會一併取得 undefined 的 facicon.ico,使用簡單的邏輯排除
const params = url.parse(req.url, true).query; // 取得網址每個參數
const collection = params.collection || 'default';
const doc = params.doc || 'default';
let data;
if(params.data){
data = JSON.parse(params.data);
}
const type = params.type;
const firestore = new Firestore({
projectId: params.projectid,
keyFilename: 'key.json' // 放入金鑰 json
});
const ref = firestore.collection(collection).doc(doc);
switch (type) { // 依據不同的參數寫入或讀取資料
case 'set':
ref.set(data).then(() => {
res.end(`set data to "${doc}" ok`);
});
break;
case 'add':
ref.add(data).then(() => {
res.end(`add data to "${doc}" ok`);
});
break;
case 'get':
firestore.collection(collection).get().then(e => {
let html = '';
e.forEach(d => {
html = `${html}<div>${d.id} : ${d.data().total} / ${d.data().good}</div>`;
});
res.end(html);
});
break;
}
}
});
server.listen(5000);
部署到 Google Cloud Functions
已經能在本地端運行 Node.js 服務並操作 Firestore 之後,下一步就是將這組程式部署到 Google Cloud Functions 裡,登入 Google 並進入 GCP ( Google Cloud Platform ) 後,在右方選單選擇 Cloud Functions。
請先參考我的這篇文章:簡易後端實作 ( Google Cloud Functions )
建立一個專案,專案建立後就新建一組函式,輸入函式名稱以及選擇 128 MB 記憶體。
Firestore 因為有一個金鑰的 json 檔案,因此無法像 Realtime Database 一樣直接在 Cloud functions 裡頭撰寫程式碼,所以這邊選擇「zip 檔」的方式上傳壓縮檔,並選擇 Node.js 10 ( Beta 版 ),又因為得上傳程式碼,所以先將剛剛我們在本地端運行的 Node.js 程式修改成下面這樣以符合 Cloud Functions 的撰寫原則。
const url = require('url');
const Firestore = require('@google-cloud/firestore');
exports.helloWorld = (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Request-Method', '*');
res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
res.setHeader('Access-Control-Allow-Headers', '*');
if (req.url !== '/favicon.ico') {
const params = url.parse(req.url, true).query;
const collection = params.collection || 'default';
const doc = params.doc || 'default';
let data;
if(params.data){
data = JSON.parse(params.data);
}
const type = params.type;
const firestore = new Firestore({
projectId: params.projectid,
keyFilename: 'key.json'
});
const ref = firestore.collection(collection).doc(doc);
switch (type) {
case 'set':
ref.set(data).then(() => {
res.end(`set data to "${doc}" ok`);
});
break;
case 'add':
ref.add(data).then(() => {
res.end(`add data to "${doc}" ok`);
});
break;
case 'get':
firestore.collection(collection).get().then(e => {
let html = '';
e.forEach(d => {
html = `${html}<div>${d.id} : ${d.data().total} / ${d.data().good}</div>`;
});
res.end(html);
});
break;
}
}
};
修改 package.json 的內容,讓部屬時會自動安裝 firebase 模組。
{
"name": "firestore",
"version": "0.0.1",
"dependencies": {
"@google-cloud/firestore": "^2.2.4"
}
}
最後將 index.js、package.json 和 key.json 放在同一個資料夾,並用加縮軟體壓縮成 zip 檔。
因為要上傳 zip 檔案到 GCP,所以要額外設定 Bucket 的資料夾名稱,這也是 GCP 的雲端儲存空間的服務之一,設定好 Bucket 的名稱後就可以上傳。
上傳完成後按下「建立」,就會開始部署程式,部署成功會看見函式前方出現綠色的勾勾圖示,在觸發條件的頁籤裡也會看見最終部署的網址。
在瀏覽器輸入網址並後方加上參數,執行後就會寫入資料到資料庫,或是從資料庫讀取資料囉~
小結
透過 Node.js 搭配 Google Cloud Functions,就能輕鬆實現透過 http request 存取 Firestore 的功能囉~