1. Overview: Mengapa Firebase untuk IoT?
Firebase adalah platform Backend-as-a-Service (BaaS) dari Google yang menyediakan berbagai layanan cloud tanpa perlu mengelola server sendiri. Untuk proyek IoT, Firebase sangat menarik karena menyediakan autentikasi user, database real-time, dan hosting dalam satu platform terintegrasi.
Bayangkan kamu punya proyek Smart Home — kamu tidak perlu membuat server API dari nol, mengelola database, atau membangun sistem login. Firebase menyediakan semuanya tinggal pakai.
Layanan Firebase yang Relevan untuk IoT
| Layanan | Fungsi | Contoh Penggunaan IoT |
|---|---|---|
| Authentication | Manajemen user & login | Login ke dashboard monitoring IoT |
| Cloud Firestore | NoSQL database real-time | Menyimpan data sensor, konfigurasi device |
| Realtime Database | Database JSON real-time | Status device, kontrol relay real-time |
| Cloud Functions | Serverless functions | Proses data sensor, kirim notifikasi |
| Cloud Messaging (FCM) | Push notifications | Alert suhu tinggi ke HP user |
| Hosting | Web hosting statis | Deploy dashboard web IoT |
Firestore vs Realtime Database
Firebase menyediakan dua database. Untuk kebanyakan proyek IoT modern, Cloud Firestore lebih direkomendasikan karena:
- Struktur data lebih baik — Koleksi dan dokumen, bukan JSON tree besar
- Query lebih kuat — Filter, sorting, dan pagination yang fleksibel
- Scalability lebih baik — Sharding otomatis untuk data besar
- Offline support — Sinkronisasi offline otomatis untuk mobile app
- Composite index — Query dengan multiple filter dan sorting
Smart Greenhouse: ESP32 mengirim data suhu dan kelembaban ke Cloud Functions via HTTP. Data disimpan di Firestore. User login ke dashboard web menggunakan Firebase Auth (Google Sign-In) dan melihat grafik real-time dari Firestore. Saat suhu melebihi threshold, Cloud Function mengirim push notification ke HP via FCM.
2. Setup Firebase Authentication
Firebase Authentication menyediakan sistem login yang aman tanpa perlu membuat backend autentikasi dari nol. Mendukung email/password, Google, GitHub, Facebook, dan metode login lainnya.
Langkah 1: Buat Project Firebase
- Buka https://console.firebase.google.com
- Klik "Add project"
- Masukkan nama project (misal: "iot-smart-home")
- Aktifkan/nonaktifkan Google Analytics sesuai kebutuhan
- Klik "Create project"
Langkah 2: Register Web App
Perbedaan utama dengan database relasional:
| Aspek | SQL (MySQL) | Firestore (NoSQL) |
|---|---|---|
| Struktur | Tabel dengan baris dan kolom | Koleksi dengan dokumen |
| Schema | Tetap, harus didefinisikan | Fleksibel, bisa berbeda per dokumen |
| Relasi | JOIN antar tabel | Sub-collection atau referensi |
| Query | SQL | SDK dengan filter, sort, limit |
| Skalabilitas | Vertikal (tambah resource) | Horizontal (otomatis) |
| Real-time | Polling / external | Built-in real-time listener |
6. CRUD Operations di Firestore
CRUD (Create, Read, Update, Delete) adalah operasi dasar database. Berikut implementasi lengkap dengan Cloud Firestore:
Create — Menambah Data
import { collection, doc, setDoc, addDoc, serverTimestamp } from "firebase/firestore";
import { db, auth } from "./firebase-config.js";
// === Metode 1: addDoc() — ID dokumen otomatis ===
async function addDevice(deviceData) {
try {
const docRef = await addDoc(collection(db, "devices"), {
name: deviceData.name,
type: deviceData.type,
location: deviceData.location,
owner_uid: auth.currentUser.uid,
status: "online",
created_at: serverTimestamp(),
updated_at: serverTimestamp()
});
console.log("Device ditambahkan dengan ID:", docRef.id);
return docRef.id;
} catch (error) {
console.error("Error menambah device:", error);
throw error;
}
}
// === Metode 2: setDoc() — ID manual ===
async function addReading(deviceId, readingData) {
try {
const readingId = new Date().toISOString(); // ID berdasarkan timestamp
const readingRef = doc(db, "devices", deviceId, "readings", readingId);
await setDoc(readingRef, {
suhu: readingData.suhu,
kelembaban: readingData.kelembaban,
timestamp: serverTimestamp()
});
console.log("Data sensor ditambahkan:", readingId);
} catch (error) {
console.error("Error menambah data:", error);
throw error;
}
}
// Contoh penggunaan
await addDevice({
name: "Sensor Suhu Gudang",
type: "temperature",
location: "Gudang Utama"
});
await addReading("device-001", {
suhu: 28.5,
kelembaban: 72.0
});
Read — Membaca Data
import { collection, doc, getDoc, getDocs, query, where, orderBy, limit } from "firebase/firestore";
import { db } from "./firebase-config.js";
// === Baca satu dokumen berdasarkan ID ===
async function getDevice(deviceId) {
const docRef = doc(db, "devices", deviceId);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Data device:", docSnap.data());
return { id: docSnap.id, ...docSnap.data() };
} else {
console.log("Device tidak ditemukan!");
return null;
}
}
// === Baca semua device milik user yang sedang login ===
async function getUserDevices() {
const q = query(
collection(db, "devices"),
where("owner_uid", "==", auth.currentUser.uid),
orderBy("created_at", "desc"),
limit(50)
);
const querySnapshot = await getDocs(q);
const devices = [];
querySnapshot.forEach((doc) => {
devices.push({ id: doc.id, ...doc.data() });
});
console.log(`Ditemukan ${devices.length} device`);
return devices;
}
// === Baca data sensor dengan filter ===
async function getHighTempReadings(deviceId, minTemp) {
const q = query(
collection(db, "devices", deviceId, "readings"),
where("suhu", ">", minTemp),
orderBy("suhu", "desc"),
limit(10)
);
const snapshot = await getDocs(q);
return snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
}
Update & Delete
import { doc, updateDoc, deleteDoc, serverTimestamp } from "firebase/firestore";
import { db } from "./firebase-config.js";
// === Update data device ===
async function updateDevice(deviceId, newData) {
const deviceRef = doc(db, "devices", deviceId);
await updateDoc(deviceRef, {
...newData,
updated_at: serverTimestamp()
});
console.log("Device berhasil diupdate");
}
// Contoh: update status dan lokasi device
await updateDevice("device-001", {
status: "offline",
location: "Lantai 3, Ruang Lab"
});
// === Hapus device (termasuk sub-collection) ===
async function deleteDevice(deviceId) {
// PERINGATAN: Sub-collection tidak otomatis terhapus!
// Hapus sub-collection "readings" terlebih dahulu
const readings = await getDocs(collection(db, "devices", deviceId, "readings"));
const deletePromises = readings.docs.map(d => deleteDoc(d.ref));
await Promise.all(deletePromises);
// Hapus dokumen device
await deleteDoc(doc(db, "devices", deviceId));
console.log("Device dan semua data sensornya berhasil dihapus");
}
// === Batch write: operasi sekaligus ===
import { writeBatch } from "firebase/firestore";
async function batchUpdateStatus(deviceIds, status) {
const batch = writeBatch(db);
deviceIds.forEach(id => {
const deviceRef = doc(db, "devices", id);
batch.update(deviceRef, { status, updated_at: serverTimestamp() });
});
await batch.commit(); // Commit semua sekaligus
console.log(`${deviceIds.length} device diupdate sekaligus`);
}
7. Firestore Security Rules
Security Rules menentukan siapa yang boleh membaca, menulis, atau menghapus data. Tanpa rules yang benar, siapa saja bisa mengakses data sensor IoT kamu. Ini adalah lapisan keamanan paling penting di Firebase.
API key Firebase bersifat publik — siapa saja yang melihat source code web kamu bisa mengetahui API key-nya. Security Rules adalah satu-satunya pertahanan untuk melindungi data kamu. Jangan pernah menggunakan "test mode" di produksi!
rules_version = "2";
service cloud.firestore {
match /databases/{database}/documents {
// Helper function: cek apakah user sudah login
function isAuth() {
return request.auth != null;
}
// Helper function: cek apakah user adalah pemilik dokumen
function isOwner(userId) {
return request.auth.uid == userId;
}
// === Collection: devices ===
match /devices/{deviceId} {
// Baca: hanya pemilik device yang bisa melihat
allow read: if isAuth() && isOwner(resource.data.owner_uid);
// Buat: user yang login, owner_uid harus miliknya, wajib ada field tertentu
allow create: if isAuth()
&& request.resource.data.owner_uid == request.auth.uid
&& request.resource.data.keys().hasAll(["name", "type", "owner_uid"])
&& request.resource.data.name is string
&& request.resource.data.name.size() > 0;
// Update: hanya pemilik, owner_uid tidak bisa diubah
allow update: if isAuth()
&& isOwner(resource.data.owner_uid)
&& request.resource.data.owner_uid == resource.data.owner_uid;
// Hapus: hanya pemilik
allow delete: if isAuth() && isOwner(resource.data.owner_uid);
// Sub-collection: readings (data sensor)
match /readings/{readingId} {
// Baca: hanya pemilik parent device
allow read: if isAuth() && isOwner(
get(/databases/$(database)/documents/devices/$(deviceId)).data.owner_uid
);
// Tulis data baru: hanya pemilik, validasi tipe data
allow create: if isAuth()
&& isOwner(get(/databases/$(database)/documents/devices/$(deviceId)).data.owner_uid)
&& request.resource.data.suhu is number
&& request.resource.data.kelembaban is number;
// Data sensor TIDAK BOLEH diubah atau dihapus (integritas data)
allow update, delete: if false;
}
}
}
}
Testing Security Rules
# Instalasi Firebase CLI
npm install -g firebase-tools
# Login ke Firebase
firebase login
# Jalankan emulator untuk test security rules secara lokal
firebase emulators:start --only firestore
# Deploy security rules ke production
firebase deploy --only firestore:rules
8. Real-Time Listeners
Fitur terbaik Firestore untuk IoT adalah real-time listener. Data yang berubah di database akan otomatis ter-update di semua client yang mendengarkan. Tidak perlu polling — Firestore melakukan push update ke client.
import { collection, doc, onSnapshot, query, where, orderBy, limit } from "firebase/firestore";
import { db } from "./firebase-config.js";
// === Listener untuk satu dokumen (real-time) ===
function watchDevice(deviceId) {
const deviceRef = doc(db, "devices", deviceId);
const unsubscribe = onSnapshot(deviceRef, (docSnap) => {
if (docSnap.exists()) {
const data = docSnap.data();
console.log("Status device berubah:", data.status);
// Update UI secara real-time
document.getElementById("deviceName").textContent = data.name;
document.getElementById("deviceStatus").textContent = data.status;
document.getElementById("deviceStatus").className =
data.status === "online" ? "badge-success" : "badge-danger";
}
}, (error) => {
console.error("Error listening device:", error);
});
// Kembalikan fungsi unsubscribe untuk berhenti mendengarkan
return unsubscribe;
}
// === Listener untuk data sensor terbaru ===
function watchLatestReadings(deviceId) {
const q = query(
collection(db, "devices", deviceId, "readings"),
orderBy("timestamp", "desc"),
limit(10)
);
const unsubscribe = onSnapshot(q, (snapshot) => {
const readings = [];
snapshot.forEach((doc) => {
readings.push({ id: doc.id, ...doc.data() });
});
console.log(`Data sensor terbaru (${readings.length} records):`, readings);
// Update grafik di dashboard
updateChart(readings);
// Update gauge suhu
if (readings.length > 0) {
const latestSuhu = readings[0].suhu;
document.getElementById("gaugeSuhu").textContent = latestSuhu + "°C";
// Alert jika suhu kritis
if (latestSuhu > 35) {
showAlert("Suhu kritis! " + latestSuhu + "°C");
}
}
});
return unsubscribe;
}
// === Menggunakan listener ===
const unsubDevice = watchDevice("device-001");
const unsubReadings = watchLatestReadings("device-001");
// Berhenti mendengarkan saat tidak diperlukan (hemat kuota)
// unsubDevice();
// unsubReadings();
Firestore menghitung setiap dokumen yang dibaca sebagai 1 read operation (termasuk dari listener). Jika listener mendengarkan 100 dokumen dan masing-masing berubah 1x, itu = 200 read operations. Gunakan limit() dan where() untuk membatasi data yang didengarkan. Unsubscribe listener saat halaman tidak aktif.
9. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Firebase Auth dan Firestore: