Codelab Web Cloud Firestore

1. Ikhtisar

Sasaran

Di codelab ini, Anda akan membuat 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 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 Tambah proyek , lalu beri nama proyek Firebase FriendlyEats .

Ingat ID Proyek untuk proyek Firebase Anda.

  1. Klik Buat proyek .

Aplikasi yang akan kita bangun 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 melayani 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 Firebase console.

Aktifkan Otentikasi Anonim

Meskipun autentikasi bukanlah 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 kepada 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 dikendalikan oleh Aturan Keamanan. Kita akan berbicara lebih banyak tentang aturan nanti di codelab ini, tetapi pertama-tama kita perlu menetapkan beberapa aturan dasar pada data kita untuk memulai. Di tab Rules 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 ke pengguna yang masuk, yang mencegah pengguna yang tidak diautentikasi untuk 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 sampel

Kloning repositori GitHub dari baris perintah:

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

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

cd friendlyeats-web

Impor aplikasi pemula

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 perlu segera mengedit kode di direktori itu.

4. Instal Antarmuka Baris Perintah Firebase

Firebase Command Line Interface (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 yang lebih baru.

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

Kami telah menyiapkan template aplikasi web untuk menarik konfigurasi aplikasi Anda untuk Firebase Hosting dari direktori dan file 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 Anda 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 kita gunakan alias default saja.

  1. Ikuti instruksi yang tersisa di baris perintah Anda.

5. Jalankan server lokal

Kami siap untuk benar-benar mulai bekerja di aplikasi kami! Ayo 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 membuat Anda masuk sebagai pengguna anonim.

img2.png

6. Tulis data ke Cloud Firestore

Di bagian ini, kami akan menulis beberapa data ke Cloud Firestore sehingga kami dapat mengisi UI aplikasi. Ini dapat dilakukan secara manual melalui Firebase console , 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 kami adalah restoran. Mari kita menulis 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 kemudian add data.

Mari tambahkan restoran!

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

Aplikasi akan secara otomatis menghasilkan serangkaian 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 menerapkan pengambilan data (bagian berikutnya dari codelab).

Namun, jika Anda menavigasi ke 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 berikutnya, 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 utama adalah membuat kueri dan menambahkan pendengar snapshot. Listener ini akan diberi tahu tentang semua data yang ada yang cocok dengan kueri dan akan menerima pembaruan secara real time.

Pertama, mari buat kueri yang akan menyajikan daftar restoran default tanpa filter.

  1. Kembali ke file scripts/FriendlyEats.Data.js .
  2. Temukan fungsi 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, panggilan balik dipicu dengan seluruh rangkaian hasil kueri – artinya seluruh koleksi restaurants dari Cloud Firestore. Kemudian meneruskan semua dokumen individual ke fungsi renderer.display .
  • Saat dokumen dihapus, change.type sama dengan removed . Jadi dalam hal ini, kami akan memanggil fungsi yang menghapus restoran dari UI.

Sekarang setelah kita mengimplementasikan kedua metode, segarkan aplikasi dan verifikasi bahwa restoran yang kita lihat sebelumnya di konsol Firebase sekarang terlihat di aplikasi. Jika Anda berhasil menyelesaikan bagian ini, maka aplikasi Anda sekarang 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 secara manual atau ubah namanya - Anda akan segera melihat perubahannya 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 apa yang kita inginkan. Terkadang lebih masuk akal untuk hanya mengambil data sekali.

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

  1. Kembali ke file scripts/FriendlyEats.Data.js Anda.
  2. Temukan fungsi 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 peringkat karena kami masih perlu menerapkan penambahan peringkat 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')

Seperti 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 menghubungkan beberapa filter untuk membuat kueri tertentu, seperti "Pizza di San Francisco" atau "Makanan Laut di Los Angeles yang dipesan berdasarkan Popularitas".

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

  1. Kembali ke file scripts/FriendlyEats.Data.js Anda.
  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 membangun kueri gabungan berdasarkan input pengguna. Permintaan kami sekarang hanya akan mengembalikan restoran yang sesuai dengan kebutuhan pengguna.

Refresh aplikasi FriendlyEats Anda di browser Anda, lalu verifikasi bahwa Anda dapat memfilter berdasarkan harga, kota, dan kategori. Saat menguji, 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/.../database/firestore/indexes?create_index=...

Error ini karena Cloud Firestore memerlukan indeks untuk sebagian besar kueri gabungan. Mewajibkan indeks pada kueri membuat Cloud Firestore tetap dalam skala besar.

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

10. Menyebarkan 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 yang diunduh aplikasi, 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 salah satu dari mereka salah, 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 banyak 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, kita akan berada dalam keadaan tidak konsisten di mana data di satu bagian database kita tidak cocok dengan data di bagian lain.

Untungnya, Cloud Firestore menyediakan fungsionalitas transaksi yang memungkinkan kami melakukan banyak pembacaan dan penulisan dalam satu operasi atom, memastikan bahwa data kami tetap konsisten.

  1. Kembali ke file scripts/FriendlyEats.Data.js Anda.
  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 Rules di bagian Cloud Firestore (atau klik di sini untuk langsung menuju ke sana).
  3. Ganti default dengan aturan berikut, lalu klik Publish .

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 abadi lainnya.
  • Peringkat hanya dapat dibuat jika ID pengguna cocok dengan pengguna yang masuk, yang mencegah spoofing.

Atau untuk menggunakan Firebase console, 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 baca dan tulis dasar dan lanjutan dengan Cloud Firestore, serta cara mengamankan akses data dengan aturan keamanan. Anda dapat menemukan solusi lengkapnya di repositori quickstarts-js .

Untuk mempelajari Cloud Firestore lebih lanjut, kunjungi referensi berikut: