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 một ứng dụng web Next.js có tên là Friendly Eats (Ăn uống thân thiện), đây là một trang web đá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 minh hoạ 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 tạo và triển khai: Lớp học lập trình này sử dụng Dịch vụ lưu trữ ứng dụng Firebase để tự động tạo và triển khai mã Next.js mỗi khi bạn đẩy mã lên một nhánh đã đị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à duy trì trạng thái đăng nhập của người dùng được quản lý hoàn toàn thông qua Xác thực Firebase.
- Hình ảnh: Ứng dụng web hoàn chỉnh cho phép người dùng đã đăng nhập tải hình ảnh nhà hàng lên. Thành phần hình ảnh được lưu trữ trong Cloud Storage cho Firebase. Firebase JavaScript SDK cung cấp một URL công khai cho hình ảnh đã tải lên. Sau đó, URL công khai này sẽ được lưu trữ trong tài liệu nhà hàng có liên quan trong Cloud Firestore.
- Bài đánh giá: Ứng dụng web hoàn chỉnh cho phép người dùng đã đăng nhập đăng bài đánh giá về nhà hàng, bao gồm điểm xếp hạng theo sao và thông báo bằng văn bản. Thông tin đánh giá được lưu trữ trong Cloud Firestore.
- Bộ lọc: Ứng dụng web hoàn chỉnh 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á. Bạn cũng có thể tuỳ chỉnh phương thức sắp xếp được dùng. Dữ liệu được truy cập từ Cloud Firestore và các truy vấn Firestore được áp dụng dựa trên các bộ lọc được dùng.
Điều kiện tiên quyết
- Tài khoản GitHub
- 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 duy 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
- Một 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 chỉnh sửa mã và cửa sổ dòng lệnh
- Một Tài khoản Google để tạo và quản lý dự án Firebase
- Khả năng nâng cấp dự án Firebase 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 vào Firebase CLI.
Tạo kho lưu trữ trên GitHub
Bạn có thể tìm thấy nguồn của lớp học lập trình tại https://github.com/firebase/friendlyeats-web. Kho lưu trữ này 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 một thiết bị đầu cuối, tạo một thư mục mới trên máy tính của bạn và chuyển 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 add . git commit -m "codelab starting point" git branch -M main
- Tạo một kho lưu trữ mới trên GitHub: https://github.com/new. Bạn có thể đặt tên theo ý muốn.
- Sao chép URL mới mà GitHub tạo cho bạn. Mã này sẽ có dạng như một trong những mã sau:
https://github.com/<USER_NAME>/<REPOSITORY_NAME>.git
hoặcgit@github.com:<USER_NAME>/<REPOSITORY_NAME>.git
- Đẩy các thay đổi cục bộ vào kho lưu trữ GitHub mới bằng cách chạy lệnh sau. Thay thế phần giữ chỗ
<REPOSITORY_URL>
bằng URL thực tế của kho lưu trữ.git remote add origin <REPOSITORY_URL> git push -u origin main
- Giờ đây, 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 này là 14.1.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 Giao diện dòng lệnh (CLI) của Firebase do lỗi về quyền, hãy xem tài liệu về npm hoặc sử dụng lựa 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 hay không, 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
- Đăng nhập vào bảng điều khiển của Firebase bằng chính Tài khoản Google mà bạn đã dùng ở bước trước.
- Nhấp vào nút này để tạo một dự án mới, rồi nhập tên dự án (ví dụ:
FriendlyEats Codelab
).
- Nhấp vào Tiếp tục.
- Nếu được nhắc, hãy xem xét và chấp nhận các điều khoản của Firebase, rồi nhấp vào Tiếp tục.
- (Không bắt buộc) Bật tính năng hỗ trợ của AI trong bảng điều khiển của Firebase (còn gọi là "Gemini trong Firebase").
- Đố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 lựa chọn Google Analytics.
- Nhấp vào Tạo dự án, đợi dự án được cấp phép rồi nhấp vào Tiếp tục.
Nâng cấp gói giá của Firebase
Để sử dụng tính năng Lưu trữ ứng dụng Firebase và Bộ nhớ đám mây cho Firebase, dự án Firebase của bạn cần phải sử dụng gói giá trả theo mức sử dụng (Blaze), 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.
- Tài khoản thanh toán trên Cloud yêu cầu 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 trên đám mây dùng thử miễn phí hay không.
- Nếu bạn đang thực hiện lớp học lập trình này trong một sự kiện, hãy hỏi người tổ chức xem có tín dụng Cloud nào không.
Để nâng cấp dự án lên gói Blaze, 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.
- Chọn gói Blaze. Làm theo hướng dẫn trên màn hình để liên kết một tài khoản thanh toán trên Cloud với dự án của bạn.
Nếu cần tạo một tài khoản thanh toán trên Cloud trong quá trình nâng cấp này, bạn có thể cần quay lại quy trình nâng cấp trong bảng điều khiển Firebase để hoàn tất quá trình nâng cấp.
Thêm một ứng dụng web vào dự án Firebase
- Chuyển đến phần Tổng quan về dự án trong dự án Firebase của bạn, rồi nhấp vào
Web.
Nếu bạn đã đăng ký ứng dụng trong dự án của mình, 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 của ứng dụng, hãy nhập một biệt hiệu dễ nhớ cho ứng dụng, chẳng hạn như
My Next.js app
. - Bỏ đánh dấu hộp Đồng thời thiết lập dịch vụ Lưu trữ Firebase cho ứng dụng này.
- Nhấp vào Đăng ký ứng dụng > Tiếp tục đến bảng điều khiển.
Thiết lập các dịch vụ Firebase trong bảng điều khiển của Firebase
Thiết lập tính năng xác thực
- Trong bảng điều khiển của Firebase, hãy chuyển đến mục Xác thực.
- Nhấp vào Bắt đầu.
- Trong cột Nhà cung cấp bổ sung, hãy nhấp vào Google > Bật.
- Trong hộp văn bản Tên dự án công khai, 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 bên trái của bảng điều khiển Firebase, hãy mở rộng mục Tạo rồi chọn Cơ sở dữ liệu Firestore.
- Nhấp vào Tạo cơ sở dữ liệu.
- Để nguyên Mã cơ sở dữ liệu được đặt thành
(default)
. - Chọn một vị trí cho cơ sở dữ liệu của bạn, rồi nhấp vào Tiếp theo.
Đối với một ứng dụng thực tế, bạn nên chọn một vị trí gần với người dùng của mình. - Nhấp vào Bắt đầu ở chế độ thử nghiệm. Đọc tuyên bố từ chối trách nhiệm về các quy tắc bảo mật.
Sau này trong 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 công khai một ứng dụng mà không thêm Quy tắc bảo mật cho cơ sở dữ liệu của bạn. - Nhấp vào Tạo.
Thiết lập Cloud Storage cho Firebase
- Trong bảng điều khiển bên trái của bảng điều khiển Firebase, hãy mở rộng Tạo rồi chọn Bộ nhớ.
- Nhấp vào Bắt đầu.
- Chọn một vị trí cho bộ chứa lưu trữ mặc định.
Các bộ chứa ởUS-WEST1
,US-CENTRAL1
vàUS-EAST1
có thể tận dụng cấp"Luôn miễn phí" của Google Cloud Storage. Các bộ chứa ở tất cả những vị trí khác đều tuân theo mức giá và mức sử dụng của Google Cloud Storage. - Nhấp vào Bắt đầu ở chế độ thử nghiệm. Đọc tuyên bố từ chối trách nhiệm về các quy tắc bảo mật.
Sau này trong lớp học lập trình này, bạn sẽ thêm các 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 công khai ứng dụng mà không thêm Quy tắc bảo mật cho Nhóm lưu trữ. - Nhấp vào Tạo.
Triển khai quy tắc bảo mật
Mã này đã có các quy tắc bảo mật cho Firestore và 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à nhóm của bạn sẽ được bảo vệ tốt hơn khỏi hành vi sử dụng sai mục đích.
- Trong thiết bị đầu cuối, 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 nhậ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 (cũng như các chỉ mục sẽ cần thiết sau này), hãy chạy lệnh sau trong thiết bị đầu cuối:
firebase deploy --only firestore,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ó.
4. Xem xét cơ sở mã khởi đầu
Trong phần này, bạn sẽ xem xét một số khu vực của 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 chứa 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 React 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 tiện ích 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 |
| Tài sả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 |
| Phần phụ thuộc dự án với npm |
| Cấu hình dành riêng cho Next.js (các thao tác trên máy chủ được bật) |
| Cấu hình dịch vụ ngôn ngữ JavaScript |
Các 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 App Router. Tính năng kết xuất phía máy chủ được 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 khách được biểu thị bằng chỉ thị "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 này sử dụng biểu tượng @
để tránh các đường dẫn nhập tương đối rườm rà và được thực hiện nhờ 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 bao bọc 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 nhà hàng và bài đánh giá mô phỏng 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 một phần phụ trợ Dịch vụ lưu trữ ứng dụng
Trong phần này, bạn sẽ thiết lập một phần phụ trợ Lưu trữ ứng dụng để theo dõi một nhánh trên kho lưu trữ git.
Đến cuối phần này, bạn sẽ có một phần phụ trợ Dịch vụ lưu trữ ứng dụng được kết nối với kho lưu trữ của bạn trong GitHub. Phần phụ trợ này sẽ tự động tạo lại và triển khai một phiên bản mới của ứng dụng bất cứ khi nào bạn đẩy một cam kết mới vào nhánh main
.
Tạo một 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:
- Chọn khu vực. Đối với một ứng dụng thực tế, bạn nên chọn khu vực gần người dùng nhất.
- Làm theo lời nhắc trong bước "Nhập kho lưu trữ GitHub" để kết nối với kho lưu trữ GitHub mà bạn đã tạo trước đó.
- Đặt chế độ cài đặt triển khai:
- Giữ thư mục gốc là
/
- Đặt nhánh phát hành công khai thành
main
- Bật tính năng phát hành tự động
- Giữ thư mục gốc là
- Đặt tên cho phần phụ trợ của bạn
friendlyeats-codelab
. - Trong phần "Liên kết một ứng dụng web Firebase", hãy nhấp vào "Tạo một ứng dụng web Firebase mới".
- Nhấp vào "Hoàn tất và triển khai". Sau một lát, bạn sẽ được chuyển đến một trang mới, nơi bạn có thể xem trạng thái của phần phụ trợ mới cho Dịch vụ 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 mục "miền". Do quá trình truyền tin DNS, có thể mất vài phút thì chế độ này mới bắt đầu hoạt động.
- Rất tiếc! Khi tải trang, bạn sẽ thấy thông báo lỗi "Lỗi ứng dụng: đã xảy ra một ngoại lệ phía máy chủ (xem nhật ký máy chủ để biết thêm thông tin)."
- Trong bảng điều khiển của Firebase, hãy kiểm tra thẻ "Nhật ký" của phần phụ trợ Lưu trữ ứng dụng. Bạn sẽ thấy nhật ký "Lỗi: chưa triển khai". Chúng ta sẽ khắc phục vấn đề đó trong bước tiếp theo khi thêm quy trình xác thực.
Bạn đã triển khai ứng dụng web ban đầu! Mỗi khi đẩ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 và quy trình phát hành mới bắt đầu trong bảng điều khiển Firebase, đồng thời trang web của bạn sẽ tự động cập nhật sau khi quy trình phát hành hoàn tất.
6. Thêm xác thực vào ứng dụng web
Trong phần này, bạn sẽ thêm quy trình xác thực vào ứng dụng web để có thể đăng nhập vào ứng dụng đó.
Thêm miền được uỷ quyền
Xác thực Firebase sẽ chỉ chấp nhận các yêu cầu đăng nhập từ những miền mà bạn cho phép. Tại đây, chúng ta sẽ thêm miền của phần phụ trợ Lưu trữ ứng dụng vào danh sách các miền được phê duyệt trong dự án của bạn.
- Sao chép miền phụ trợ của Dịch vụ lưu trữ ứng dụng từ trang "Tổng quan" của Dịch vụ lưu trữ ứng dụng.
- Chuyển đến thẻ Cài đặt uỷ quyền rồi chọn Miền được uỷ quyền.
- Nhấp vào nút Thêm miền.
- Nhập miền của phần phụ trợ Dịch vụ lưu trữ ứng dụng.
- Nhấp vào Thêm.
Triển khai các hàm đăng nhập và đăng xuất
- Trong tệp
src/lib/firebase/auth.js
, hãy thay thế các hàmonAuthStateChanged
,onIdTokenChanged
,signInWithGoogle
vàsignOut
bằng đoạn mã sau:
export function onAuthStateChanged(cb) {
return _onAuthStateChanged(auth, cb);
}
export function onIdTokenChanged(cb) {
return _onIdTokenChanged(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);
}
}
Đoạn mã này sử dụng các API Firebase sau:
Firebase API | Nội dung mô tả |
Thêm một đối tượng tiếp nhận dữ liệu để theo dõi những thay đổi về trạng thái đăng nhập của người dùng. | |
Thêm một đối tượng tiếp nhận dữ liệu để theo dõi các thay đổi đối với mã thông báo nhận dạng của người dùng. | |
Tạo một thực thể trình cung cấp dịch vụ xác thực của Google. | |
Bắt đầu một 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
.
Gửi trạng thái xác thực đến máy chủ
Để truyền trạng thái xác thực đến máy chủ, chúng ta sẽ sử dụng cookie. Bất cứ khi nào trạng thái xác thực thay đổi trong ứng dụng, chúng tôi sẽ cập nhật cookie __session
.
Trong src/components/Header.jsx
, hãy thay thế hàm useUserSession
bằng đoạn mã sau:
function useUserSession(initialUser) {
useEffect(() => {
return onIdTokenChanged(async (user) => {
if (user) {
const idToken = await user.getIdToken();
await setCookie("__session", idToken);
} else {
await deleteCookie("__session");
}
if (initialUser?.uid === user?.uid) {
return;
}
window.location.reload();
});
}, [initialUser]);
return initialUser;
}
Đọ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
rồi thay thế hàm getAuthenticatedAppForUser
:
export async function getAuthenticatedAppForUser() {
const authIdToken = (await cookies()).get("__session")?.value;
// Firebase Server App is a new feature in the JS SDK that allows you to
// instantiate the SDK with credentials retrieved from the client & has
// other affordances for use in server environments.
const firebaseServerApp = initializeServerApp(
// https://github.com/firebase/firebase-js-sdk/issues/8863#issuecomment-2751401913
initializeApp(),
{
authIdToken,
}
);
const auth = getAuth(firebaseServerApp);
await auth.authStateReady();
return { firebaseServerApp, currentUser: auth.currentUser };
}
Xác minh các thay đổi
Bố cục gốc trong tệp src/app/layout.js
sẽ hiển thị tiêu đề và truyền người dùng (nếu có) dưới dạng một prop.
<Header initialUser={currentUser?.toJSON()} />
Điều này có nghĩa là thành phần <Header>
sẽ kết xuất 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 nào về việc xác thực trong vòng đời của trang sau khi tải trang ban đầu, trình xử lý onAuthStateChanged
sẽ xử lý các nội dung cập nhật đó.
Giờ là lúc bạn triển khai một bản dựng mới và xác minh những gì bạn đã tạo.
- Tạo một cam kết có thông báo cam kết "Add authentication" (Thêm quy trình xác thực) rồi đẩy cam kết đó vào kho lưu trữ GitHub của bạn.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và đợi 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 sẽ xuất hiện trong tiêu đề.
- Đăng xuất rồi đăng nhập lại. 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ị. Nội dung này xuất hiện trong nguồn HTML thô do máy chủ trả về.
7. Xem thông tin về nhà hàng
Ứng dụng web này có dữ liệu mô phỏng cho 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:
- Đăng nhập vào ứng dụng web nếu bạn chưa đăng nhập. Sau đó, hãy chọn
> Thêm nhà hàng mẫu.
- Trong bảng điều khiển của Firebase, trên trang Cơ sở dữ liệu Firestore, hãy chọn nhà hàng. Bạn sẽ thấy các tài liệu cấp cao nhất trong bộ sưu tậ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 vài tài liệu để khám phá các thuộc tính của tài liệu 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 />
và xem xét lệnh gọi đến hàmgetRestaurants
. Hàm này sẽ truy xuất danh sách nhà hàng tại thời gian chạy máy chủ. Bạn sẽ triển khai hàmgetRestaurants
trong các bước sau. - Trong tệp
src/lib/firebase/firestore.js
, hãy thay thế các 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 cam kết có thông báo cam kết "Read the list of restaurants from Firestore" (Đọc danh sách nhà hàng từ Firestore) rồi đẩy cam kết đó lên kho lưu trữ GitHub của bạn.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và đợi 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 các ô trên trang.
Xác minh rằng trang thông tin nhà hàng tải vào thời gian chạy máy chủ
Khi sử dụng khung Next.js, có thể bạn không biết 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. Danh sách nhà hàng vẫn tải. Thông tin về nhà hàng được trả về trong phản hồi của máy chủ. Khi JavaScript được bật, thông tin nhà hàng sẽ được truy xuất 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 của 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 bộ nhà hàng ban đầu từ tệp src/app/page.js
. Tệp src/app/page.js
là một thành phần máy chủ và được kết xuất 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 của ứng dụng và có thể được định cấu hình để bổ sung mã đánh dấu do máy chủ kết xuất.
Để định cấu hình tệp src/components/RestaurantListings.jsx
nhằm truyền dữ liệu cho mã đánh dấu được kết xuất phía máy chủ, hãy làm theo các bước sau:
- Trong tệp
src/components/RestaurantListings.jsx
, hãy quan sát đoạn mã sau (đã được viết sẵn cho bạn):
useEffect(() => {
return getRestaurantsSnapshot((data) => {
setRestaurants(data);
}, filters);
}, [filters]);
Đoạn mã này 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 snapshot này cung cấp một cơ chế gọi lại để lệnh gọi lại được gọ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 đoạn 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);
return 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);
});
}
Những thay đổi được thực hiện thông qua trang Cơ sở dữ liệu Firestore hiện được phản ánh trong ứng dụng web theo thời gian thực.
- Tạo một cam kết có thông báo cam kết "Listen for realtime restaurant updates" (Lắng nghe thông tin cập nhật theo thời gian thực của nhà hàng) và đẩy thông báo đó lên kho lưu trữ GitHub của bạn.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và đợi quá trình triển khai mới hoàn tất.
- Trong ứng dụng web, hãy chọn
> Thêm nhà hàng mẫu. Nếu bạn triển khai đúng chức năng chụp nhanh, 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 đoạn 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()),
});
};
Đoạn mã này chèn một tài liệu Firestore mới đại diện cho bài đánh giá mới. Mã này cũng cập nhật tài liệu Firestore hiện có đại diện cho nhà hàng bằng các số liệu mới nhất về số lượt xếp hạng và điểm xếp hạng trung bình đã tính.
- 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 Thao tác máy chủ Next.js
Next.js Server Action 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")
để lấy giá trị văn bản từ tải trọng gửi biểu mẫu.
Để sử dụng Thao tác trên máy chủ Next.js để xử lý biểu mẫu gửi bài đánh giá, hãy làm 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
đề cập đến một hàm mà bạn triển khai ở bước tiếp theo.
- Trong tệp
src/app/actions.js
, hãy thay thế hàmhandleReviewFormSubmission()
bằng đoạn 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á cho nhà hàng
Bạn đã triển khai tính năng hỗ trợ gửi bài đánh giá, vì vậy, giờ đây, bạn có thể xác minh rằng các bài đánh giá của mình được chèn chính xác vào Cloud Firestore.
Để thêm một 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á nhà hàng" và đẩy cam kết đó vào kho lưu trữ GitHub của bạn.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và đợi 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, hãy nhấp vào biểu tượng
.
- Chọn đ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 kiếm ngăn Thêm tài liệu để tìm tài liệu về nhà hàng mà bạn đã đánh giá rồi chọn tài liệu đó.
- Trong ngăn Bắt đầu thu thập, hãy chọn mức phân loại.
- Trong ngăn Thêm tài liệu, hãy tìm tài liệu bạn muốn xem xét để xác minh rằng tài liệu đó đã được chèn như mong đợi.
9. Lưu các tệp do người dùng tải lên từ ứng dụng web
Trong phần này, bạn sẽ thêm chức năng để có thể thay thế hình ảnh được liên kết với một nhà hàng khi đã đă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 Cloud Firestore đại diện cho nhà hàng.
Để lưu các 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 đoạn mã chạy khi người dùng tải một 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);
setRestaurantDetails({ ...restaurantDetails, photo: imageURL });
}
Bạn không cần thay đổi hàm này, nhưng hãy triển khai hành vi của hàm updateRestaurantImage()
trong các bước sau.
- Trong tệp
src/lib/firebase/storage.js
, hãy thay thế các 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 một tài liệu hiện có về nhà hàng trong Cloud Firestore bằng một URL hình ảnh mới.
Xác minh chức năng tải hình ảnh lên
Để xác minh rằng hình ảnh tải lên như mong đợi, 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 "Allow users to change each restaurants' photo" (Cho phép người dùng thay đổi ảnh của từng nhà hàng) rồi đẩy cam kết đó lên kho lưu trữ GitHub của bạn.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và đợi 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 và chọn một nhà hàng.
- Nhấp vào biểu tượng
rồi tải một hình ảnh lên từ hệ thống tệp. Hình ảnh của bạn sẽ 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 bạn tải lên có trong thư mục.
10. Tóm tắt bài đánh giá nhà hàng bằng AI tạo sinh
Trong phần này, bạn sẽ thêm một tính năng tóm tắt bài đánh giá để người dùng có thể nhanh chóng nắm được ý kiến 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 cần có một khoá API. Truy cập vào Google AI Studio rồi nhấp vào "Tạo khoá API".
- Trong ô nhập "Tìm kiếm dự án trên Google Cloud", hãy chọn dự án Firebase của bạn. Mỗi dự án Firebase đều được hỗ trợ bởi một dự án Google Cloud.
- Dịch vụ Lưu trữ ứng dụng tích hợp với Cloud Secret Manager để cho phép bạn lưu trữ an toàn các giá trị nhạy cảm như khoá API:
- Trong một thiết bị đầu cuối, hãy chạy lệnh để tạo một khoá bí mật mới:
firebase apphosting:secrets:set GEMINI_API_KEY
- Khi được nhắc nhập giá trị 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 liệu khoá bí mật mới có dành cho kiểm thử cục bộ hay phát hành công khai, hãy chọn "Phát hành công khai".
- Khi được hỏi liệu bạn có muốn cấp quyền truy cập để tài khoản dịch vụ của phần phụ trợ có thể truy cập vào khoá bí mật hay không, hãy chọn "Có".
- 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 Cloud Secret Manager và có thể truy cập vào phần phụ trợ của dịch vụ Lưu trữ ứng dụng.
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 đoạn mã sau:export async function GeminiSummary({ restaurantId }) { const { firebaseServerApp } = await getAuthenticatedAppForUser(); const reviews = await getReviewsByRestaurantId( getFirestore(firebaseServerApp), restaurantId ); 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 { if (!process.env.GEMINI_API_KEY) { // Make sure GEMINI_API_KEY environment variable is set: // https://firebase.google.com/docs/genkit/get-started throw new Error( 'GEMINI_API_KEY not set. Set it with "firebase apphosting:secrets:set GEMINI_API_KEY"' ); } // Configure a Genkit instance. const ai = genkit({ plugins: [googleAI()], model: gemini20Flash, // set default model }); const { text } = await ai.generate(prompt); return ( <div className="restaurant__review_summary"> <p>{text}</p> <p>✨ Summarized with Gemini</p> </div> ); } catch (e) { console.error(e); return <p>Error summarizing reviews.</p>; } }
- Tạo một cam kết có thông báo cam kết "Sử dụng AI để tóm tắt bài đánh giá" rồi đẩy cam kết đó vào kho lưu trữ GitHub của bạn.
- Mở trang Lưu trữ ứng dụng trong bảng điều khiển của Firebase và đợi quá trình triển khai mới hoàn tất.
- Mở trang của một nhà hàng. Ở trên cùng, bạn sẽ thấy một câu tóm tắt 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 thay đổi trong phần tóm tắt.
11. Kết luận
Xin chúc mừng! Bạn đã tìm hiểu cách sử dụng Firebase để thêm các tính năng và chức năng vào một ứng dụng Next.js. Cụ thể, bạn đã sử dụng những tính năng sau:
- Dịch vụ lưu trữ ứng dụng Firebase để tự động tạo và triển khai mã Next.js mỗi khi bạn đẩy mã lên một nhánh đã đị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à dữ liệu đánh giá nhà hàng.
- Cloud Storage cho Firebase cho hình ảnh nhà hàng.