Hướng dẫn viết prompt hiệu quả để Claude Code tạo các tính năng CRUD, lọc dữ liệu và tìm kiếm trên Firestore trong dự án Next.js — minh hoạ bằng case study Task Management
Hướng dẫn này giúp bạn viết prompt hiệu quả để Claude Code tạo các tính năng Firestore trong dự án Next.js, bao gồm CRUD, lọc dữ liệu và tìm kiếm.
Tất cả ví dụ trong bài dựa trên hệ thống Task Management với các collection tasks, projects, users và subcollection comments. Thay tên field và collection cho phù hợp với dự án của bạn.
Vị trí file: Đặt code ở đâu (ví dụ: services/task-services.ts, components/add-task-modal.tsx)
Loại component: Server component, Client component, hoặc utility module
Điểm tích hợp: Kết nối với service, component nào đã có
Kiểu dữ liệu: Tham chiếu services/types/task-types.ts cho các type đã định nghĩa
Xử lý lỗi: Lỗi được xử lý như thế nào (state cho component, throw cho service)
TypeScript: Luôn chỉ định dùng TypeScript
Cấu trúc thư mục cho Task Management app:
Cấu trúc thư mục
modules└── tasks ├── components │ ├── add-task-modal.tsx # Modal thêm task mới │ ├── columns.tsx # Định nghĩa cột bảng dữ liệu │ ├── data-table-column-header.tsx │ ├── data-table-faceted-filter.tsx # Bộ lọc nhiều điều kiện │ ├── data-table-pagination.tsx # Phân trang │ ├── data-table-row-actions.tsx # Thành phần hành động từng dòng │ ├── data-table-toolbar.tsx # Thanh công cụ bảng │ ├── data-table-view-options.tsx # Tùy chọn hiển thị │ ├── data-table.tsx # Component bảng chính └── services ├── types │ └── task-types.ts # Định nghĩa kiểu dữ liệu Task ├── task_mock-data.ts # Data mẫu Task └── task-services.ts # Service kết nối Firestore thực tế └── task-charts-services.ts # Service lấy dữ liệu để vẽ biểu đồ └── task-statistics-services.ts # Service lấy dữ liệu thống kê
Thêm function để tạo task mới trong Firestore:- File: lib/firebase/tasks.ts- Collection: "tasks"- Fields bắt buộc: title, projectId, reporter- Fields tùy chọn: description, assignee, dueDate, priority, tags- Giá trị mặc định: - status: "todo" - priority: "medium" - isCompleted: false, isBlocked: false, isPinned: false - estimatedHours: 0, actualHours: 0, percentComplete: 0 - createdAt: server timestamp, updatedAt: server timestamp- Validation: - title: bắt buộc, 3–200 ký tự - projectId: phải tồn tại trong collection "projects" - dueDate: nếu có, phải là ngày trong tương lai - priority: chỉ nhận "low" | "medium" | "high" | "critical"- Return: { id: string, task: Task }- Error handling: ValidationError cho input sai, ProjectNotFoundError cho projectId sai- Không tạo API route, chỉ utility function
Tạo function để lấy task theo ID:- Collection: "tasks", Document ID: taskId- Return: Task object với tất cả fields hoặc null nếu không tìm thấy- Xử lý Firestore errors một cách thuần tuý- Bao gồm TypeScript Task interface
Tạo function updateTask:- Nhận taskId và partial task data- Chỉ cập nhật các fields được cung cấp (partial update, không xóa fields khác)- Fields cho phép cập nhật: title, description, status, priority, assignee, dueDate, tags, estimatedHours, actualHours, percentComplete, isBlocked, isPinned- Ngăn chặn cập nhật: createdAt, reporter, projectId- Tự động: - Set updatedAt = server timestamp - Nếu status chuyển sang "done", set isCompleted = true và completedAt = server timestamp - Nếu percentComplete = 100, tự động set status = "done"- Validate status: chỉ nhận "todo" | "in_progress" | "done" | "blocked"- Xử lý lỗi document not found
Thêm function deleteTask:- Soft delete: set status thành "archived" (không xoá khỏi Firestore)- Ghi lại: updatedAt = server timestamp- Không xoá subcollection comments (giữ lại lịch sử)- Return: true nếu thành công, false nếu taskId không tồn tại- Xử lý not found một cách đuồ đặt (không throw lỗi)
Tạo function getTasksByStatus:- Collection: "tasks"- Filter: where("status", "==", status)- status chỉ nhận: "todo" | "in_progress" | "done" | "blocked"- Return: Task[]- Order by: dueDate ascending- Hỗ trợ phân trang (limit parameter, mặc định 20)
Thêm function searchTasks với nhiều điều kiện lọc:- Collection: "tasks"- Bộ lọc (tất cả tùy chọn): - projectId: exact match - assignee: exact match (userId) - status: exact match ("todo" | "in_progress" | "done" | "blocked") - priority: exact match ("low" | "medium" | "high" | "critical") - isBlocked: boolean - dueBefore: Timestamp range (dueDate <= dueBefore) - dueAfter: Timestamp range (dueDate >= dueAfter) - tags: array-contains- Order by: dueDate ascending, updatedAt descending- Return: Task[]- Nếu không có bộ lọc, return tất cả tasks không phải "archived"
Composite index: Firestore yêu cầu tạo composite index khi kết hợp nhiều where() khác field. Trong prompt, hãy liệt kê rõ các field được filter cùng nhau để Claude Code tạo đúng query và nhậc bạn tạo index.
Cập nhật function searchTasks để hỗ trợ cursor-based pagination:- Parameters: filters (như trên), pageSize (mặc định 20), lastDoc (DocumentSnapshot | null)- Lần gọi đầu tiên: lastDoc = null (lấy trang đầu)- Các trang tiếp theo: truyền lastDoc từ kết quả trước (dùng startAfter)- Return: { tasks: Task[], lastDoc: DocumentSnapshot | null, hasMore: boolean }- Order by: dueDate ascending
Thêm function searchTasksByTitle:- Tìm kiếm query string trên field "title"- Thực hiện dùng where("title", ">=", query) và where("title", "<=", query + "\uf8ff")- Kết hợp client-side filter để loại kết quả không khớp- Xử lý search string rỗng (return empty array)- Return: Task[] với limit 30 kết quả đầu
Nếu không có filter, return tất cả tasks không phải “archived”
<Info>**Composite index**: Firestore yêu cầu tạo composite index khi kết hợp nhiều `where()` khác field. Trong prompt, hãy liệt kê rõ các field được filter cùng nhau để Claude Code tạo đúng query và nhắc bạn tạo index.</Info>### 2.3 Pagination**Example prompt:**```text PromptUpdate function searchTasks để hỗ trợ cursor-based pagination:- Parameters: filters (như trên), pageSize (mặc định 20), lastDoc (DocumentSnapshot | null)- Lần gọi đầu tiên: lastDoc = null (lấy trang đầu)- Các trang tiếp theo: truyền lastDoc từ kết quả trước (dùng startAfter)- Return: { tasks: Task[], lastDoc: DocumentSnapshot | null, hasMore: boolean }- Order by: dueDate ascending
Thêm function searchTasksByTitle:- Search query string trên field "title"- Implement dùng where("title", ">=", query) và where("title", "<=", query + "\uf8ff")- Kết hợp client-side filter để loại kết quả không khớp- Handle empty search string (return empty array)- Return: Task[] với limit 30 kết quả đầu
Firestore không có native full-text search. Các lựa chọn thay thế:
Tạo hook hooks/useTasks.ts:- Export hook: useTasks(filters: TaskFilters)- State: { tasks: Task[], loading: boolean, error: Error | null }- Lấy dữ liệu từ API endpoint /api/tasks/search với filters làm query params- Gọi lại khi filters thay đổi (useEffect với filters làm dependency)- Return: { tasks, loading, error, refetch }- Xử lý cleanup khi component unmount
# Kiểu enum (giá trị cố định)task: status: todo | in_progress | done | blocked priority: low | medium | high | criticalproject: status: active | archived | completeduser: role: admin | manager | developer | designer# Mô tả cấu trúc task đầy đủtask: id: text title: text (bắt buộc) description: text (tùy chọn) status: todo | in_progress | done | blocked priority: low | medium | high | critical projectId: id của project assignee: id của user (tùy chọn) reporter: id của user (bắt buộc) tags: danh sách text isCompleted: true | false isBlocked: true | false estimatedHours: số actualHours: số percentComplete: số (0–100) dueDate: timestamp completedAt: timestamp hoặc null createdAt: timestamp updatedAt: timestamp# Sau đó yêu cầu thực hiện:# createTask, getTask, updateTask, deleteTask, getTaskComments, addComment
Cho function getTaskWithDetails:- Throw các lỗi cụ thể: - TaskNotFoundError khi taskId không tồn tại - ValidationError cho taskId format không hợp lệ - FirestoreError cho lỗi network/permission- Bao gồm thông báo lỗi kèm ngữ cảnh (ví dụ: taskId nào gây lỗi)
Thêm validation cho createTask:- title: bắt buộc, 3–200 ký tự- projectId: bắt buộc, phải tồn tại trong collection "projects"- dueDate: nếu có, phải là ngày trong tương lai- priority: phải là "low" | "medium" | "high" | "critical"- tags: mỗi tag tối đa 30 ký tự, tối đa 10 tags- Return: { success: true, id: string } hoặc { success: false, errors: string[] }
Tạo function completeTask sử dụng Firestore transaction:- Cập nhật task document: - status: "done" - isCompleted: true - completedAt: server timestamp - percentComplete: 100- Cập nhật user stats trong collection "users": - Tăng completedTasksCount lên 1 - Cập nhật lastActivityAt: server timestamp- Dùng Firestore transaction để đảm bảo atomicity (cả 2 updates hoặc không cái nào)- Return: task ID nếu thành công- Tự động rollback nếu bất kỳ bước nào thất bại- Throw TaskNotFoundError nếu taskId không hợp lệ
Tạo function [tên function] cho collection [tên collection]:Đầu vào:- Tham số: [danh sách kèm kiểu dữ liệu]- Validation: [quy tắc kiểm tra]Lo-gic:- Thao tác: [create/read/update/delete]- Điều kiện query: [nếu có]- Biến đổi dữ liệu: [nếu có]Đầu ra:- Kiểu trả về: [mô tả chính xác]- Trường hợp thành công: [dữ liệu trả về]- Trường hợp lỗi: [xử lý lỗi cụ thể]Bổ sung:- Xử lý timestamp: [createdAt/updatedAt/completedAt]- Validation enum: [giá trị hợp lệ của status/priority]- Thông báo lỗi: [mô tả lỗi có ngữ cảnh]
Tạo function [tên function]:Collection: [tên] (hoặc subcollection: [parent/{id}/tên])Lọc theo:- Field 1: [kiểu] ([exact match / range / array-contains])- Field 2: [kiểu] ([exact match / range / array-contains])Sắp xếp:- Chính: [field] [tăng/giảm]- Phụ: [field] [tăng/giảm]Phân trang:- Kiểu: cursor-based- Số lượng mặc định: [số]Trả về:- Kiểu: { items: T[], lastDoc: DocumentSnapshot | null, hasMore: boolean }- Bao gồm: [các field cần lấy]Trường hợp biên:- Không có kết quả: return mảng rỗng- Giá trị enum không hợp lệ: throw ValidationError- Dataset lớn: cursor-based pagination
Dùng khi: Cần HTTP endpoint cho client gọi qua mạng
File: app/api/tasks/route.tsPrompt cần có: HTTP method, body/query params, định dạng phản hồi, status codes
Dùng khi: Lấy dữ liệu phía server trong quá trình render trang
File: app/tasks/page.tsxPrompt cần có: Cách dùng server component, mẫu await, xử lý lỗi phía server
Dùng khi: Cập nhật real-time, tương tác người dùng, gửi form
File: hooks/useTasks.ts hoặc hooks/useTask.tsPrompt cần có: useState, useEffect, cleanup, trạng thái loading/error/success
Dùng khi: Gửi form, thay đổi dữ liệu từ client không cần API route
File: app/actions/tasks.tsPrompt cần có: Chỉ thị 'use server', định nghĩa kiểu, validation, giá trị trả vềSơ đồ quyết định:
Sơ đồ chọn pattern
Chọn pattern cho thao tác Firestore:Chỉ đọc dữ liệu? ├─ Server component (page/layout)? → Pattern 1 hoặc Pattern 3 └─ Client component? → Pattern 4 (hook)Cần cập nhật real-time? └─ → Pattern 4 (hook với onSnapshot listener)Thay đổi/ghi dữ liệu? ├─ Gửi form? → Pattern 5 (server action) hoặc Pattern 2 (API route) └─ Tương tác client? → Pattern 2 (API route) hoặc Pattern 5 (server action)Logic dùng chung (xác thực, validation)? └─ → Pattern 1 (utility function)
Thêm function createTask trong lib/firebase/tasks.ts:- Collection: "tasks"- Đầu vào: { title, projectId, reporter, description?, assignee?, dueDate?, priority?, tags? }- Validation: - title: bắt buộc, 3–200 ký tự - projectId: phải tồn tại trong collection "projects" - dueDate: nếu có, phải là ngày trong tương lai - priority: chỉ nhận "low" | "medium" | "high" | "critical" - tags: tối đa 10 tags, mỗi tag ≤ 30 ký tự- Fields cần lưu: - Tất cả đầu vào - status: "todo" (mặc định) - priority: "medium" nếu không truyền - isCompleted: false, isBlocked: false, isPinned: false - estimatedHours: 0, actualHours: 0, percentComplete: 0 - createdAt: server timestamp, updatedAt: server timestamp- Return: { id: string, task: Task }- Lỗi: ValidationError cho dữ liệu sai, ProjectNotFoundError cho projectId sai
Tạo function completeTask trong lib/firebase/tasks.ts:- Đầu vào: taskId: string, notes?: string- Dùng Firestore transaction để cập nhật đồng thời: 1. Task document (tasks/{taskId}): - status: "done" - isCompleted: true - percentComplete: 100 - completedAt: server timestamp - updatedAt: server timestamp 2. User stats (users/{assigneeId}): - Tăng completedTasksCount += 1 - lastActivityAt: server timestamp- Return: { success: true }- Lỗi: TaskNotFoundError nếu taskId không tồn tại- Tự động rollback cả 2 updates nếu bất kỳ bước nào thất bại
Tạo tính năng quản lý task với 3 files:1. lib/firebase/tasks.ts: - createTask(data: CreateTaskInput): Promise<{ id, task }> - getTaskById(taskId: string): Promise<Task | null> - updateTask(taskId: string, data: Partial<Task>): Promise<Task> - deleteTask(taskId: string): Promise<boolean> - Export interfaces: Task, CreateTaskInput, TaskStatus, TaskPriority2. app/api/tasks/route.ts: - GET: query params ?projectId=&status=&assignee= → gọi searchTasks - POST: body { title, projectId, reporter, ... } → gọi createTask - Xử lý lỗi validation (return 400) - Xử lý project không tồn tại (return 404) - Return đúng status codes3. hooks/useTaskMutation.ts: - Hook: useTaskMutation() - createTask(data): gọi POST /api/tasks - updateTask(id, data): gọi PATCH /api/tasks/[id] - deleteTask(id): gọi DELETE /api/tasks/[id] - Quản lý trạng thái loading, error - Cleanup khi component unmountYêu cầu: - TypeScript cho tất cả files - Xử lý lỗi mạng - Validation ở cả client (hook) và server (API route) - Tuân theo quy ước đặt tên Next.js - Không tạo thêm files ngoài 3 files trên