Mengintegrasikan Firebase dengan aplikasi Next.js

1. Sebelum memulai

Dalam codelab ini, Anda akan mempelajari cara mengintegrasikan Firebase dengan aplikasi web Next.js yang disebut Friendly Eats, yaitu situs yang menyediakan ulasan restoran.

Aplikasi web Friendly Eats

Aplikasi web yang sudah selesai menawarkan fitur berguna yang menunjukkan bagaimana Firebase dapat membantu Anda membangun aplikasi Next.js. Fitur tersebut mencakup:

  • Build dan deploy otomatis: Codelab ini menggunakan Firebase App Hosting untuk secara otomatis mem-build dan men-deploy kode Next.js setiap kali Anda mengirim ke cabang yang dikonfigurasi.
  • Login dan logout: Aplikasi web yang telah selesai memungkinkan Anda login dengan Google serta logout. Login dan persistensi pengguna dikelola sepenuhnya melalui Firebase Authentication.
  • Gambar: Aplikasi web yang sudah selesai memungkinkan pengguna yang login mengupload gambar restoran. Aset gambar disimpan di Cloud Storage for Firebase. Firebase JavaScript SDK menyediakan URL publik untuk gambar yang diupload. URL publik ini kemudian disimpan di dokumen restoran yang relevan di Cloud Firestore.
  • Ulasan: Dengan aplikasi web yang sudah selesai, pengguna yang login dapat memposting ulasan tentang restoran yang terdiri dari rating bintang dan pesan berbasis teks. Informasi ulasan disimpan di Cloud Firestore.
  • Filter: Dengan aplikasi web yang sudah selesai, pengguna yang login dapat memfilter daftar restoran berdasarkan kategori, lokasi, dan harga. Anda juga dapat menyesuaikan metode pengurutan yang digunakan. Data diakses dari Cloud Firestore, dan kueri Firestore diterapkan berdasarkan filter yang digunakan.

Prasyarat

  • Akun GitHub
  • Pengetahuan tentang Next.js dan JavaScript

Yang akan Anda pelajari

  • Cara menggunakan Firebase dengan Router Aplikasi Next.js dan rendering sisi server.
  • Cara mempertahankan gambar di Cloud Storage for Firebase.
  • Cara membaca dan menulis data di database Cloud Firestore.
  • Cara menggunakan login dengan Google menggunakan Firebase JavaScript SDK.

Yang Anda butuhkan

  • Git
  • Node.js versi stabil terbaru
  • Browser pilihan Anda, seperti Google Chrome
  • Lingkungan pengembangan dengan editor kode dan terminal
  • Akun Google untuk pembuatan dan pengelolaan project Firebase Anda
  • Kemampuan untuk mengupgrade project Firebase ke paket harga Blaze

2. Menyiapkan lingkungan pengembangan dan repositori GitHub

Codelab ini menyediakan codebase awal aplikasi dan mengandalkan Firebase CLI.

Membuat repositori GitHub

Sumber codelab dapat ditemukan di https://github.com/firebase/friendlyeats-web. Repositori berisi project contoh untuk berbagai platform. Namun, codelab ini hanya menggunakan direktori nextjs-start. Perhatikan direktori berikut:

* `nextjs-start`: contains the starter code upon which you build.
* `nextjs-end`: contains the solution code for the finished web app.

Salin folder nextjs-start ke repositori Anda sendiri:

  1. Dengan menggunakan terminal, buat folder baru di komputer Anda dan pindah ke direktori baru:
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. Gunakan paket npm giget untuk mengambil folder nextjs-start saja:
    npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
    
  3. Lacak perubahan secara lokal dengan git:
    git init
    
    git commit -a -m "codelab starting point"
    
    git branch -M main
    
  4. Buat repositori GitHub baru: https://github.com/new. Beri nama sesuai keinginan Anda.
    1. GitHub akan memberi Anda URL repositori baru yang terlihat seperti https://github.com//.git atau git@github.com:/.git. Salin URL ini.
  5. Kirim perubahan lokal ke repositori GitHub baru Anda. Jalankan perintah berikut, dengan mengganti URL repositori Anda dengan placeholder .
    git remote add origin <your-repository-url>
    
    git push -u origin main
    
  6. Anda sekarang akan melihat kode awal di repositori GitHub.

Menginstal atau mengupdate Firebase CLI

Jalankan perintah berikut untuk memverifikasi bahwa Anda telah menginstal Firebase CLI dan menggunakan versi v13.9.0 atau yang lebih tinggi:

firebase --version

Jika Anda melihat versi yang lebih rendah atau belum menginstal Firebase CLI, jalankan perintah instal:

npm install -g firebase-tools@latest

