Giới thiệu

Firestore realtime cho phép ứng dụng của bạn lắng nghe thay đổi dữ liệu ngay lập tức. Khi một document được thêm, sửa hoặc xóa, client sẽ nhận được cập nhật tự động mà không cần refresh trang. Hai case study trong tài liệu này giúp bạn thực hành pattern realtime phổ biến:
  1. Ứng dụng chat: Tin nhắn hiển thị đồng bộ trên nhiều thiết bị.
  2. Ứng dụng quiz ranking: Giáo viên điều khiển bài quiz, học sinh trả lời và bảng xếp hạng cập nhật real-time.
Tài liệu này giả định bạn đã có dự án Next.js với Firebase đã cấu hình. Nếu chưa, xem Thiết lập Firebase và Firestore và tổ chức Data.

Case study 1: Ứng dụng chat

Thiết kế dữ liệu

Bạn cần hai collection chính:
Lưu senderName và senderPhotoURL trong message để tránh phải join với users khi hiển thị. Dữ liệu user có thể thay đổi, nhưng tên trong tin nhắn thường giữ nguyên để giữ ngữ cảnh lịch sử.

Gửi tin nhắn

Hàm gửi tin nhắn cần:
  • Kiểm tra người dùng đã đăng nhập.
  • Kiểm tra nội dung tin nhắn không để trống.
  • Ghi document vào subcollection messages của chat-rooms/{roomId}.
  • Lưu senderId, senderName, senderPhotoURL và timestamp từ server.

Lắng nghe tin nhắn real-time

Hàm đăng ký listener dùng onSnapshot trên subcollection messages, sắp xếp theo createdAt tăng dần. Khi có document mới, sửa hoặc xóa, callback nhận danh sách tin nhắn cập nhật.

Sử dụng trong React component

Trong component React, bạn đăng ký listener khi mount và cleanup khi unmount. State messages được cập nhật tự động. Form gửi tin nhắn gọi hàm gửi và xóa input sau khi gửi.

Security Rules cho chat

  • Chỉ người dùng đã đăng nhập mới được đọc phòng chat và tin nhắn.
  • Người tạo phòng chat mới được sửa/xóa phòng.
  • Người gửi tin nhắn mới được sửa/xóa tin nhắn của chính mình.

Prompt mẫu cho vibe-coding (chat app)


Case study 2: Ứng dụng quiz ranking

Mô tả hệ thống

Hệ thống quiz gồm hai vai trò:
  • Giáo viên: Tạo quiz, phát đề, theo dõi bảng xếp hạng real-time.
  • Học sinh: Tham gia quiz bằng auth uid, trả lời câu hỏi, xem điểm của mình cập nhật ngay.

Thiết kế dữ liệu

Lưu questions trong document quiz vì một bộ đề thường nhỏ hơn 1MB. Nếu đề lớn, tách thành subcollection questions riêng.

Màn hình giáo viên: tạo và điều khiển quiz

Giáo viên cần các hàm sau:
  • createQuiz: tạo document quiz với trạng thái waiting, lưu tiêu đề, danh sách câu hỏi, createdBy và timestamp.
  • startQuiz: chuyển trạng thái quiz sang active, ghi startedAt.
  • finishQuiz: chuyển trạng thái quiz sang finished, ghi endedAt.

Lắng nghe bảng xếp hạng real-time (giáo viên)

Hàm đăng ký listener trên subcollection participants, sắp xếp theo score giảm dần. Mỗi lần dữ liệu thay đổi, callback nhận danh sách participant kèm thứ hạng.

Màn hình học sinh: tham gia và trả lời

Học sinh cần các hàm sau:
  • joinQuiz: tạo hoặc cập nhật document participant của học sinh trong quiz, lưu displayName, điểm ban đầu bằng 0 và thời gian tham gia.
  • submitAnswer: đọc document quiz để lấy câu hỏi và đáp án đúng, tính điểm, sau đó cập nhật document participant với câu trả lời mới và tổng điểm. Cần kiểm tra quiz đang ở trạng thái active.

Theo dõi điểm của chính học sinh

Hàm đăng ký listener trên document participant của người dùng hiện tại. Nếu chưa đăng nhập hoặc chưa tham gia, callback nhận giá trị null.

Security Rules cho quiz

  • Chỉ người dùng đã đăng nhập mới được đọc quiz.
  • Chỉ giáo viên tạo quiz mới được sửa/xóa quiz.
  • Học sinh chỉ được tạo/sửa document participant của chính mình.
  • Điểm và số câu đã trả lời chỉ được tăng, không được giảm.
  • Không cho phép xóa document participant.
Rule score >= resource.data.score ngăn học sinh tự giảm điểm hoặc xóa câu trả lời. Trong ứng dụng thực tế, bạn nên xử lý chấm điểm ở server hoặc Cloud Function để tránh gian lận phía client.

Prompt mẫu cho vibe-coding (quiz ranking)


So sánh hai pattern realtime


Best practices

1. Luôn cleanup listener

Trong React, hãy trả về hàm unsubscribe từ useEffect. Nếu không cleanup, listener tiếp tục chạy khi component bị hủy, gây rò rỉ bộ nhớ và tốn chi phí đọc Firestore.

2. Tránh lắng nghe quá nhiều document

Nếu danh sách participant có thể lớn, hãy giới hạn số lượng hiển thị trên bảng xếp hạng và cung cấp nút “Xem thêm”. Sử dụng limit kết hợp orderBy trong query để giảm số document cần đọc.

3. Tách logic Firestore ra khỏi component

Đặt các hàm như subscribeToMessages, sendMessage, submitAnswer trong thư mục lib/ thay vì viết trực tiếp trong component. Giúp tái sử dụng và dễ test hơn.

4. Validate ở client trước khi ghi

Kiểm tra dữ liệu trước khi gọi Firestore để tránh lỗi và giảm số lần ghi không cần thiết. Ví dụ: từ chối tin nhắn rỗng, kiểm tra quiz đang active trước khi nộp đáp án.

5. Dùng server timestamp

Luôn dùng timestamp từ server cho các trường createdAt và updatedAt để thời gian nhất quán trên tất cả client.

6. Cẩn thận với dữ liệu nhạy cảm

Trong quiz, việc lưu correctIndex trong document quiz có thể bị học sinh đọc được nếu rules không chặt. Nếu cần giấu đáp án, chuyển logic chấm điểm sang Cloud Function hoặc Server Action.

Bài tập thực chiến

Bài 1: Mở rộng chat app

Thêm các tính năng sau vào ứng dụng chat:
  • Hiển thị “đang nhập…” khi người khác gõ tin nhắn.
  • Cho phép xóa tin nhắn của chính mình.
  • Hiển thị thời gian gửi theo định dạng “2 phút trước”.
Gợi ý: dùng document chat-rooms/{roomId}/typing/{userId} để theo dõi trạng thái đang nhập.

Bài 2: Hoàn thiện quiz ranking

Thêm các tính năng sau:
  • Giáo viên chuyển từng câu hỏi một bằng currentQuestionIndex.
  • Học sinh chỉ thấy câu hỏi hiện tại và chỉ được trả lời một lần.
  • Sau khi kết thúc, hiển thị podium top 3.

Bài 3: Viết prompt tổng hợp

Viết một prompt duy nhất để Claude Code tạo cả chat app và quiz ranking trong cùng một dự án Next.js, với navigation chuyển đổi giữa hai tính năng.

Tài liệu liên quan