1. Ringkasan
Sasaran
Dalam codelab ini, Anda akan membangun aplikasi web rekomendasi restoran dengan teknologi Cloud Firestore.
Hal yang akan Anda pelajari
- Membaca dan menulis data ke Cloud Firestore dari aplikasi web
- Memproses perubahan pada data Cloud Firestore secara real time
- Menggunakan Firebase Authentication dan aturan keamanan untuk mengamankan data Cloud Firestore
- Menulis kueri Cloud Firestore yang kompleks
Hal yang akan Anda perlukan
Sebelum memulai codelab ini, pastikan Anda telah menginstal:
2. Membuat dan menyiapkan project Firebase
Membuat project Firebase
- Di Firebase console, klik Add project, lalu beri nama project Firebase dengan FriendlyEats.
Ingat Project ID untuk project Firebase Anda.
- Klik Create project.
Aplikasi yang akan kita bangun menggunakan beberapa layanan Firebase yang tersedia di web:
- Firebase Authentication untuk mengidentifikasi pengguna dengan mudah
- Cloud Firestore untuk menyimpan data terstruktur di Cloud dan mendapatkan notifikasi instan saat data diperbarui
- Firebase Hosting untuk menghosting dan menayangkan aset statis
Untuk codelab khusus ini, kami telah mengonfigurasi Firebase Hosting. Namun, untuk Firebase Auth dan Cloud Firestore, kami akan memandu Anda mengonfigurasi dan mengaktifkan layanan menggunakan Firebase console.
Aktifkan Autentikasi Anonim
Meskipun autentikasi bukan fokus codelab ini, penting untuk memiliki beberapa bentuk autentikasi di aplikasi kita. Kami akan menggunakan Login secara anonim - artinya pengguna akan login secara otomatis tanpa diminta.
Anda harus mengaktifkan Login anonim.
- Di Firebase console, temukan bagian Build di navigasi sebelah kiri.
- Klik Autentikasi, lalu klik tab Metode login (atau klik di sini untuk langsung membukanya).
- Aktifkan Penyedia Login Anonymous, lalu klik Save.
Tindakan ini akan memungkinkan aplikasi membuat pengguna login secara otomatis saat mereka mengakses aplikasi web. Anda dapat membaca dokumentasi Autentikasi Anonim untuk mempelajari lebih lanjut.
Aktifkan Cloud Firestore
Aplikasi ini menggunakan Cloud Firestore untuk menyimpan dan menerima informasi dan rating restoran.
Anda harus mengaktifkan Cloud Firestore. Di bagian Build Firebase console, klik Firestore Database. Klik Create database di panel Cloud Firestore.
Akses ke data di Cloud Firestore dikontrol oleh Aturan Keamanan. Kita akan berbicara lebih banyak tentang aturan nanti dalam codelab ini, tetapi pertama-tama kita perlu menetapkan beberapa aturan dasar pada data untuk memulai. Di tab Aturan di Firebase console, tambahkan aturan berikut, lalu klik Publish.
service cloud.firestore { match /databases/{database}/documents { match /{document=**} { // // WARNING: These rules are insecure! We will replace them with // more secure rules later in the codelab // allow read, write: if request.auth != null; } } }
Aturan di atas membatasi akses data hanya untuk pengguna yang login, yang mencegah pengguna yang tidak diautentikasi membaca atau menulis. Ini lebih baik daripada mengizinkan akses publik, tetapi masih jauh dari aman, kita akan meningkatkan aturan ini nanti di codelab.
3. Mendapatkan kode contoh
Clone repositori GitHub dari command line:
git clone https://github.com/firebase/friendlyeats-web
Kode contoh seharusnya sudah di-clone ke direktori 🪄friendlyeats-web
. Mulai sekarang, pastikan untuk menjalankan semua perintah Anda dari direktori ini:
cd friendlyeats-web/vanilla-js
Mengimpor aplikasi awal
Dengan menggunakan IDE (WebStorm, Atom, Sublime, Visual Studio Code...), buka atau impor direktori 🏅friendlyeats-web
. Direktori ini berisi kode awal untuk codelab yang terdiri dari aplikasi rekomendasi restoran yang belum berfungsi. Kita akan membuatnya berfungsi di seluruh codelab ini sehingga Anda harus segera mengedit kode dalam direktori tersebut.
4. Menginstal Antarmuka Command Line Firebase
Antarmuka Command Line (CLI) Firebase memungkinkan Anda menyalurkan aplikasi web secara lokal dan men-deploy aplikasi web ke Firebase Hosting.
- Instal CLI dengan menjalankan perintah npm berikut:
npm -g install firebase-tools
- Pastikan bahwa CLI telah diinstal dengan benar dengan menjalankan perintah berikut:
firebase --version
Pastikan versi Firebase CLI v7.4.0 atau yang lebih baru.
- Otorisasi Firebase CLI dengan menjalankan perintah berikut:
firebase login
Kami telah menyiapkan template aplikasi web untuk mengambil konfigurasi aplikasi Anda untuk Firebase Hosting dari direktori lokal dan file aplikasi. Tetapi untuk melakukannya, kami perlu mengaitkan aplikasi dengan project Firebase Anda.
- Pastikan command line mengakses direktori lokal aplikasi Anda.
- Kaitkan aplikasi Anda dengan project Firebase Anda dengan menjalankan perintah berikut:
firebase use --add
- Saat diminta, pilih Project ID Anda, lalu berikan alias untuk project Firebase Anda.
Alias berguna jika Anda memiliki beberapa lingkungan (produksi, staging, dll.). Namun, untuk codelab ini, mari kita gunakan alias default
.
- Ikuti petunjuk selanjutnya di command line.
5. Menjalankan server lokal
Kita siap untuk mulai mengerjakan aplikasi kita. Mari kita jalankan aplikasi secara lokal.
- Jalankan perintah Firebase CLI berikut:
firebase emulators:start --only hosting
- Command line Anda akan menampilkan respons berikut:
hosting: Local server: http://localhost:5000
Kita menggunakan emulator Firebase Hosting untuk menayangkan aplikasi secara lokal. Aplikasi web sekarang akan tersedia dari http://localhost:5000.
- Buka aplikasi di http://localhost:5000.
Anda akan melihat salinan FriendlyEats yang telah terhubung ke project Firebase Anda.
Aplikasi tersebut telah otomatis terhubung ke project Firebase Anda dan diam-diam membuat Anda login sebagai pengguna anonim.
6. Menulis data ke Cloud Firestore
Di bagian ini, kita akan menulis beberapa data ke Cloud Firestore sehingga kita dapat mengisi UI aplikasi. Hal ini dapat dilakukan secara manual melalui Firebase console, tetapi kita akan melakukannya di aplikasi itu sendiri untuk mendemonstrasikan penulisan Cloud Firestore dasar.
Model Data
Data Firestore dibagi menjadi beberapa koleksi, dokumen, kolom, dan subkoleksi. Kita akan menyimpan setiap restoran sebagai dokumen di koleksi tingkat atas yang disebut restaurants
.
Nanti, kita akan menyimpan setiap ulasan di subkoleksi yang disebut ratings
di bawah setiap restoran.
Menambahkan restoran ke Firestore
Objek model utama di aplikasi kita adalah restoran. Mari kita tulis beberapa kode yang menambahkan dokumen restoran ke koleksi restaurants
.
- Dari file yang Anda download, buka
scripts/FriendlyEats.Data.js
. - Temukan fungsi
FriendlyEats.prototype.addRestaurant
. - Ganti seluruh fungsi dengan kode berikut.
FriendlyEats.Data.js
FriendlyEats.prototype.addRestaurant = function(data) { var collection = firebase.firestore().collection('restaurants'); return collection.add(data); };
Kode di atas menambahkan dokumen baru ke koleksi restaurants
. Data dokumen berasal dari objek JavaScript biasa. Kita melakukannya dengan mendapatkan referensi ke koleksi Cloud Firestore restaurants
terlebih dahulu, lalu add
dengan datanya.
Mari kita tambahkan restoran.
- Kembali ke aplikasi FriendlyEats di browser Anda dan muat ulang.
- Klik Tambahkan Data Tiruan.
Aplikasi akan otomatis membuat kumpulan acak objek restoran, lalu memanggil fungsi addRestaurant
Anda. Namun, Anda tidak akan melihat data di aplikasi web yang sebenarnya karena kita masih perlu menerapkan pengambilan data (bagian berikutnya dari codelab).
Namun, jika membuka tab Cloud Firestore di Firebase console, Anda akan melihat dokumen baru di koleksi restaurants
.
Selamat, Anda baru saja berhasil menulis data dari aplikasi web ke Cloud Firestore.
Di bagian berikutnya, Anda akan mempelajari cara mengambil data dari Cloud Firestore dan menampilkannya di aplikasi.
7. Menampilkan data dari Cloud Firestore
Di bagian ini, Anda akan mempelajari cara mengambil data dari Cloud Firestore dan menampilkannya di aplikasi. Dua langkah utama adalah membuat kueri dan menambahkan pemroses snapshot. Pemroses ini akan menerima notifikasi tentang semua data yang ada yang cocok dengan kueri dan akan menerima pembaruan secara real time.
Pertama, mari kita buat kueri yang akan menampilkan daftar restoran {i>default<i} yang tidak difilter.
- Kembali ke file
scripts/FriendlyEats.Data.js
. - Temukan fungsi
FriendlyEats.prototype.getAllRestaurants
. - Ganti seluruh fungsi dengan kode berikut.
FriendlyEats.Data.js
FriendlyEats.prototype.getAllRestaurants = function(renderer) { var query = firebase.firestore() .collection('restaurants') .orderBy('avgRating', 'desc') .limit(50); this.getDocumentsInQuery(query, renderer); };
Pada kode di atas, kita membuat kueri yang akan mengambil hingga 50 restoran dari koleksi level teratas bernama restaurants
, yang diurutkan berdasarkan rating rata-rata (saat ini semuanya nol). Setelah mendeklarasikan kueri ini, kami meneruskannya ke metode getDocumentsInQuery()
yang bertanggung jawab untuk memuat dan merender data.
Kita akan melakukannya dengan menambahkan pemroses snapshot.
- Kembali ke file
scripts/FriendlyEats.Data.js
. - Temukan fungsi
FriendlyEats.prototype.getDocumentsInQuery
. - Ganti seluruh fungsi dengan kode berikut.
FriendlyEats.Data.js
FriendlyEats.prototype.getDocumentsInQuery = function(query, renderer) { query.onSnapshot(function(snapshot) { if (!snapshot.size) return renderer.empty(); // Display "There are no restaurants". snapshot.docChanges().forEach(function(change) { if (change.type === 'removed') { renderer.remove(change.doc); } else { renderer.display(change.doc); } }); }); };
Dalam kode di atas, query.onSnapshot
akan memicu callback-nya setiap kali ada perubahan pada hasil kueri.
- Pertama kali, callback akan dipicu dengan seluruh kumpulan hasil kueri - yang berarti seluruh koleksi
restaurants
dari Cloud Firestore. Tindakan ini kemudian meneruskan semua dokumen individual ke fungsirenderer.display
. - Saat dokumen dihapus,
change.type
sama denganremoved
. Jadi, dalam hal ini, kita akan memanggil fungsi yang menghapus restoran dari UI.
Setelah mengimplementasikan kedua metode tersebut, muat ulang aplikasi dan verifikasi bahwa restoran yang kita lihat sebelumnya di Firebase console sekarang terlihat di aplikasi. Jika Anda berhasil menyelesaikan bagian ini, berarti aplikasi Anda sekarang membaca dan menulis data dengan Cloud Firestore.
Karena daftar restoran berubah sewaktu-waktu, pemroses ini akan terus diperbarui secara otomatis. Coba buka Firebase console dan hapus restoran secara manual atau ubah namanya - perubahan tersebut akan segera muncul di situs Anda.
8. Data Get()
Sejauh ini, kami telah menunjukkan cara menggunakan onSnapshot
untuk mengambil update secara real time; namun, itu tidak selalu sesuai keinginan kita. Terkadang lebih masuk akal untuk hanya mengambil data satu kali.
Kita ingin menerapkan metode yang dipicu ketika pengguna mengklik ke restoran tertentu dalam aplikasi Anda.
- Kembali ke file
scripts/FriendlyEats.Data.js
Anda. - Temukan fungsi
FriendlyEats.prototype.getRestaurant
. - Ganti seluruh fungsi dengan kode berikut.
FriendlyEats.Data.js
FriendlyEats.prototype.getRestaurant = function(id) { return firebase.firestore().collection('restaurants').doc(id).get(); };
Setelah menerapkan metode ini, Anda akan dapat melihat halaman untuk setiap restoran. Cukup klik restoran dalam daftar dan Anda akan melihat halaman detail restoran tersebut:
Untuk saat ini, Anda tidak dapat menambahkan rating karena kita masih perlu menerapkan penambahan rating nanti di codelab.
9. Mengurutkan dan memfilter data
Saat ini, aplikasi kita menampilkan daftar restoran, tetapi pengguna tidak dapat memfilter restoran berdasarkan kebutuhan mereka. Di bagian ini, Anda akan menggunakan kueri lanjutan Cloud Firestore untuk mengaktifkan pemfilteran.
Berikut adalah contoh kueri sederhana untuk mengambil semua restoran Dim Sum
:
var filteredQuery = query.where('category', '==', 'Dim Sum')
Seperti namanya, metode where()
akan membuat kueri kita hanya mendownload anggota koleksi yang kolomnya memenuhi batasan yang telah ditetapkan. Dalam kasus ini, tindakan tersebut hanya akan mendownload restoran dengan category
adalah Dim Sum
.
Di aplikasi, pengguna dapat merangkai beberapa filter untuk membuat kueri tertentu, seperti "Pizza di San Francisco" atau "Makanan laut di Los Angeles yang dipesan berdasarkan Popularitas".
Kita akan membuat metode yang membangun kueri yang akan memfilter restoran kita berdasarkan beberapa kriteria yang dipilih oleh pengguna.
- Kembali ke file
scripts/FriendlyEats.Data.js
Anda. - Temukan fungsi
FriendlyEats.prototype.getFilteredRestaurants
. - Ganti seluruh fungsi dengan kode berikut.
FriendlyEats.Data.js
FriendlyEats.prototype.getFilteredRestaurants = function(filters, renderer) { var query = firebase.firestore().collection('restaurants'); if (filters.category !== 'Any') { query = query.where('category', '==', filters.category); } if (filters.city !== 'Any') { query = query.where('city', '==', filters.city); } if (filters.price !== 'Any') { query = query.where('price', '==', filters.price.length); } if (filters.sort === 'Rating') { query = query.orderBy('avgRating', 'desc'); } else if (filters.sort === 'Reviews') { query = query.orderBy('numRatings', 'desc'); } this.getDocumentsInQuery(query, renderer); };
Kode di atas menambahkan beberapa filter where
dan satu klausa orderBy
untuk membangun kueri gabungan berdasarkan input pengguna. Kueri kita sekarang hanya akan menampilkan restoran yang cocok dengan kebutuhan pengguna.
Refresh aplikasi FriendlyEats di browser Anda, lalu verifikasi bahwa Anda dapat memfilter menurut harga, kota, dan kategori. Saat menguji, Anda akan melihat error di Konsol JavaScript browser yang terlihat seperti ini:
The query requires an index. You can create it here: https://console.firebase.google.com/project/project-id/database/firestore/indexes?create_composite=...
Error ini terjadi karena Cloud Firestore memerlukan indeks untuk sebagian besar kueri gabungan. Mewajibkan indeks pada kueri menjaga Cloud Firestore tetap cepat dalam skala besar.
Membuka link dari pesan error akan otomatis membuka UI pembuatan indeks di Firebase console dengan parameter yang benar telah diisi. Di bagian berikutnya, kita akan menulis dan men-deploy indeks yang dibutuhkan untuk aplikasi ini.
10. Men-deploy indeks
Jika tidak ingin menjelajahi setiap jalur di aplikasi dan mengikuti setiap link pembuatan indeks, kita dapat dengan mudah men-deploy banyak indeks sekaligus menggunakan Firebase CLI.
- Di direktori lokal yang didownload aplikasi Anda, Anda akan menemukan file
firestore.indexes.json
.
File ini menjelaskan semua indeks yang diperlukan untuk semua kemungkinan kombinasi filter.
firestore.indexes.json
{ "indexes": [ { "collectionGroup": "restaurants", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "city", "order": "ASCENDING" }, { "fieldPath": "avgRating", "order": "DESCENDING" } ] }, ... ] }
- Deploy indeks tersebut dengan perintah berikut:
firebase deploy --only firestore:indexes
Setelah beberapa menit, indeks Anda akan aktif dan pesan error akan hilang.
11. Menulis data dalam transaksi
Di bagian ini, kita akan menambahkan kemampuan bagi pengguna untuk mengirim ulasan ke restoran. Sejauh ini, semua penulisan kita atomik dan relatif sederhana. Jika ada yang error, kita mungkin hanya meminta pengguna untuk mencobanya lagi atau aplikasi kita akan mencoba ulang penulisan secara otomatis.
Aplikasi kita akan memiliki banyak pengguna yang ingin menambahkan rating untuk sebuah restoran, jadi kita harus mengoordinasikan beberapa pembacaan dan penulisan. Pertama, ulasan itu sendiri harus dikirimkan, lalu rating count
dan average rating
restoran perlu diperbarui. Jika salah satunya gagal, tetapi tidak, akan menghasilkan status yang tidak konsisten, yaitu data di satu bagian database tidak cocok dengan data di bagian yang lain.
Untungnya, Cloud Firestore menyediakan fungsi transaksi yang memungkinkan kita menjalankan beberapa pembacaan dan penulisan dalam satu operasi atomik, memastikan bahwa data kita tetap konsisten.
- Kembali ke file
scripts/FriendlyEats.Data.js
Anda. - Temukan fungsi
FriendlyEats.prototype.addRating
. - Ganti seluruh fungsi dengan kode berikut.
FriendlyEats.Data.js
FriendlyEats.prototype.addRating = function(restaurantID, rating) { var collection = firebase.firestore().collection('restaurants'); var document = collection.doc(restaurantID); var newRatingDocument = document.collection('ratings').doc(); return firebase.firestore().runTransaction(function(transaction) { return transaction.get(document).then(function(doc) { var data = doc.data(); var newAverage = (data.numRatings * data.avgRating + rating.rating) / (data.numRatings + 1); transaction.update(document, { numRatings: data.numRatings + 1, avgRating: newAverage }); return transaction.set(newRatingDocument, rating); }); }); };
Di blok di atas, kita memicu transaksi untuk memperbarui nilai numerik avgRating
dan numRatings
dalam dokumen restoran. Pada saat yang sama, kami menambahkan rating
baru ke subkoleksi ratings
.
12. Melindungi data
Di bagian awal codelab ini, kita menetapkan aturan keamanan aplikasi guna membuka database sepenuhnya untuk pembacaan atau penulisan apa pun. Dalam penerapan nyata, kita ingin menetapkan aturan yang jauh lebih terperinci untuk mencegah modifikasi atau akses data yang tidak diinginkan.
- Di bagian Build Firebase console, klik Firestore Database.
- Klik tab Rules di bagian Cloud Firestore (atau klik di sini untuk langsung membukanya).
- Ganti default dengan aturan berikut, lalu klik Publikasikan.
firestore.rules
rules_version = '2'; service cloud.firestore { // Determine if the value of the field "key" is the same // before and after the request. function unchanged(key) { return (key in resource.data) && (key in request.resource.data) && (resource.data[key] == request.resource.data[key]); } match /databases/{database}/documents { // Restaurants: // - Authenticated user can read // - Authenticated user can create/update (for demo purposes only) // - Updates are allowed if no fields are added and name is unchanged // - Deletes are not allowed (default) match /restaurants/{restaurantId} { allow read: if request.auth != null; allow create: if request.auth != null; allow update: if request.auth != null && (request.resource.data.keys() == resource.data.keys()) && unchanged("name"); // Ratings: // - Authenticated user can read // - Authenticated user can create if userId matches // - Deletes and updates are not allowed (default) match /ratings/{ratingId} { allow read: if request.auth != null; allow create: if request.auth != null && request.resource.data.userId == request.auth.uid; } } } }
Aturan ini membatasi akses untuk memastikan bahwa klien hanya membuat perubahan yang aman. Contoh:
- Pembaruan pada dokumen restoran hanya dapat mengubah rating, bukan nama atau data yang tidak dapat diubah lainnya.
- Rating hanya dapat dibuat jika ID pengguna cocok dengan pengguna yang login, guna mencegah spoofing.
Atau, untuk menggunakan Firebase console, Anda dapat menggunakan Firebase CLI untuk men-deploy aturan ke project Firebase. File firestore.rules di direktori kerja Anda sudah berisi aturan dari atas. Untuk men-deploy aturan tersebut dari sistem file lokal Anda (daripada menggunakan Firebase console), jalankan perintah berikut:
firebase deploy --only firestore:rules
13. Kesimpulan
Dalam codelab ini, Anda telah mempelajari cara melakukan pembacaan dan penulisan dasar dan lanjutan dengan Cloud Firestore, serta cara mengamankan akses data dengan aturan keamanan. Anda dapat menemukan solusi lengkapnya di repositori panduan memulai-js.
Untuk mempelajari Cloud Firestore lebih lanjut, buka referensi berikut:
14. [Opsional] Menerapkan dengan App Check
Firebase App Check memberikan perlindungan dengan membantu memvalidasi dan mencegah traffic yang tidak diinginkan ke aplikasi Anda. Pada langkah ini, Anda akan mengamankan akses ke layanan Anda dengan menambahkan App Check dengan reCAPTCHA Enterprise.
Pertama, Anda harus mengaktifkan App Check dan reCaptcha.
Mengaktifkan reCaptcha Enterprise
- Di Konsol Cloud, temukan dan pilih reCaptcha Enterprise di bagian Keamanan.
- Aktifkan layanan saat diminta, lalu klik Create Key.
- Masukkan nama tampilan seperti yang diminta, lalu pilih Situs sebagai jenis platform Anda.
- Tambahkan URL yang telah di-deploy ke Daftar domain, dan pastikan bahwa "Gunakan verifikasi kotak centang" tidak dipilih.
- Klik Create Key, lalu simpan kunci yang dihasilkan di suatu tempat untuk disimpan. Anda akan membutuhkannya nanti di langkah ini.
Mengaktifkan App Check
- Di Firebase console, cari bagian Build di panel kiri.
- Klik App Check, lalu klik tombol Get Started (atau alihkan langsung ke konsol).
- Klik Register dan masukkan kunci Perusahaan reCaptcha saat diminta, lalu klik Simpan.
- Di Tampilan API, pilih Storage, lalu klik Enforce. Lakukan hal yang sama untuk Cloud Firestore.
App Check sekarang seharusnya diterapkan. Muat ulang aplikasi Anda dan coba membuat/melihat restoran. Anda akan mendapatkan pesan error:
Uncaught Error in snapshot listener: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
Artinya, App Check memblokir permintaan yang tidak divalidasi secara default. Sekarang, mari tambahkan validasi ke aplikasi.
Buka file FriendlyEats.View.js, lalu update fungsi initAppCheck
dan tambahkan kunci reCaptcha Anda untuk melakukan inisialisasi App Check.
FriendlyEats.prototype.initAppCheck = function() {
var appCheck = firebase.appCheck();
appCheck.activate(
new firebase.appCheck.ReCaptchaEnterpriseProvider(
/* reCAPTCHA Enterprise site key */
),
true // Set to true to allow auto-refresh.
);
};
Instance appCheck
diinisialisasi dengan ReCaptchaEnterpriseProvider
dengan kunci Anda, dan isTokenAutoRefreshEnabled
memungkinkan token dimuat ulang secara otomatis di aplikasi Anda.
Untuk mengaktifkan pengujian lokal, cari bagian tempat aplikasi diinisialisasi dalam file FriendlyEats.js, dan tambahkan baris berikut ke fungsi FriendlyEats.prototype.initAppCheck
:
if(isLocalhost) {
self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}
Tindakan ini akan mencatat token debug di konsol aplikasi web lokal Anda, seperti:
App Check debug token: 8DBDF614-649D-4D22-B0A3-6D489412838B. You will need to add it to your app's App Check settings in the Firebase console for it to work.
Sekarang, buka Tampilan Aplikasi dari App Check di Firebase console.
Klik menu tambahan, lalu pilih Manage debug tokens.
Kemudian, klik Add debug token dan tempel token debug dari konsol saat diminta.
Selamat! App Check kini seharusnya sudah berfungsi di aplikasi Anda.