Jika Anda tidak dapat menginstal Firebase CLI karena error izin, lihat dokumentasi npm atau gunakan opsi penginstalan lainnya.

Login ke Firebase

  1. Jalankan perintah berikut untuk login ke Firebase CLI:
    firebase login
    
  2. Bergantung pada apakah Anda ingin Firebase mengumpulkan data, masukkan Y atau N.
  3. Di browser, pilih akun Google Anda, lalu klik Allow.

3. Menyiapkan project Firebase

Di bagian ini, Anda akan menyiapkan project Firebase dan mengaitkan aplikasi web Firebase dengan project tersebut. Anda juga akan menyiapkan layanan Firebase yang digunakan oleh aplikasi web contoh.

Membuat project Firebase

  1. Di Firebase console, klik Add project.
  2. Di kotak teks Enter your project name, masukkan FriendlyEats Codelab (atau nama project pilihan Anda), lalu klik Continue.
  3. Di modal Confirm Firebase billing plan, pastikan paket tersebut adalah Blaze, lalu klik Confirm plan
  4. Untuk codelab ini, Anda tidak memerlukan Google Analytics. Oleh karena itu, nonaktifkan opsi Enable Google Analytics for this project.
  5. Klik Create project.
  6. Tunggu hingga project Anda disediakan, lalu klik Continue.
  7. Di project Firebase Anda, buka Project Settings. Catat project ID Anda karena Anda akan memerlukannya nanti. ID unik ini adalah cara project Anda diidentifikasi (misalnya, di Firebase CLI).

Mengupgrade paket harga Firebase Anda

Untuk menggunakan App Hosting, project Firebase Anda harus menggunakan paket harga Blaze, yang berarti project tersebut terkait dengan akun Penagihan Cloud.

  • Akun Penagihan Cloud memerlukan metode pembayaran, seperti kartu kredit.
  • Jika Anda baru menggunakan Firebase dan Google Cloud, periksa apakah Anda memenuhi syarat untuk mendapatkan kredit sebesar $300 dan akun Penagihan Cloud Uji Coba Gratis.

Untuk mengupgrade project Anda ke paket Blaze, ikuti langkah-langkah berikut:

  1. Di Firebase console, pilih upgrade your plan.
  2. Pada dialog yang muncul, pilih paket Blaze, lalu ikuti petunjuk di layar untuk mengaitkan project Anda dengan akun Penagihan Cloud.
    Jika perlu membuat akun Penagihan Cloud, Anda mungkin perlu kembali ke alur upgrade di Firebase console untuk menyelesaikan upgrade.

Menambahkan aplikasi web ke project Firebase

  1. Buka Ringkasan project di project Firebase Anda, lalu klik e41f2efdd9539c31.png Web.

    Jika sudah memiliki aplikasi yang terdaftar di project Anda, klik Tambahkan aplikasi untuk melihat ikon Web.
  2. Di kotak teks App nickname, masukkan nama panggilan aplikasi yang mudah diingat, seperti My Next.js app.
  3. Biarkan kotak centang Siapkan juga Firebase Hosting untuk aplikasi ini tidak dicentang.
  4. Klik Register app > Next > Next > Continue to konsol.

Menyiapkan layanan Firebase di Firebase console

Menyiapkan Authentication

  1. Di Firebase console, buka Authentication.
  2. Klik Get started.
  3. Di kolom Additional providers, klik Google > Enable.
  4. Di kotak teks Public-facing name for project, masukkan nama yang mudah diingat, seperti My Next.js app.
  5. Dari drop-down Support email for project, pilih alamat email Anda.
  6. Klik Save.

Menyiapkan Cloud Firestore

  1. Di Firebase console, buka Firestore.
  2. Klik Create database > Next > Start in test mode > Next.
    Di bagian selanjutnya dalam codelab ini, Anda akan menambahkan Aturan Keamanan untuk mengamankan data. Jangan mendistribusikan atau mengekspos aplikasi ke publik tanpa menambahkan Aturan Keamanan untuk database Anda.
  3. Gunakan lokasi default atau pilih lokasi pilihan Anda.
    Untuk aplikasi yang sebenarnya, Anda harus memilih lokasi yang dekat dengan pengguna. Perlu diperhatikan bahwa lokasi ini tidak dapat diubah nanti, dan juga akan secara otomatis menjadi lokasi bucket Cloud Storage default Anda (langkah berikutnya).
  4. Klik Done.

Menyiapkan Cloud Storage for Firebase

  1. Di Firebase console, buka Storage.
  2. Klik Get started > Start in test mode > Next.
    Di codelab ini, Anda akan menambahkan Aturan Keamanan untuk mengamankan data. Jangan mendistribusikan atau mengekspos aplikasi ke publik tanpa menambahkan Aturan Keamanan untuk bucket Storage Anda.
  3. Lokasi bucket Anda seharusnya sudah dipilih (karena Anda menyiapkan Firestore di langkah sebelumnya).
  4. Klik Done.

4. Meninjau codebase awal

Di bagian ini, Anda akan meninjau beberapa area codebase awal aplikasi yang akan ditambahi fungsi dalam codelab ini.

Struktur folder dan file

Tabel berikut berisi ringkasan struktur folder dan file aplikasi:

Folder dan file

Deskripsi

src/components

Komponen React untuk filter, header, detail restoran, dan ulasan

src/lib

Fungsi utilitas yang tidak harus terikat dengan React atau Next.js

src/lib/firebase

Kode khusus Firebase dan konfigurasi Firebase

public

Aset statis di aplikasi web, seperti ikon

src/app

Memilih rute dengan Router Aplikasi Next.js

src/app/restaurant

Pengendali rute API

package.json dan package-lock.json

Dependensi project dengan npm

next.config.js

Konfigurasi khusus Next.js (tindakan server diaktifkan)

jsconfig.json

Konfigurasi layanan bahasa JavaScript

Komponen server dan klien

Aplikasi ini adalah aplikasi web Next.js yang menggunakan Router Aplikasi. Rendering server digunakan di seluruh aplikasi. Misalnya, file src/app/page.js adalah komponen server yang bertanggung jawab atas halaman utama. File src/components/RestaurantListings.jsx adalah komponen klien yang ditunjukkan oleh perintah "use client" di awal file.

Pernyataan impor

Anda mungkin melihat pernyataan impor seperti berikut:

import RatingPicker from "@/src/components/RatingPicker.jsx";

Aplikasi menggunakan simbol @ untuk menghindari jalur impor relatif yang kaku dan dimungkinkan oleh alias jalur.

API khusus Firebase

Semua kode Firebase API digabungkan dalam direktori src/lib/firebase. Setiap komponen React kemudian mengimpor fungsi yang digabungkan dari direktori src/lib/firebase, bukan mengimpor fungsi Firebase secara langsung.

Data tiruan

Data restoran dan ulasan tiruan terdapat dalam file src/lib/randomData.js. Data dari file tersebut disusun dalam kode di file src/lib/fakeRestaurants.js.

5. Membuat backend App Hosting

Di bagian ini, Anda akan menyiapkan backend App Hosting untuk melihat cabang di repositori git Anda.

Di akhir bagian ini, Anda akan memiliki backend App Hosting yang terhubung ke repositori di GitHub, yang akan otomatis membangun ulang dan meluncurkan versi baru aplikasi Anda setiap kali Anda mengirim commit baru ke cabang main.

Men-deploy Aturan Keamanan

Kode ini sudah memiliki kumpulan aturan keamanan untuk Firestore dan Cloud Storage for Firebase. Setelah Anda men-deploy Aturan Keamanan, data dalam database dan bucket akan lebih terlindungi dari penyalahgunaan.

  1. Di terminal Anda, konfigurasikan CLI untuk menggunakan project Firebase yang Anda buat sebelumnya:
    firebase use --add
    
    Saat diminta untuk memasukkan alias, masukkan friendlyeats-codelab.
  2. Untuk men-deploy Aturan Keamanan ini, jalankan perintah ini di terminal Anda:
    firebase deploy --only firestore:rules,storage
    
  3. Jika Anda ditanya: "Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?", tekan Enter untuk memilih Ya.

Menambahkan konfigurasi Firebase ke kode aplikasi web

  1. Di Firebase console, buka Project settings.
  2. Di panel SDK setup and configuration, klik "Add app", lalu klik ikon kurung kode untuk mendaftarkan aplikasi web baru.
  3. Di akhir alur pembuatan aplikasi web, salin variabel firebaseConfig, lalu salin properti dan nilainya.
  4. Buka file apphosting.yaml di editor kode Anda, dan isi nilai variabel lingkungan dengan nilai konfigurasi dari Firebase console.
  5. Dalam file, ganti properti yang ada dengan properti yang Anda salin.
  6. Simpan file.

Membuat backend

  1. Buka halaman App Hosting di Firebase console:

