Codelab Web Cloud Firestore

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.

1. Tinjauan

Sasaran

Dalam codelab ini, Anda akan mem-build aplikasi web rekomendasi restoran yang diberdayakan oleh Cloud Firestore .

img5.png

Apa yang akan Anda pelajari

  • Membaca dan menulis data ke Cloud Firestore dari aplikasi web
  • Dengarkan perubahan dalam data Cloud Firestore secara real time
  • Gunakan Firebase Authentication dan aturan keamanan untuk mengamankan data Cloud Firestore
  • Tulis kueri Cloud Firestore yang kompleks

Apa yang Anda butuhkan

Sebelum memulai codelab ini, pastikan Anda telah menginstal:

2. Buat dan siapkan proyek Firebase

Buat proyek Firebase

  1. Di konsol Firebase , klik Tambahkan proyek , lalu beri nama proyek Firebase FriendlyEats .

Ingat ID Proyek untuk proyek Firebase Anda.

  1. Klik Buat proyek .

Aplikasi yang akan kita buat menggunakan beberapa layanan Firebase yang tersedia di web:

  • Firebase Authentication untuk mengidentifikasi pengguna Anda 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 Anda

Untuk codelab khusus ini, kami telah mengonfigurasi Firebase Hosting. Namun, untuk Firebase Auth dan Cloud Firestore, kami akan memandu Anda melalui konfigurasi dan pengaktifan layanan menggunakan konsol Firebase.

Aktifkan Autentikasi Anonim

Meskipun autentikasi bukan fokus dari codelab ini, penting untuk memiliki beberapa bentuk autentikasi di aplikasi kita. Kami akan menggunakan login Anonim - artinya pengguna akan masuk secara diam-diam tanpa diminta.

Anda harus mengaktifkan login Anonim.

  1. Di konsol Firebase, temukan bagian Build di navigasi kiri.
  2. Klik Otentikasi , lalu klik tab Metode masuk (atau klik di sini untuk langsung ke sana).
  3. Aktifkan Penyedia Masuk Anonim , lalu klik Simpan .

img7.png

Ini akan memungkinkan aplikasi untuk masuk secara diam-diam ke pengguna Anda saat mereka mengakses aplikasi web. Jangan ragu untuk membaca dokumentasi Otentikasi Anonim untuk mempelajari lebih lanjut.

Aktifkan Cloud Firestore

Aplikasi ini menggunakan Cloud Firestore untuk menyimpan dan menerima informasi dan peringkat restoran.

Anda harus mengaktifkan Cloud Firestore. Di bagian Build Firebase console, klik Firestore Database . Klik Buat database di panel Cloud Firestore.

Akses ke data di Cloud Firestore dikontrol oleh Aturan Keamanan. Kita akan membahas aturan lebih lanjut nanti di codelab ini, tetapi pertama-tama kita perlu menetapkan beberapa aturan dasar pada data kita untuk memulai. Di tab Rules pada konsol Firebase, tambahkan aturan berikut, lalu klik Publikasikan .

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 ke pengguna yang masuk, yang mencegah pengguna yang tidak diautentikasi membaca atau menulis. Ini lebih baik daripada mengizinkan akses publik tetapi masih jauh dari aman, kami akan menyempurnakan aturan ini nanti di codelab.

3. Dapatkan kode contoh

Kloning repositori GitHub dari baris perintah:

git clone https://github.com/firebase/friendlyeats-web

Kode sampel seharusnya sudah diklon ke direktori 📁 friendlyeats-web . Mulai sekarang, pastikan untuk menjalankan semua perintah Anda dari direktori ini:

cd friendlyeats-web

Impor aplikasi awal

Menggunakan IDE Anda (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. Kami akan membuatnya berfungsi di seluruh codelab ini sehingga Anda harus segera mengedit kode di direktori tersebut.

4. Instal Antarmuka Baris Perintah Firebase

Antarmuka Baris Perintah Firebase (CLI) memungkinkan Anda menyajikan aplikasi web secara lokal dan menerapkan aplikasi web ke Firebase Hosting.

  1. Instal CLI dengan menjalankan perintah npm berikut:
npm -g install firebase-tools
  1. Verifikasi bahwa CLI telah diinstal dengan benar dengan menjalankan perintah berikut:
firebase --version

Pastikan versi Firebase CLI adalah v7.4.0 atau lebih baru.

  1. Otorisasi Firebase CLI dengan menjalankan perintah berikut:
firebase login

Kami telah menyiapkan template aplikasi web untuk mengambil konfigurasi aplikasi Anda untuk Firebase Hosting dari file dan direktori lokal aplikasi Anda. Namun untuk melakukannya, kami perlu mengaitkan aplikasi Anda dengan proyek Firebase Anda.

  1. Pastikan baris perintah Anda mengakses direktori lokal aplikasi Anda.
  2. Kaitkan aplikasi Anda dengan proyek Firebase dengan menjalankan perintah berikut:
firebase use --add
  1. Saat diminta, pilih ID Proyek Anda , lalu berikan alias untuk proyek Firebase Anda.

Alias ​​​​berguna jika Anda memiliki banyak lingkungan (produksi, pementasan, dll). Namun, untuk codelab ini, mari gunakan saja alias default .

  1. Ikuti instruksi yang tersisa di baris perintah Anda.

5. Jalankan server lokal

Kami siap untuk benar-benar mulai mengerjakan aplikasi kami! Mari jalankan aplikasi kita secara lokal!

  1. Jalankan perintah Firebase CLI berikut:
firebase emulators:start --only hosting
  1. Baris perintah Anda akan menampilkan respons berikut:
hosting: Local server: http://localhost:5000

Kami menggunakan emulator Firebase Hosting untuk melayani aplikasi kami secara lokal. Aplikasi web sekarang harus tersedia dari http://localhost:5000 .

  1. Buka aplikasi Anda di http://localhost:5000 .

Anda akan melihat salinan FriendlyEats yang telah terhubung ke proyek Firebase Anda.

Aplikasi secara otomatis terhubung ke proyek Firebase Anda dan secara diam-diam memasukkan Anda sebagai pengguna anonim.

img2.png

6. Tulis data ke Cloud Firestore

Di bagian ini, kita akan menulis beberapa data ke Cloud Firestore agar kita dapat mengisi UI aplikasi. Ini dapat dilakukan secara manual melalui konsol Firebase , tetapi kami akan melakukannya di aplikasi itu sendiri untuk mendemonstrasikan penulisan Cloud Firestore dasar.

Model data

Data Firestore dibagi menjadi koleksi, dokumen, bidang, dan subkoleksi. Kami akan menyimpan setiap restoran sebagai dokumen dalam koleksi tingkat atas yang disebut restaurants .

img3.png

Nanti, kami akan menyimpan setiap ulasan dalam subkoleksi yang disebut ratings di bawah setiap restoran.

img4.png

Tambahkan restoran ke Firestore

Objek model utama di aplikasi kita adalah restoran. Mari tulis beberapa kode yang menambahkan dokumen restoran ke koleksi restaurants .

  1. Dari file yang Anda unduh, buka scripts/FriendlyEats.Data.js .
  2. Temukan fungsi FriendlyEats.prototype.addRestaurant .
  3. 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. Kami melakukan ini dengan terlebih dahulu mendapatkan referensi ke restaurants koleksi Cloud Firestore, lalu add datanya.

Mari tambahkan restoran!

  1. Kembali ke aplikasi FriendlyEats Anda di browser Anda dan segarkan.
  2. Klik Tambahkan Data Tiruan .

Aplikasi ini akan secara otomatis menghasilkan sekumpulan objek restoran secara acak, lalu memanggil fungsi addRestaurant Anda. Namun, Anda belum akan melihat data di aplikasi web Anda yang sebenarnya karena kami masih perlu mengimplementasikan pengambilan data (bagian selanjutnya dari codelab).

Namun, jika Anda membuka tab Cloud Firestore di konsol Firebase, Anda sekarang akan melihat dokumen baru di koleksi restaurants !

img6.png

Selamat, Anda baru saja menulis data ke Cloud Firestore dari aplikasi web!

Di bagian selanjutnya, Anda akan mempelajari cara mengambil data dari Cloud Firestore dan menampilkannya di aplikasi Anda.

7. Menampilkan data dari Cloud Firestore

Di bagian ini, Anda akan mempelajari cara mengambil data dari Cloud Firestore dan menampilkannya di aplikasi Anda. Dua langkah kuncinya adalah membuat kueri dan menambahkan pendengar snapshot. Pemroses ini akan diberi tahu tentang semua data yang ada yang cocok dengan kueri dan akan menerima pembaruan secara waktu nyata.

Pertama, mari buat kueri yang akan menyajikan daftar restoran default yang tidak difilter.

  1. Kembali ke file scripts/FriendlyEats.Data.js .
  2. Temukan fungsinya FriendlyEats.prototype.getAllRestaurants .
  3. 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);
};

