1. Trước khi bắt đầu
Trong lớp học lập trình này, bạn sẽ tìm hiểu cách tích hợp Firebase với ứng dụng web Next.js có tên là friendly Eats, một trang web chuyên đánh giá nhà hàng.
Ứng dụng web đã hoàn chỉnh này cung cấp các tính năng hữu ích thể hiện cách Firebase có thể giúp bạn tạo ứng dụng Next.js. Các tính năng này bao gồm:
- Tự động xây dựng và triển khai: Lớp học lập trình này sử dụng tính năng Lưu trữ ứng dụng Firebase để tự động tạo và triển khai mã Next.js mỗi khi bạn đẩy đến một nhánh được định cấu hình.
- Đăng nhập và đăng xuất: Ứng dụng web hoàn chỉnh cho phép bạn đăng nhập bằng Google và đăng xuất. Hoạt động đăng nhập và khả năng lưu trữ của người dùng được quản lý hoàn toàn thông qua tính năng Xác thực Firebase.
- Hình ảnh: Ứng dụng web đã hoàn thiện cho phép người dùng đã đăng nhập tải hình ảnh nhà hàng lên. Nội dung hình ảnh được lưu trữ trong Cloud Storage cho Firebase. SDK JavaScript của Firebase cung cấp URL công khai cho các hình ảnh được tải lên. Sau đó, URL công khai này được lưu trữ trong tài liệu về nhà hàng có liên quan trong Cloud Firestore.
- Bài đánh giá: Ứng dụng web đã hoàn thiện cho phép người dùng đã đăng nhập đăng bài đánh giá về các nhà hàng đi kèm điểm xếp hạng theo sao và thông báo bằng văn bản. Thông tin về bài đánh giá được lưu trữ trong Cloud Firestore.
- Bộ lọc: Ứng dụng web đã hoàn thiện cho phép người dùng đã đăng nhập lọc danh sách nhà hàng dựa trên danh mục, vị trí và giá cả. Bạn cũng có thể tuỳ chỉnh phương pháp sắp xếp được sử dụng. Dữ liệu được truy cập từ Cloud Firestore và các truy vấn trên Firestore được áp dụng dựa trên các bộ lọc đã sử dụng.
Điều kiện tiên quyết
- Tài khoản GitHub
- Có kiến thức về Next.js và JavaScript
Kiến thức bạn sẽ học được
- Cách sử dụng Firebase với Bộ định tuyến ứng dụng Next.js và tính năng hiển thị phía máy chủ.
- Cách lưu trữ hình ảnh trong Cloud Storage cho Firebase.
- Cách đọc và ghi dữ liệu trong cơ sở dữ liệu Cloud Firestore.
- Cách sử dụng tính năng đăng nhập bằng Google với SDK JavaScript của Firebase.
Bạn cần có
- Git
- Phiên bản ổn định gần đây của Node.js
- Một trình duyệt mà bạn chọn, chẳng hạn như Google Chrome
- Môi trường phát triển có trình soạn thảo mã và thiết bị đầu cuối
- Tài khoản Google để tạo và quản lý dự án Firebase của bạn
- Khả năng nâng cấp dự án Firebase của bạn lên Gói giá linh hoạt
2. Thiết lập môi trường phát triển và kho lưu trữ GitHub
Lớp học lập trình này cung cấp cơ sở mã khởi đầu của ứng dụng và dựa trên Giao diện dòng lệnh (CLI) của Firebase.
Tạo kho lưu trữ GitHub
Bạn có thể tìm thấy nguồn của lớp học lập trình này tại https://github.com/firebase/friendlyeats-web. Kho lưu trữ chứa các dự án mẫu cho nhiều nền tảng. Tuy nhiên, lớp học lập trình này chỉ sử dụng thư mục nextjs-start
. Hãy lưu ý các thư mục sau:
* `nextjs-start`: contains the starter code upon which you build.
* `nextjs-end`: contains the solution code for the finished web app.
Sao chép thư mục nextjs-start
vào kho lưu trữ của riêng bạn:
- Sử dụng cửa sổ dòng lệnh, tạo một thư mục mới trên máy tính và thay đổi sang thư mục mới:
mkdir codelab-friendlyeats-web cd codelab-friendlyeats-web
- Sử dụng gói npm giget để chỉ tìm nạp thư mục
nextjs-start
:npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
- Theo dõi các thay đổi cục bộ bằng git:
git init git commit -a -m "codelab starting point" git branch -M main
- Tạo kho lưu trữ GitHub mới: https://github.com/new. Đặt tên bất kỳ tên nào bạn muốn.
- GitHub sẽ cung cấp cho bạn một URL kho lưu trữ mới có dạng
https://github.com/
hoặc/ .git git@github.com:
. Sao chép URL này./ .git
- GitHub sẽ cung cấp cho bạn một URL kho lưu trữ mới có dạng
- Đẩy các thay đổi cục bộ vào kho lưu trữ GitHub mới của bạn. Chạy lệnh sau, thay thế URL kho lưu trữ của bạn cho phần giữ chỗ
.git remote add origin <your-repository-url> git push -u origin main
- Bây giờ, bạn sẽ thấy mã khởi đầu trong kho lưu trữ GitHub.
Cài đặt hoặc cập nhật Giao diện dòng lệnh (CLI) của Firebase
Chạy lệnh sau để xác minh rằng bạn đã cài đặt Firebase CLI và phiên bản 13.9.0 trở lên:
firebase --version
Nếu bạn thấy phiên bản thấp hơn hoặc chưa cài đặt Firebase CLI, hãy chạy lệnh cài đặt:
npm install -g firebase-tools@latest
Nếu bạn không thể cài đặt Firebase CLI do lỗi về quyền, hãy xem tài liệu npm hoặc sử dụng một tuỳ chọn cài đặt khác.
Đăng nhập vào Firebase
- Chạy lệnh sau để đăng nhập vào Firebase CLI:
firebase login
- Tuỳ thuộc vào việc bạn muốn Firebase thu thập dữ liệu, hãy nhập
Y
hoặcN
. - Trong trình duyệt, hãy chọn Tài khoản Google của bạn rồi nhấp vào Cho phép.
3. Thiết lập dự án Firebase
Trong phần này, bạn sẽ thiết lập một dự án Firebase và liên kết một ứng dụng web Firebase với dự án đó. Bạn cũng sẽ thiết lập các dịch vụ Firebase mà ứng dụng web mẫu sử dụng.
Tạo một dự án Firebase
- Trong bảng điều khiển của Firebase, hãy nhấp vào Thêm dự án.
- Trong hộp văn bản Nhập tên dự án của bạn, hãy nhập
FriendlyEats Codelab
(hoặc tên dự án mà bạn chọn), sau đó nhấp vào Tiếp tục. - Trong cửa sổ Xác nhận kế hoạch thanh toán Firebase, hãy xác nhận kế hoạch là Blaze, sau đó nhấp vào Xác nhận gói
- Đối với lớp học lập trình này, bạn không cần Google Analytics, vì vậy, hãy tắt tuỳ chọn Bật Google Analytics cho dự án này.
- Nhấp vào Tạo dự án.
- Đợi dự án của bạn được cấp phép, sau đó nhấp vào Tiếp tục.
- Trong dự án Firebase của bạn, hãy chuyển đến phần Cài đặt dự án. Ghi lại mã dự án vì sau này bạn sẽ cần đến mã này. Mã nhận dạng duy nhất này là cách dự án của bạn được xác định (ví dụ: trong Giao diện dòng lệnh (CLI) của Firebase).
Nâng cấp gói giá Firebase của bạn
Để sử dụng tính năng Lưu trữ ứng dụng, dự án Firebase của bạn cần phải sử dụng Gói giá linh hoạt, tức là dự án được liên kết với một tài khoản thanh toán trên đám mây.
- Để sử dụng tài khoản thanh toán Cloud, bạn phải có một phương thức thanh toán, chẳng hạn như thẻ tín dụng.
- Nếu bạn mới sử dụng Firebase và Google Cloud, hãy kiểm tra xem bạn có đủ điều kiện nhận khoản tín dụng trị giá 300 USD và tài khoản thanh toán miễn phí trên đám mây dành cho ưu đãi dùng thử miễn phí hay không.
Để nâng cấp dự án của bạn lên Gói linh hoạt, hãy làm theo các bước sau:
- Trong bảng điều khiển của Firebase, hãy chọn nâng cấp gói của bạn.
- Trong hộp thoại này, hãy chọn Gói linh hoạt, sau đó làm theo hướng dẫn trên màn hình để liên kết dự án của bạn với một tài khoản thanh toán Cloud.
Nếu cần tạo một tài khoản thanh toán Cloud, bạn có thể phải quay lại quy trình nâng cấp trong bảng điều khiển của Firebase để hoàn tất quá trình nâng cấp.
Thêm ứng dụng web vào dự án Firebase
- Chuyển đến phần Tổng quan về dự án trong dự án Firebase, sau đó nhấp vào Web.
Nếu bạn đã đăng ký ứng dụng trong dự án, hãy nhấp vào Thêm ứng dụng để xem biểu tượng Web. - Trong hộp văn bản Biệt hiệu ứng dụng, nhập biệt hiệu dễ nhớ của ứng dụng, chẳng hạn như
My Next.js app
. - Bỏ đánh dấu hộp Cũng thiết lập tính năng Lưu trữ Firebase cho ứng dụng này.
- Nhấp vào Đăng ký ứng dụng > Tiếp theo > Tiếp theo > Chuyển đến bảng điều khiển.
Thiết lập các dịch vụ của Firebase trong bảng điều khiển của Firebase
Thiết lập chế độ xác thực
- Trong bảng điều khiển của Firebase, hãy chuyển đến phần Xác thực.
- Nhấp vào Bắt đầu.
- Trong cột Nhà cung cấp khác, hãy nhấp vào Google > Bật.
- Trong hộp văn bản Tên hiển thị công khai cho dự án, hãy nhập một tên dễ nhớ, chẳng hạn như
My Next.js app
. - Trong trình đơn thả xuống Email hỗ trợ cho dự án, hãy chọn địa chỉ email của bạn.
- Nhấp vào Lưu.
Thiết lập Cloud Firestore
- Trong bảng điều khiển của Firebase, hãy chuyển đến Firestore.
- Nhấp vào Tạo cơ sở dữ liệu > Tiếp theo > Bắt đầu ở chế độ thử nghiệm > Tiếp theo.
Trong phần sau của lớp học lập trình này, bạn sẽ thêm Quy tắc bảo mật để bảo mật dữ liệu của mình. Không phân phối hoặc tiết lộ ứng dụng công khai khi chưa thêm Quy tắc bảo mật cho cơ sở dữ liệu của bạn. - Hãy dùng vị trí mặc định hoặc chọn vị trí mà bạn chọn.
Đối với ứng dụng thực tế, bạn cần chọn vị trí gần người dùng. Xin lưu ý rằng bạn không thể thay đổi vị trí này sau này và vị trí này cũng sẽ tự động là vị trí của bộ chứa Cloud Storage mặc định của bạn (bước tiếp theo). - Nhấp vào Xong.
Thiết lập Cloud Storage cho Firebase
- Trong bảng điều khiển của Firebase, hãy chuyển đến mục Bộ nhớ.
- Nhấp vào Bắt đầu > Bắt đầu ở chế độ thử nghiệm > Tiếp theo.
Trong phần sau của lớp học lập trình này, bạn sẽ thêm Quy tắc bảo mật để bảo mật dữ liệu của mình. Không không phân phối hoặc tiết lộ ứng dụng công khai khi chưa thêm Quy tắc bảo mật cho bộ chứa Storage. - Bạn phải chọn vị trí cho bộ chứa của mình (do đã thiết lập Firestore ở bước trước).
- Nhấp vào Xong.
4. Xem lại cơ sở mã khởi đầu
Trong phần này, bạn sẽ ôn tập một số khía cạnh trong cơ sở mã khởi đầu của ứng dụng mà bạn sẽ thêm chức năng vào trong lớp học lập trình này.
Cấu trúc thư mục và tệp
Bảng sau đây trình bày thông tin tổng quan về cấu trúc thư mục và tệp của ứng dụng:
Thư mục và tệp | Nội dung mô tả |
| Các thành phần tương tác cho bộ lọc, tiêu đề, thông tin chi tiết về nhà hàng và bài đánh giá |
| Các hàm hiệu dụng không nhất thiết phải liên kết với React hoặc Next.js |
| Mã dành riêng cho Firebase và cấu hình Firebase |
| Thành phần tĩnh trong ứng dụng web, chẳng hạn như biểu tượng |
| Định tuyến bằng Bộ định tuyến ứng dụng Next.js |
| Một trình xử lý định tuyến API |
| Các phần phụ thuộc dự án với npm |
| Cấu hình dành riêng cho Next.js (hành động của máy chủ đang bật) |
| Cấu hình dịch vụ ngôn ngữ JavaScript |
Thành phần máy chủ và máy khách
Ứng dụng này là một ứng dụng web Next.js sử dụng Bộ định tuyến ứng dụng. Tính năng kết xuất qua máy chủ được sử dụng trong toàn bộ ứng dụng. Ví dụ: tệp src/app/page.js
là một thành phần máy chủ chịu trách nhiệm về trang chính. Tệp src/components/RestaurantListings.jsx
là một thành phần ứng dụng được biểu thị bằng lệnh "use client"
ở đầu tệp.
Câu lệnh nhập
Bạn có thể thấy các câu lệnh nhập như sau:
import RatingPicker from "@/src/components/RatingPicker.jsx";
Ứng dụng sử dụng biểu tượng @
để tránh các đường dẫn nhập tương đối rối mắt và có thể thực hiện được bằng các bí danh đường dẫn.
API dành riêng cho Firebase
Tất cả mã Firebase API đều được gói trong thư mục src/lib/firebase
. Sau đó, các thành phần React riêng lẻ sẽ nhập các hàm được gói từ thư mục src/lib/firebase
, thay vì nhập trực tiếp các hàm Firebase.
Dữ liệu mô phỏng
Dữ liệu mô phỏng về nhà hàng và bài đánh giá có trong tệp src/lib/randomData.js
. Dữ liệu từ tệp đó được tập hợp trong mã của tệp src/lib/fakeRestaurants.js
.
5. Tạo phần phụ trợ Lưu trữ ứng dụng
Trong phần này, bạn sẽ thiết lập phần phụ trợ Lưu trữ ứng dụng để theo dõi một nhánh trên kho lưu trữ git.
Ở cuối phần này, bạn sẽ có một phần phụ trợ của tính năng Lưu trữ ứng dụng được kết nối với kho lưu trữ trong GitHub. Phần phụ trợ này sẽ tự động tạo lại và ra mắt một phiên bản mới của ứng dụng mỗi khi bạn đẩy một cam kết mới vào nhánh main
.
Triển khai quy tắc bảo mật
Mã này đã có sẵn các bộ quy tắc bảo mật cho Firestore và cho Cloud Storage cho Firebase. Sau khi bạn triển khai Quy tắc bảo mật, dữ liệu trong cơ sở dữ liệu và bộ chứa của bạn sẽ được bảo vệ tốt hơn khỏi hành vi sử dụng sai.
- Trong dòng lệnh, hãy định cấu hình CLI để sử dụng dự án Firebase mà bạn đã tạo trước đó:
Khi được nhắc cung cấp bí danh, hãy nhậpfirebase use --add
friendlyeats-codelab
. - Để triển khai các Quy tắc bảo mật này, hãy chạy lệnh sau trong thiết bị đầu cuối của bạn:
firebase deploy --only firestore:rules,storage
- Nếu bạn được hỏi:
"Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?"
, hãy nhấnEnter
để chọn Có.
Thêm cấu hình Firebase vào mã ứng dụng web
- Trong bảng điều khiển của Firebase, hãy chuyển đến phần Cài đặt dự án.
- Trong ngăn Thiết lập và cấu hình SDK, hãy nhấp vào "Thêm ứng dụng" rồi nhấp vào biểu tượng dấu ngoặc mã
để đăng ký ứng dụng web mới.
- Ở cuối quy trình tạo ứng dụng web, hãy sao chép biến
firebaseConfig
rồi sao chép các thuộc tính cũng như giá trị của biến đó. - Mở tệp
apphosting.yaml
trong trình soạn thảo mã rồi điền các giá trị biến môi trường bằng các giá trị cấu hình từ bảng điều khiển của Firebase. - Trong tệp này, hãy thay thế các thuộc tính hiện có bằng các thuộc tính bạn đã sao chép.
- Lưu tệp.
Tạo phần phụ trợ
- Chuyển đến trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase:
- Nhấp vào "Bắt đầu" để bắt đầu quy trình tạo phần phụ trợ. Định cấu hình phần phụ trợ như sau:
- Làm theo lời nhắc trong bước đầu tiên để kết nối kho lưu trữ GitHub mà bạn đã tạo trước đó.
- Thiết lập chế độ cài đặt triển khai:
- Giữ thư mục gốc là
/
- Đặt nhánh trực tiếp thành
main
- Bật chế độ phát hành tự động
- Giữ thư mục gốc là
- Đặt tên cho phần phụ trợ là
friendlyeats-codelab
. - Trong phần "Tạo hoặc liên kết ứng dụng web Firebase", hãy chọn ứng dụng web mà bạn đã định cấu hình trước đó trong phần "Chọn một ứng dụng web Firebase hiện có" trình đơn thả xuống.
- Nhấp vào "Hoàn tất và triển khai". Sau giây lát, bạn sẽ được đưa đến trang mới để xem trạng thái của phần phụ trợ mới của tính năng Lưu trữ ứng dụng!
- Sau khi quá trình phát hành hoàn tất, hãy nhấp vào miền miễn phí của bạn trong phần "miền". Quá trình này có thể mất vài phút thì mới bắt đầu hoạt động do quá trình truyền DNS.
Bạn đã triển khai ứng dụng web ban đầu! Mỗi lần đẩy một cam kết mới vào nhánh main
của kho lưu trữ GitHub, bạn sẽ thấy một bản dựng mới và bắt đầu phát hành trong bảng điều khiển của Firebase, đồng thời trang web của bạn sẽ tự động cập nhật sau khi quá trình phát hành hoàn tất.
6. Thêm phương thức xác thực vào ứng dụng web
Trong phần này, bạn thêm phương thức xác thực vào ứng dụng web để có thể đăng nhập vào ứng dụng.
Triển khai chức năng đăng nhập và đăng xuất
- Trong tệp
src/lib/firebase/auth.js
, hãy thay thế các hàmonAuthStateChanged
,signInWithGoogle
vàsignOut
bằng đoạn mã sau:
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);
}
}
Mã này sử dụng các API Firebase sau:
API Firebase | Nội dung mô tả |
Tạo một thực thể của nhà cung cấp dịch vụ xác thực của Google. | |
Bắt đầu quy trình xác thực dựa trên hộp thoại. | |
Đăng xuất người dùng. |
Trong tệp src/components/Header.jsx
, mã này đã gọi các hàm signInWithGoogle
và signOut
.
- Tạo một lệnh xác nhận có thông báo xác nhận "Thêm xác thực Google" rồi đẩy tệp đó vào kho lưu trữ GitHub. 1. Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và chờ quá trình triển khai mới hoàn tất.
- Trong ứng dụng web, hãy làm mới trang rồi nhấp vào Đăng nhập bằng Google. Ứng dụng web không cập nhật, vì vậy không rõ liệu đã đăng nhập thành công hay chưa.
Gửi trạng thái xác thực đến máy chủ
Để chuyển trạng thái xác thực tới máy chủ, chúng ta sẽ sử dụng service worker. Thay thế các hàm fetchWithFirebaseHeaders
và getAuthIdToken
bằng đoạn mã sau:
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);
}
Đọc trạng thái xác thực trên máy chủ
Chúng ta sẽ sử dụng FirebaseServerApp để phản ánh trạng thái xác thực của ứng dụng trên máy chủ.
Mở src/lib/firebase/serverApp.js
và thay thế hàm 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 };
}
Đăng ký nhận các thay đổi về phương thức xác thực
Để đăng ký nhận các thay đổi về phương thức xác thực, hãy làm theo các bước sau:
- Chuyển đến tệp
src/components/Header.jsx
. - Thay thế hàm
useUserSession
bằng mã sau:
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;
}
Mã này sử dụng hook state (trạng thái) của React để cập nhật người dùng khi hàm onAuthStateChanged
chỉ định rằng có sự thay đổi đối với trạng thái xác thực.
Xác minh thay đổi
Bố cục gốc trong tệp src/app/layout.js
sẽ hiển thị tiêu đề và truyền cho người dùng (nếu có) dưới dạng một giá trị.
<Header initialUser={currentUser?.toJSON()} />
Tức là thành phần <Header>
hiển thị dữ liệu người dùng (nếu có) trong thời gian chạy máy chủ. Nếu có bất kỳ nội dung cập nhật xác thực nào trong vòng đời của trang sau lần tải trang đầu tiên, thì trình xử lý onAuthStateChanged
sẽ xử lý các nội dung đó.
Giờ là lúc triển khai một bản dựng mới và xác minh thành phần bạn đã xây dựng.
- Tạo một cam kết có thông báo cam kết "Hiển thị trạng thái đăng nhập" rồi đẩy tệp đó vào kho lưu trữ GitHub.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và chờ quá trình triển khai mới hoàn tất.
- Xác minh hành vi xác thực mới:
- Trong trình duyệt, hãy làm mới ứng dụng web. Tên hiển thị của bạn xuất hiện trong tiêu đề.
- Hãy đăng xuất rồi đăng nhập lại. Trang sẽ cập nhật theo thời gian thực mà không cần làm mới trang. Bạn có thể lặp lại bước này với những người dùng khác.
- Không bắt buộc: Nhấp chuột phải vào ứng dụng web, chọn Xem nguồn trang rồi tìm tên hiển thị. Tệp này xuất hiện trong nguồn HTML thô được máy chủ trả về.
7. Xem thông tin nhà hàng
Ứng dụng web bao gồm dữ liệu mô phỏng về nhà hàng và bài đánh giá.
Thêm một hoặc nhiều nhà hàng
Để chèn dữ liệu nhà hàng mô phỏng vào cơ sở dữ liệu Cloud Firestore cục bộ, hãy làm theo các bước sau:
- Trong ứng dụng web, hãy chọn > Thêm các nhà hàng mẫu.
- Trong bảng điều khiển của Firebase trên trang Firestore Database (Cơ sở dữ liệu khôi phục), hãy chọn nhà hàng. Bạn sẽ thấy tài liệu cấp cao nhất trong tập hợp nhà hàng, mỗi tài liệu đại diện cho một nhà hàng.
- Nhấp vào một số tài liệu để khám phá các thuộc tính của một tài liệu về nhà hàng.
Hiển thị danh sách nhà hàng
Cơ sở dữ liệu Cloud Firestore của bạn hiện đã có các nhà hàng mà ứng dụng web Next.js có thể hiển thị.
Để xác định mã tìm nạp dữ liệu, hãy làm theo các bước sau:
- Trong tệp
src/app/page.js
, hãy tìm thành phần máy chủ<Home />
rồi xem lại lệnh gọi đến hàmgetRestaurants
. Hàm này truy xuất danh sách các nhà hàng trong thời gian chạy máy chủ. Bạn sẽ triển khai hàmgetRestaurants
theo các bước sau. - Trong tệp
src/lib/firebase/firestore.js
, hãy thay thế hàmapplyQueryFilters
vàgetRestaurants
bằng đoạn mã sau:
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(),
};
});
}
- Tạo một lệnh xác nhận có thông báo xác nhận "Đọc danh sách các nhà hàng trên Firestore" rồi đẩy tệp đó vào kho lưu trữ GitHub.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và chờ quá trình triển khai mới hoàn tất.
- Trong ứng dụng web, hãy làm mới trang. Hình ảnh nhà hàng xuất hiện dưới dạng ô trên trang.
Xác minh rằng danh sách nhà hàng tải vào thời gian chạy máy chủ
Khi sử dụng khung Next.js, bạn có thể sẽ không thấy rõ thời điểm dữ liệu được tải trong thời gian chạy máy chủ hoặc thời gian chạy phía máy khách.
Để xác minh rằng trang thông tin nhà hàng tải vào thời gian chạy máy chủ, hãy làm theo các bước sau:
- Trong ứng dụng web, hãy mở Công cụ cho nhà phát triển rồi tắt JavaScript.
- Làm mới ứng dụng web. Trang thông tin của nhà hàng vẫn sẽ tải. Thông tin nhà hàng được trả về trong phản hồi của máy chủ. Khi JavaScript được bật, thông tin về nhà hàng sẽ được nước thông qua mã JavaScript phía máy khách.
- Trong Công cụ cho nhà phát triển, hãy bật lại JavaScript.
Nghe thông tin cập nhật về nhà hàng bằng trình nghe ảnh chụp nhanh Cloud Firestore
Trong phần trước, bạn đã thấy cách tải nhóm nhà hàng ban đầu qua tệp src/app/page.js
. Tệp src/app/page.js
là một thành phần máy chủ và được hiển thị trên máy chủ, bao gồm cả mã tìm nạp dữ liệu Firebase.
Tệp src/components/RestaurantListings.jsx
là một thành phần ứng dụng và có thể được định cấu hình để bổ sung dữ liệu đánh dấu do máy chủ kết xuất.
Để định cấu hình tệp src/components/RestaurantListings.jsx
nhằm hydrat hoá mã đánh dấu do máy chủ kết xuất, hãy làm theo các bước sau:
- Trong tệp
src/components/RestaurantListings.jsx
, hãy quan sát mã sau (đã được viết sẵn cho bạn):
useEffect(() => {
const unsubscribe = getRestaurantsSnapshot(data => {
setRestaurants(data);
}, filters);
return () => {
unsubscribe();
};
}, [filters]);
Mã này sẽ gọi hàm getRestaurantsSnapshot()
, tương tự như hàm getRestaurants()
mà bạn đã triển khai ở bước trước. Tuy nhiên, hàm chụp nhanh này cung cấp cơ chế gọi lại để hệ thống thực hiện lệnh gọi lại mỗi khi có thay đổi đối với bộ sưu tập của nhà hàng.
- Trong tệp
src/lib/firebase/firestore.js
, hãy thay thế hàmgetRestaurantsSnapshot()
bằng mã sau:
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;
}
Những thay đổi bạn thực hiện thông qua trang Firestore Database (Cơ sở dữ liệu Khôi phục dữ liệu) giờ đây sẽ phản ánh trong ứng dụng web theo thời gian thực.
- Tạo một cam kết với thông báo cam kết "Nghe thông tin cập nhật về nhà hàng theo thời gian thực" rồi đẩy tệp đó vào kho lưu trữ GitHub.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và chờ quá trình triển khai mới hoàn tất.
- Trong ứng dụng web, hãy chọn > Thêm các nhà hàng mẫu. Nếu triển khai hàm chụp nhanh của bạn đúng cách, các nhà hàng sẽ xuất hiện theo thời gian thực mà không cần làm mới trang.
8. Lưu bài đánh giá do người dùng gửi từ ứng dụng web
- Trong tệp
src/lib/firebase/firestore.js
, hãy thay thế hàmupdateWithRating()
bằng mã sau:
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()),
});
};
Mã này chèn một tài liệu mới trên Firestore để minh hoạ cho bài đánh giá mới. Mã này cũng cập nhật tài liệu hiện có trên Firestore thể hiện nhà hàng bằng số liệu cập nhật về số lượt xếp hạng và điểm xếp hạng trung bình được tính toán.
- Thay thế hàm
addReviewToRestaurant()
bằng mã sau:
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;
}
}
Triển khai hành động của máy chủ Next.js
Hành động trên máy chủ Next.js cung cấp một API thuận tiện để truy cập vào dữ liệu biểu mẫu, chẳng hạn như data.get("text")
để nhận giá trị văn bản từ tải trọng gửi biểu mẫu.
Để sử dụng Hành động của máy chủ Next.js nhằm xử lý việc gửi biểu mẫu xem xét, hãy thực hiện theo các bước sau:
- Trong tệp
src/components/ReviewDialog.jsx
, hãy tìm thuộc tínhaction
trong phần tử<form>
.
<form action={handleReviewFormSubmission}>
Giá trị thuộc tính action
là một hàm mà bạn triển khai trong bước tiếp theo.
- Trong tệp
src/app/actions.js
, hãy thay thế hàmhandleReviewFormSubmission()
bằng mã sau:
// 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"),
});
}
Thêm bài đánh giá về nhà hàng
Bạn đã triển khai dịch vụ hỗ trợ cho việc gửi bài đánh giá, nên giờ đây, bạn có thể xác minh rằng các bài đánh giá của mình đã được chèn đúng cách vào Cloud Firestore.
Để thêm bài đánh giá và xác minh rằng bài đánh giá đó đã được chèn vào Cloud Firestore, hãy làm theo các bước sau:
- Tạo một cam kết có thông báo cam kết "Cho phép người dùng gửi bài đánh giá về nhà hàng" rồi đẩy tệp đó vào kho lưu trữ GitHub.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và chờ quá trình triển khai mới hoàn tất.
- Làm mới ứng dụng web, rồi chọn một nhà hàng trên trang chủ.
- Trên trang của nhà hàng này, hãy nhấp vào .
- Chọn một điểm xếp hạng theo sao.
- Viết bài đánh giá.
- Nhấp vào Gửi. Bài đánh giá của bạn sẽ xuất hiện ở đầu danh sách bài đánh giá.
- Trong Cloud Firestore, hãy tìm trong ngăn Add document (Thêm tài liệu) cho tài liệu của nhà hàng mà bạn đã đánh giá và chọn tài liệu đó.
- Trong ngăn Bắt đầu thu thập, hãy chọn điểm xếp hạng.
- Trong ngăn Add document (Thêm tài liệu), hãy tìm tài liệu cần xem xét để chắc chắn rằng tài liệu đó được chèn như dự kiến.
9. Lưu tệp do người dùng tải lên từ ứng dụng web
Trong phần này, bạn thêm chức năng để có thể thay thế hình ảnh liên kết với một nhà hàng khi bạn đăng nhập. Bạn tải hình ảnh lên Firebase Storage và cập nhật URL hình ảnh trong tài liệu trên Cloud Firestore thể hiện nhà hàng.
Để lưu tệp do người dùng tải lên từ ứng dụng web, hãy làm theo các bước sau:
- Trong tệp
src/components/Restaurant.jsx
, hãy quan sát mã chạy khi người dùng tải tệp lên:
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 });
}
Không cần thay đổi, nhưng bạn sẽ triển khai hành vi của hàm updateRestaurantImage()
theo các bước sau.
- Trong tệp
src/lib/firebase/storage.js
, hãy thay thế hàmupdateRestaurantImage()
vàuploadImage()
bằng đoạn mã sau:
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);
}
Hàm updateRestaurantImageReference()
đã được triển khai cho bạn. Hàm này cập nhật tài liệu hiện có về nhà hàng trong Cloud Firestore bằng URL hình ảnh đã cập nhật.
Xác minh chức năng tải hình ảnh lên
Để xác minh rằng hình ảnh được tải lên như dự kiến, hãy làm theo các bước sau:
- Tạo một lệnh xác nhận có thông báo xác nhận "Cho phép người dùng thay đổi từng nhà hàng" ảnh" rồi đẩy tệp đó vào kho lưu trữ GitHub.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và chờ quá trình triển khai mới hoàn tất.
- Trong ứng dụng web, hãy xác minh rằng bạn đã đăng nhập rồi chọn một nhà hàng.
- Nhấp vào rồi tải hình ảnh lên từ hệ thống tệp của bạn. Hình ảnh của bạn rời khỏi môi trường cục bộ và được tải lên Cloud Storage. Hình ảnh đó sẽ xuất hiện ngay sau khi bạn tải lên.
- Chuyển đến Cloud Storage cho Firebase.
- Chuyển đến thư mục đại diện cho nhà hàng. Hình ảnh mà bạn tải lên tồn tại trong thư mục này.
10. Tóm tắt các bài đánh giá về nhà hàng bằng AI tạo sinh
Trong phần này, bạn sẽ thêm tính năng tóm tắt bài đánh giá để người dùng có thể nhanh chóng hiểu suy nghĩ của mọi người về một nhà hàng mà không cần phải đọc từng bài đánh giá.
Lưu trữ khoá Gemini API trong Cloud Secret Manager
- Để sử dụng Gemini API, bạn sẽ cần có khoá API. Tạo khoá trong Google AI Studio.
- Dịch vụ Lưu trữ ứng dụng tích hợp với Cloud Secret Manager để giúp bạn lưu trữ các giá trị nhạy cảm như khoá API một cách an toàn:
- Trong dòng lệnh, hãy chạy lệnh để tạo một khoá bí mật mới:
firebase apphosting:secrets:set gemini-api-key
- Khi thấy lời nhắc nhập giá trị khoá bí mật, hãy sao chép và dán khoá Gemini API của bạn từ Google AI Studio.
- Khi được hỏi có nên thêm khoá bí mật mới vào
apphosting.yaml
hay không, hãy nhậpY
để chấp nhận.
Khoá Gemini API của bạn hiện được lưu trữ an toàn trong trình quản lý Cloud Secret và phần phụ trợ Lưu trữ ứng dụng của bạn có thể truy cập vào.
Triển khai thành phần tóm tắt bài đánh giá
- Trong
src/components/Reviews/ReviewSummary.jsx
, hãy thay thế hàmGeminiSummary
bằng mã sau: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>; } }
- Tạo cam kết có thông báo cam kết "Dùng AI để tóm tắt bài đánh giá" rồi đẩy tệp đó vào kho lưu trữ GitHub.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và chờ quá trình triển khai mới hoàn tất.
- Mở một trang của một nhà hàng. Ở trên cùng, bạn sẽ thấy nội dung tóm tắt gồm một câu về tất cả các bài đánh giá trên trang.
- Thêm một bài đánh giá mới rồi làm mới trang. Bạn sẽ thấy nội dung tóm tắt thay đổi.
11. Kết luận
Xin chúc mừng! Bạn đã tìm hiểu cách sử dụng Firebase để thêm tính năng và chức năng vào ứng dụng Next.js. Cụ thể, bạn đã sử dụng:
- Lưu trữ ứng dụng Firebase để tự động tạo và triển khai mã Next.js mỗi khi bạn đẩy đến một nhánh được định cấu hình.
- Xác thực Firebase để bật chức năng đăng nhập và đăng xuất.
- Cloud Firestore cho dữ liệu nhà hàng và bài đánh giá nhà hàng.
- Cloud Storage cho Firebase dành cho hình ảnh nhà hàng.