Status nol pada konsol App Hosting, dengan tombol &#39;Mulai&#39;

  1. Klik "Get started" untuk memulai alur pembuatan backend. Konfigurasi backend Anda sebagai berikut:
  2. Ikuti perintah di langkah pertama untuk menghubungkan repositori GitHub yang Anda buat sebelumnya.
  3. Tetapkan setelan penerapan:
    1. Biarkan direktori root sebagai /
    2. Tetapkan cabang live ke main
    3. Aktifkan peluncuran otomatis
  4. Beri nama backend Anda friendlyeats-codelab.
  5. Di bagian "Create or associate a Firebase web app", pilih aplikasi web yang telah Anda konfigurasi sebelumnya dari drop-down "Select an existing Firebase web app".
  6. Klik "Finish and deploy". Setelah beberapa saat, Anda akan diarahkan ke halaman baru tempat Anda dapat melihat status backend App Hosting yang baru.
  7. Setelah peluncuran selesai, klik domain gratis Anda di bagian "domains". Proses ini mungkin memerlukan waktu beberapa menit karena penyebaran DNS.

Anda telah men-deploy aplikasi web awal. Setiap kali mengirim commit baru ke cabang main dari repositori GitHub, Anda akan melihat build dan peluncuran baru dimulai di Firebase console, dan situs Anda akan otomatis diupdate setelah peluncuran selesai.

6. Menambahkan autentikasi ke aplikasi web

Di bagian ini, Anda akan menambahkan autentikasi ke aplikasi web sehingga Anda dapat login ke aplikasi tersebut.

Mengimplementasikan fungsi login dan logout

  1. Di file src/lib/firebase/auth.js, ganti fungsi onAuthStateChanged, signInWithGoogle, dan signOut dengan kode berikut:
export function onAuthStateChanged(cb) {
	return _onAuthStateChanged(auth, cb);
}

export async function signInWithGoogle() {
  const provider = new GoogleAuthProvider();

  try {
    await signInWithPopup(auth, provider);
  } catch (error) {
    console.error("Error signing in with Google", error);
  }
}

export async function signOut() {
  try {
    return auth.signOut();
  } catch (error) {
    console.error("Error signing out with Google", error);
  }
}

Kode ini menggunakan Firebase API berikut:

Firebase API

Deskripsi

GoogleAuthProvider

Membuat instance penyedia autentikasi Google.

signInWithPopup

Memulai alur autentikasi berbasis dialog.

auth.signOut

Memproses logout pengguna.

Dalam file src/components/Header.jsx, kode sudah memanggil fungsi signInWithGoogle dan signOut.

  1. Buat commit dengan pesan commit "Menambahkan Autentikasi Google" dan kirim ke repositori GitHub Anda. 1. Buka halaman App Hosting di Firebase console dan tunggu hingga peluncuran baru selesai.
  2. Di aplikasi web, muat ulang halaman dan klik Sign in with Google. Aplikasi web tidak diupdate, sehingga tidak jelas apakah login berhasil atau tidak.

Mengirim status autentikasi ke server

Untuk meneruskan status autentikasi ke server, kita akan menggunakan service worker. Ganti fungsi fetchWithFirebaseHeaders dan getAuthIdToken dengan kode berikut:

async function fetchWithFirebaseHeaders(request) {
  const app = initializeApp(firebaseConfig);
  const auth = getAuth(app);
  const installations = getInstallations(app);
  const headers = new Headers(request.headers);
  const [authIdToken, installationToken] = await Promise.all([
    getAuthIdToken(auth),
    getToken(installations),
  ]);
  headers.append("Firebase-Instance-ID-Token", installationToken);
  if (authIdToken) headers.append("Authorization", `Bearer ${authIdToken}`);
  const newRequest = new Request(request, { headers });
  return await fetch(newRequest);
}

async function getAuthIdToken(auth) {
  await auth.authStateReady();
  if (!auth.currentUser) return;
  return await getIdToken(auth.currentUser);
}

Membaca status autentikasi di server

Kita akan menggunakan FirebaseServerApp untuk mencerminkan status autentikasi klien di server.

Buka src/lib/firebase/serverApp.js, dan ganti fungsi getAuthenticatedAppForUser:

export async function getAuthenticatedAppForUser() {
  const idToken = headers().get("Authorization")?.split("Bearer ")[1];
  console.log('firebaseConfig', JSON.stringify(firebaseConfig));
  const firebaseServerApp = initializeServerApp(
    firebaseConfig,
    idToken
      ? {
          authIdToken: idToken,
        }
      : {}
  );

  const auth = getAuth(firebaseServerApp);
  await auth.authStateReady();

  return { firebaseServerApp, currentUser: auth.currentUser };
}

Berlangganan perubahan autentikasi

Untuk berlangganan perubahan autentikasi, ikuti langkah-langkah berikut:

  1. Buka file src/components/Header.jsx.
  2. Ganti fungsi useUserSession dengan kode berikut:
function useUserSession(initialUser) {
	// The initialUser comes from the server via a server component
	const [user, setUser] = useState(initialUser);
	const router = useRouter();

	// Register the service worker that sends auth state back to server
	// The service worker is built with npm run build-service-worker
	useEffect(() => {
		if ("serviceWorker" in navigator) {
			const serializedFirebaseConfig = encodeURIComponent(JSON.stringify(firebaseConfig));
			const serviceWorkerUrl = `/auth-service-worker.js?firebaseConfig=${serializedFirebaseConfig}`
		
		  navigator.serviceWorker
			.register(serviceWorkerUrl)
			.then((registration) => console.log("scope is: ", registration.scope));
		}
	  }, []);

	useEffect(() => {
		const unsubscribe = onAuthStateChanged((authUser) => {
			setUser(authUser)
		})

		return () => unsubscribe()
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);

	useEffect(() => {
		onAuthStateChanged((authUser) => {
			if (user === undefined) return

			// refresh when user changed to ease testing
			if (user?.email !== authUser?.email) {
				router.refresh()
			}
		})
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [user])

	return user;
}

Kode ini menggunakan hook status React untuk mengupdate pengguna saat fungsi onAuthStateChanged menentukan bahwa ada perubahan pada status autentikasi.

Memverifikasi perubahan

Tata letak root di file src/app/layout.js merender header dan meneruskan pengguna, jika tersedia, sebagai properti.

<Header initialUser={currentUser?.toJSON()} />

Ini berarti komponen <Header> akan merender data pengguna, jika tersedia, selama waktu proses server. Jika ada update autentikasi selama siklus proses halaman setelah pemuatan halaman awal, pengendali onAuthStateChanged akan menanganinya.

Sekarang saatnya meluncurkan build baru dan memverifikasi build yang Anda buat.

  1. Buat commit dengan pesan commit "Show signin state" dan kirim ke repositori GitHub Anda.
  2. Buka halaman App Hosting di Firebase console dan tunggu hingga peluncuran baru selesai.
  3. Verifikasi perilaku autentikasi baru:
    1. Di browser, muat ulang aplikasi web. Nama tampilan Anda akan muncul di header.
    2. Logout dan login lagi. Halaman diupdate secara real time tanpa memuat ulang halaman. Anda dapat mengulangi langkah ini dengan pengguna lain.
    3. Opsional: Klik kanan aplikasi web, pilih View page source, lalu telusuri nama tampilan. Nama tampilan akan muncul di sumber HTML mentah yang ditampilkan dari server.

7. Melihat informasi restoran

Aplikasi web menyertakan data tiruan untuk restoran dan ulasan.

Menambahkan satu atau beberapa restoran

Untuk memasukkan data restoran tiruan ke database Cloud Firestore lokal Anda, ikuti langkah-langkah berikut:

  1. Di aplikasi web, pilih 2cf67d488d8e6332.png > Add sample restaurants.
  2. Di Firebase console di halaman Firestore Database, pilih restaurants. Anda akan melihat dokumen tingkat teratas dalam koleksi restoran, yang masing-masing mewakili restoran.
  3. Klik beberapa dokumen untuk menjelajahi properti dokumen restoran.

Menampilkan daftar restoran

Database Cloud Firestore Anda sekarang memiliki restoran yang dapat ditampilkan oleh aplikasi web Next.js.

Untuk menentukan kode pengambilan data, ikuti langkah-langkah berikut:

  1. Dalam file src/app/page.js, temukan komponen server <Home />, dan tinjau panggilan ke fungsi getRestaurants, yang mengambil daftar restoran pada waktu proses server. Anda akan menerapkan fungsi getRestaurants dalam langkah-langkah berikut.
  2. Di file src/lib/firebase/firestore.js, ganti fungsi applyQueryFilters dan getRestaurants dengan kode berikut:
function applyQueryFilters(q, { category, city, price, sort }) {
	if (category) {
		q = query(q, where("category", "==", category));
	}
	if (city) {
		q = query(q, where("city", "==", city));
	}
	if (price) {
		q = query(q, where("price", "==", price.length));
	}
	if (sort === "Rating" || !sort) {
		q = query(q, orderBy("avgRating", "desc"));
	} else if (sort === "Review") {
		q = query(q, orderBy("numRatings", "desc"));
	}
	return q;
}

export async function getRestaurants(db = db, filters = {}) {
	let q = query(collection(db, "restaurants"));

	q = applyQueryFilters(q, filters);
	const results = await getDocs(q);
	return results.docs.map(doc => {
		return {
			id: doc.id,
			...doc.data(),
			// Only plain objects can be passed to Client Components from Server Components
			timestamp: doc.data().timestamp.toDate(),
		};
	});
}
  1. Buat commit dengan pesan commit "Baca daftar restoran dari Firestore" dan kirim ke repositori GitHub Anda.
  2. Buka halaman App Hosting di Firebase console dan tunggu hingga peluncuran baru selesai.
  3. Di aplikasi web, muat ulang halaman. Gambar restoran muncul sebagai kotak di halaman.

Memastikan listingan restoran dimuat pada waktu proses server

Dengan menggunakan framework Next.js, mungkin akan sulit untuk mengetahui kapan data dimuat pada waktu proses server atau waktu proses sisi klien.

Untuk memverifikasi bahwa listingan restoran dimuat pada waktu proses server, ikuti langkah-langkah berikut:

  1. Di aplikasi web, buka DevTools dan nonaktifkan JavaScript.

Menonaktifkan JavaScipt di DevTools

  1. Muat ulang aplikasi web. Listingan restoran masih dimuat. Informasi restoran ditampilkan di respons server. Jika JavaScript diaktifkan, informasi restoran dihidrasi melalui kode JavaScript sisi klien.
  2. Di DevTools, aktifkan kembali JavaScript.

Memproses update restoran dengan pemroses snapshot Cloud Firestore

Di bagian sebelumnya, Anda telah melihat cara kumpulan restoran awal dimuat dari file src/app/page.js. File src/app/page.js adalah komponen server dan dirender di server, termasuk kode pengambilan data Firebase.

File src/components/RestaurantListings.jsx adalah komponen klien dan dapat dikonfigurasi untuk menghidrasi markup yang dirender server.

Untuk mengonfigurasi file src/components/RestaurantListings.jsx guna menghidrasi markup yang dirender server, ikuti langkah-langkah berikut:

  1. Dalam file src/components/RestaurantListings.jsx, amati kode berikut, yang sudah ditulis untuk Anda:
useEffect(() => {
        const unsubscribe = getRestaurantsSnapshot(data => {
                setRestaurants(data);
        }, filters);

        return () => {
                unsubscribe();
        };
}, [filters]);

Kode ini memanggil fungsi getRestaurantsSnapshot(), yang mirip dengan fungsi getRestaurants() yang Anda implementasikan di langkah sebelumnya. Namun, fungsi snapshot ini menyediakan mekanisme callback sehingga callback dipanggil setiap kali perubahan dilakukan pada koleksi restoran.

  1. Di file src/lib/firebase/firestore.js, ganti fungsi getRestaurantsSnapshot() dengan kode berikut:
export function getRestaurantsSnapshot(cb, filters = {}) {
	if (typeof cb !== "function") {
		console.log("Error: The callback parameter is not a function");
		return;
	}

	let q = query(collection(db, "restaurants"));
	q = applyQueryFilters(q, filters);

	const unsubscribe = onSnapshot(q, querySnapshot => {
		const results = querySnapshot.docs.map(doc => {
			return {
				id: doc.id,
				...doc.data(),
				// Only plain objects can be passed to Client Components from Server Components
				timestamp: doc.data().timestamp.toDate(),
			};
		});

		cb(results);
	});

	return unsubscribe;
}

Perubahan yang dilakukan melalui halaman Database Firestore kini tercermin di aplikasi web secara real time.

  1. Buat commit dengan pesan commit "Dengarkan update restoran realtime" dan kirim ke repositori GitHub Anda.
  2. Buka halaman App Hosting di Firebase console dan tunggu hingga peluncuran baru selesai.
  3. Di aplikasi web, pilih 27ca5d1e8ed8adfe.png > Add sample restaurants. Jika fungsi snapshot Anda diterapkan dengan benar, restoran akan muncul secara real time tanpa memuat ulang halaman.

8. Simpan ulasan yang dikirim pengguna dari aplikasi web

  1. Di file src/lib/firebase/firestore.js, ganti fungsi updateWithRating() dengan kode berikut:
const updateWithRating = async (
	transaction,
	docRef,
	newRatingDocument,
	review
) => {
	const restaurant = await transaction.get(docRef);
	const data = restaurant.data();
	const newNumRatings = data?.numRatings ? data.numRatings + 1 : 1;
	const newSumRating = (data?.sumRating || 0) + Number(review.rating);
	const newAverage = newSumRating / newNumRatings;

	transaction.update(docRef, {
		numRatings: newNumRatings,
		sumRating: newSumRating,
		avgRating: newAverage,
	});

	transaction.set(newRatingDocument, {
		...review,
		timestamp: Timestamp.fromDate(new Date()),
	});
};

Kode ini menyisipkan dokumen Firestore baru yang mewakili ulasan baru. Kode ini juga mengupdate dokumen Firestore yang ada yang mewakili restoran dengan angka yang diperbarui untuk jumlah rating dan rata-rata rating yang dihitung.

  1. Ganti fungsi addReviewToRestaurant() dengan kode berikut:
export async function addReviewToRestaurant(db, restaurantId, review) {
	if (!restaurantId) {
		throw new Error("No restaurant ID has been provided.");
	}

	if (!review) {
		throw new Error("A valid review has not been provided.");
	}

	try {
		const docRef = doc(collection(db, "restaurants"), restaurantId);
		const newRatingDocument = doc(
			collection(db, `restaurants/${restaurantId}/ratings`)
		);

		// corrected line
		await runTransaction(db, transaction =>
			updateWithRating(transaction, docRef, newRatingDocument, review)
		);
	} catch (error) {
		console.error(
			"There was an error adding the rating to the restaurant",
			error
		);
		throw error;
	}
}

Mengimplementasikan Tindakan Server Next.js

Tindakan Server Next.js menyediakan API yang mudah digunakan untuk mengakses data formulir, seperti data.get("text") untuk mendapatkan nilai teks dari payload pengiriman formulir.

Untuk menggunakan Tindakan Server Next.js guna memproses pengiriman formulir peninjauan, ikuti langkah-langkah berikut:

  1. Dalam file src/components/ReviewDialog.jsx, temukan atribut action di elemen <form>.
<form action={handleReviewFormSubmission}>

Nilai atribut action mengacu pada fungsi yang Anda implementasikan di langkah berikutnya.

  1. Di file src/app/actions.js, ganti fungsi handleReviewFormSubmission() dengan kode berikut:
// This is a next.js server action, which is an alpha feature, so
// use with caution.
// https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions
export async function handleReviewFormSubmission(data) {
        const { app } = await getAuthenticatedAppForUser();
        const db = getFirestore(app);

        await addReviewToRestaurant(db, data.get("restaurantId"), {
                text: data.get("text"),
                rating: data.get("rating"),

                // This came from a hidden form field.
                userId: data.get("userId"),
        });
}

Menambahkan ulasan restoran

Anda telah menerapkan dukungan untuk pengiriman ulasan, jadi sekarang Anda dapat memverifikasi bahwa ulasan Anda disisipkan ke Cloud Firestore dengan benar.

Untuk menambahkan ulasan dan memverifikasi bahwa ulasan tersebut disisipkan ke Cloud Firestore, ikuti langkah-langkah berikut:

  1. Buat commit dengan pesan commit "Izinkan pengguna untuk mengirimkan ulasan restoran" dan kirim ke repositori GitHub Anda.
  2. Buka halaman App Hosting di Firebase console dan tunggu hingga peluncuran baru selesai.
  3. Muat ulang aplikasi web, lalu pilih restoran dari halaman beranda.
  4. Di halaman restoran, klik 3e19beef78bb0d0e.png.
  5. Pilih rating bintang.
  6. Tulis ulasan.
  7. Klik Submit. Ulasan Anda akan muncul di bagian atas daftar ulasan.
  8. Di Cloud Firestore, telusuri panel Add document untuk dokumen restoran yang telah Anda ulas dan pilih dokumen tersebut.
  9. Di panel Start collection, pilih ratings.
  10. Di panel Add document, temukan dokumen untuk peninjauan Anda guna memverifikasi bahwa dokumen tersebut disisipkan seperti yang diharapkan.

Dokumen di Emulator Firestore

9. Menyimpan file yang diupload pengguna dari aplikasi web

Di bagian ini, Anda menambahkan fungsi sehingga Anda dapat mengganti gambar yang terkait dengan restoran saat Anda login. Anda mengupload gambar ke Firebase Storage, dan mengupdate URL gambar di dokumen Cloud Firestore yang mewakili restoran.

Untuk menyimpan file yang diupload pengguna dari aplikasi web, ikuti langkah-langkah berikut:

  1. Di file src/components/Restaurant.jsx, amati kode yang berjalan saat pengguna mengupload file:
async function handleRestaurantImage(target) {
        const image = target.files ? target.files[0] : null;
        if (!image) {
                return;
        }

        const imageURL = await updateRestaurantImage(id, image);
        setRestaurant({ ...restaurant, photo: imageURL });
}

Perubahan tidak diperlukan, tetapi Anda menerapkan perilaku fungsi updateRestaurantImage() dalam langkah-langkah berikut.

  1. Di file src/lib/firebase/storage.js, ganti fungsi updateRestaurantImage() dan uploadImage() dengan kode berikut:
export async function updateRestaurantImage(restaurantId, image) {
	try {
		if (!restaurantId)
			throw new Error("No restaurant ID has been provided.");

		if (!image || !image.name)
			throw new Error("A valid image has not been provided.");

		const publicImageUrl = await uploadImage(restaurantId, image);
		await updateRestaurantImageReference(restaurantId, publicImageUrl);

		return publicImageUrl;
	} catch (error) {
		console.error("Error processing request:", error);
	}
}

async function uploadImage(restaurantId, image) {
	const filePath = `images/${restaurantId}/${image.name}`;
	const newImageRef = ref(storage, filePath);
	await uploadBytesResumable(newImageRef, image);

	return await getDownloadURL(newImageRef);
}

Fungsi updateRestaurantImageReference() sudah diimplementasikan untuk Anda. Fungsi ini mengupdate dokumen restoran yang ada di Cloud Firestore dengan URL gambar yang diupdate.

Memverifikasi fungsi upload gambar

Untuk memastikan bahwa gambar diupload seperti yang diharapkan, ikuti langkah-langkah berikut:

  1. Buat commit dengan pesan commit "Izinkan pengguna mengubah foto setiap restoran" dan kirim ke repositori GitHub Anda.
  2. Buka halaman App Hosting di Firebase console dan tunggu hingga peluncuran baru selesai.
  3. Di aplikasi web, pastikan Anda login dan pilih restoran.
  4. Klik 7067eb41fea41ff0.png lalu upload gambar dari sistem file Anda. Gambar Anda meninggalkan lingkungan lokal dan diupload ke Cloud Storage. Gambar akan langsung muncul setelah Anda menguploadnya.
  5. Buka Cloud Storage for Firebase.
  6. Buka folder yang mewakili restoran. Gambar yang Anda upload ada di folder.

6cf3f9e2303c931c.png

10. Meringkas ulasan restoran dengan AI generatif

Di bagian ini, Anda akan menambahkan fitur ringkasan ulasan sehingga pengguna dapat dengan cepat memahami pendapat semua orang tentang suatu restoran tanpa harus membaca setiap ulasan.

Menyimpan kunci Gemini API di Cloud Secret Manager

  1. Untuk menggunakan Gemini API, Anda memerlukan kunci API. Membuat kunci di Google AI Studio.
  2. App Hosting terintegrasi dengan Cloud Secret Manager untuk memungkinkan Anda menyimpan nilai sensitif seperti kunci API dengan aman:
    1. Di terminal, jalankan perintah untuk membuat secret baru:
    firebase apphosting:secrets:set gemini-api-key
    
    1. Saat diminta memasukkan nilai rahasia, salin dan tempel kunci Gemini API Anda dari Google AI Studio.
    2. Saat ditanya apakah rahasia baru harus ditambahkan ke apphosting.yaml, masukkan Y untuk menerima.

Kunci Gemini API Anda kini disimpan dengan aman di Cloud Secret Manager, dan dapat diakses oleh backend App Hosting Anda.

Menerapkan komponen ringkasan ulasan

  1. Di src/components/Reviews/ReviewSummary.jsx, ganti fungsi GeminiSummary dengan kode berikut:
    export async function GeminiSummary({ restaurantId }) {
        const { firebaseServerApp } = await getAuthenticatedAppForUser();
        const reviews = await getReviewsByRestaurantId(
            getFirestore(firebaseServerApp),
            restaurantId
        );
    
        const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
        const model = genAI.getGenerativeModel({ model: "gemini-pro"});
    
        const reviewSeparator = "@";
        const prompt = `
            Based on the following restaurant reviews, 
            where each review is separated by a '${reviewSeparator}' character, 
            create a one-sentence summary of what people think of the restaurant. 
    
            Here are the reviews: ${reviews.map(review => review.text).join(reviewSeparator)}
        `;
    
        try {
            const result = await model.generateContent(prompt);
            const response = await result.response;
            const text = response.text();
    
            return (
                <div className="restaurant__review_summary">
                    <p>{text}</p>
                    <p>✨ Summarized with Gemini</p>
                </div>
            );
        } catch (e) {
            console.error(e);
            return <p>Error contacting Gemini</p>;
        }
    }
    
  2. Buat commit dengan pesan commit "Use AI to summarize reviews" dan kirim ke repositori GitHub Anda.
  3. Buka halaman App Hosting di Firebase console dan tunggu hingga peluncuran baru selesai.
  4. Buka halaman restoran. Di bagian atas, Anda akan melihat ringkasan satu kalimat dari semua ulasan di halaman.
  5. Tambahkan ulasan baru dan muat ulang halaman. Anda akan melihat perubahan ringkasan.

11. Kesimpulan

Selamat! Anda telah mempelajari cara menggunakan Firebase untuk menambahkan fitur dan fungsi ke aplikasi Next.js. Secara khusus, Anda telah menggunakan hal berikut:

Pelajari lebih lanjut