Dalam kode di atas, kami membuat kueri yang akan mengambil hingga 50 restoran dari koleksi tingkat atas bernama restaurants , yang diurutkan berdasarkan peringkat rata-rata (saat ini semuanya nol). Setelah kami mendeklarasikan kueri ini, kami meneruskannya ke metode getDocumentsInQuery() yang bertanggung jawab untuk memuat dan merender data.

Kami akan melakukan ini dengan menambahkan pendengar snapshot.

  1. Kembali ke file scripts/FriendlyEats.Data.js .
  2. Temukan fungsi FriendlyEats.prototype.getDocumentsInQuery .
  3. 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);
      }
    });
  });
};

Pada kode di atas, query.onSnapshot akan memicu panggilan baliknya setiap kali ada perubahan pada hasil kueri.

  • Pertama kali, callback dipicu dengan seluruh rangkaian hasil kueri – artinya seluruh koleksi restaurants dari Cloud Firestore. Ini kemudian meneruskan semua dokumen individual ke fungsi renderer.display .
  • Saat dokumen dihapus, change.type sama dengan removed . Jadi dalam hal ini, kita akan memanggil fungsi yang menghapus restoran dari UI.

Sekarang kita telah mengimplementasikan kedua metode, segarkan aplikasi dan verifikasi bahwa restoran yang kita lihat sebelumnya di konsol Firebase kini terlihat di aplikasi. Jika Anda berhasil menyelesaikan bagian ini, aplikasi Anda sekarang sedang membaca dan menulis data dengan Cloud Firestore!

Saat daftar restoran Anda berubah, pendengar ini akan terus memperbarui secara otomatis. Coba buka konsol Firebase dan hapus restoran atau ubah namanya secara manual - Anda akan segera melihat perubahannya muncul di situs Anda!

img5.png

8. Dapatkan() data

Sejauh ini, kami telah menunjukkan cara menggunakan onSnapshot untuk mengambil pembaruan secara real time; namun, itu tidak selalu yang kita inginkan. Terkadang lebih masuk akal untuk hanya mengambil data satu kali.

Kami ingin menerapkan metode yang dipicu saat pengguna mengeklik restoran tertentu di aplikasi Anda.

  1. Kembali ke file scripts/FriendlyEats.Data.js .
  2. Temukan fungsinya FriendlyEats.prototype.getRestaurant .
  3. Ganti seluruh fungsi dengan kode berikut.

FriendlyEats.Data.js

FriendlyEats.prototype.getRestaurant = function(id) {
  return firebase.firestore().collection('restaurants').doc(id).get();
};

Setelah Anda menerapkan metode ini, Anda akan dapat melihat halaman untuk setiap restoran. Cukup klik restoran dalam daftar dan Anda akan melihat halaman detail restoran:

img1.png

Untuk saat ini, Anda tidak dapat menambahkan rating karena kami masih perlu mengimplementasikan penambahan rating nanti di codelab.

9. Urutkan dan filter data

Saat ini, aplikasi kami menampilkan daftar restoran, tetapi tidak ada cara bagi pengguna untuk memfilter 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')

Sesuai dengan namanya, metode where() akan membuat kueri kita hanya mengunduh anggota koleksi yang bidangnya memenuhi batasan yang kita tetapkan. Dalam hal ini, itu hanya akan mengunduh restoran dengan category Dim Sum .

Di aplikasi kami, pengguna dapat merangkai beberapa filter untuk membuat kueri tertentu, seperti "Pizza di San Francisco" atau "Makanan Laut di Los Angeles dipesan berdasarkan Popularitas".

Kami akan membuat metode yang membuat kueri yang akan memfilter restoran kami berdasarkan beberapa kriteria yang dipilih oleh pengguna kami.

  1. Kembali ke file scripts/FriendlyEats.Data.js .
  2. Temukan fungsi FriendlyEats.prototype.getFilteredRestaurants .
  3. 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 membuat kueri gabungan berdasarkan input pengguna. Kueri kami sekarang hanya akan menampilkan restoran yang sesuai dengan kebutuhan pengguna.

Refresh aplikasi FriendlyEats Anda di browser Anda, lalu verifikasi bahwa Anda dapat memfilter menurut harga, kota, dan kategori. Saat pengujian, Anda akan melihat kesalahan di Konsol JavaScript browser Anda 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. Memerlukan indeks pada kueri membuat Cloud Firestore cepat dalam skala besar.

Membuka tautan dari pesan kesalahan akan secara otomatis membuka UI pembuatan indeks di konsol Firebase dengan parameter yang diisi dengan benar. Di bagian selanjutnya, kita akan menulis dan menerapkan indeks yang diperlukan untuk aplikasi ini.

10. Terapkan indeks

Jika kita tidak ingin menjelajahi setiap jalur di aplikasi kita dan mengikuti setiap tautan pembuatan indeks, kita dapat dengan mudah menerapkan banyak indeks sekaligus menggunakan Firebase CLI.

  1. Di direktori lokal aplikasi yang diunduh, 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" }
     ]
   },

   ...

 ]
}
  1. Terapkan indeks ini dengan perintah berikut:
firebase deploy --only firestore:indexes

Setelah beberapa menit, indeks Anda akan aktif dan pesan kesalahan akan hilang.

11. Tulis data dalam transaksi

Di bagian ini, kami akan menambahkan kemampuan bagi pengguna untuk mengirimkan ulasan ke restoran. Sejauh ini, semua tulisan kami bersifat atomik dan relatif sederhana. Jika ada kesalahan, kami mungkin hanya akan meminta pengguna untuk mencobanya lagi atau aplikasi kami akan mencoba menulis ulang secara otomatis.

Aplikasi kami akan memiliki banyak pengguna yang ingin menambahkan peringkat untuk sebuah restoran, jadi kami perlu mengoordinasikan beberapa pembacaan dan penulisan. Pertama, ulasan itu sendiri harus diserahkan, kemudian count peringkat restoran dan average rating perlu diperbarui. Jika salah satu dari ini gagal tetapi tidak yang lain, kami berada dalam keadaan tidak konsisten di mana data di satu bagian dari basis data kami tidak cocok dengan data di bagian lain.

Untungnya, Cloud Firestore menyediakan fungsionalitas transaksi yang memungkinkan kami melakukan beberapa operasi baca dan tulis dalam satu operasi atomik, memastikan bahwa data kami tetap konsisten.

  1. Kembali ke file scripts/FriendlyEats.Data.js .
  2. Temukan fungsi FriendlyEats.prototype.addRating .
  3. 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, kami memicu transaksi untuk memperbarui nilai numerik avgRating dan numRatings di dokumen restoran. Pada saat yang sama, kami menambahkan rating baru ke subkoleksi ratings .

12. Amankan data Anda

Di awal codelab ini, kami menetapkan aturan keamanan aplikasi kami untuk sepenuhnya membuka database untuk membaca atau menulis apa pun. Dalam aplikasi nyata, kami ingin menetapkan aturan yang lebih halus untuk mencegah akses atau modifikasi data yang tidak diinginkan.

  1. Di bagian Build Firebase console, klik Firestore Database .
  2. Klik tab Aturan di bagian Cloud Firestore (atau klik di sini untuk langsung ke sana).
  3. Ganti default dengan aturan berikut, lalu klik Terbitkan .

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. Sebagai contoh:

  • Pembaruan pada dokumen restoran hanya dapat mengubah peringkat, bukan nama atau data lain yang tidak dapat diubah.
  • Peringkat hanya dapat dibuat jika ID pengguna cocok dengan pengguna yang masuk, yang mencegah spoofing.

Selain menggunakan konsol Firebase, Anda dapat menggunakan Firebase CLI untuk menerapkan aturan ke proyek Firebase Anda. File firestore.rules di direktori kerja Anda sudah berisi aturan dari atas. Untuk menerapkan aturan ini dari sistem file lokal Anda (daripada menggunakan konsol Firebase), Anda akan menjalankan perintah berikut:

firebase deploy --only firestore:rules

13. Kesimpulan

Dalam codelab ini, Anda 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 quickstarts-js repository .

Untuk mempelajari Cloud Firestore lebih lanjut, kunjungi referensi berikut: