0

# Bài 23 — Lỗi thường gặp & cách debug

⬅️ Bài trước | Mục lục


🎯 Mục đích

Đây là trang tra cứu nhanh. Khi bị kẹt, dùng Ctrl+F tìm thông báo lỗi bạn đang gặp.

Cuối bài có mục Phương pháp debug — quan trọng hơn cả danh sách lỗi, vì nó dạy bạn cách tự tìm ra nguyên nhân cho những lỗi không có trong danh sách này.


📑 Mục lục nhanh


A. Lỗi khởi động & môi trường

'node' is not recognized as an internal or external command

Node chưa vào PATH.

  1. Đóng hết terminal, mở lại
  2. Vẫn lỗi → cài lại Node, nhớ tick "Add to PATH"
  3. Kiểm tra: node -v

Error: listen EADDRINUSE: address already in use :::3000

Port đang bị chiếm. Thường là do backend và frontend cùng dùng port 3000.

# Tìm tiến trình đang chiếm port
netstat -ano | findstr :3000
# Tắt nó (thay <PID> bằng số ở cột cuối)
taskkill /PID <PID> /F

Cách sửa đúng: đổi backend sang port 5000 trong backend/bin/www (bài 04).

macOS: port 5000 bị AirPlay chiếm → tắt trong System Settings hoặc dùng 5001.


Cannot find module 'xxx'

Chưa cài thư viện:

npm install xxx

Nếu đã cài rồi mà vẫn lỗi:

rm -rf node_modules package-lock.json    # Windows: Remove-Item -Recurse -Force
npm install

nodemon: command not found

npm install --save-dev nodemon
# hoặc chạy trực tiếp
npx nodemon ./bin/www

B. Lỗi MongoDB

MongoServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017

MongoDB chưa chạy. Đây là lỗi phổ biến nhất khi mới bắt đầu.

Get-Service MongoDB       # phải là Running
Start-Service MongoDB     # nếu Stopped

Nếu không có service nào tên MongoDB → bạn cài mà không tick "Install as a Service". Chạy thủ công:

& "C:\Program Files\MongoDB\Server\7.0\bin\mongod.exe" --dbpath "C:\data\db"

connect ECONNREFUSED ::1:27017 (chú ý ::1 chứ không phải 127.0.0.1)

Node phân giải localhost thành IPv6 nhưng MongoDB chỉ nghe IPv4.

Sửa trong backend/model/db.js:

const url = "mongodb://127.0.0.1:27017";   // dùng IP thay vì localhost

API trả về mảng rỗng [] dù database có dữ liệu

Kiểm tra theo thứ tự:

  1. Sai tên database — dbName trong db.js phải khớp chính xác
  2. Sai tên collection — db.collection("products") phân biệt hoa thường
  3. Chưa import dữ liệu — chạy db.products.countDocuments() trong Compass
  4. Kiểu dữ liệu không khớp — xem lỗi tiếp theo

/products/byCategory/:id luôn trả về []

Lỗi kinh điển của dự án này. categoryId trong DB là chuỗi, nhưng query tìm bằng ObjectId.

Kiểm tra:

db.products.countDocuments({ categoryId: { $type: "string" } })
// Ra > 0 → đây chính là vấn đề

Sửa dữ liệu:

db.products.find({ categoryId: { $type: "string" } }).forEach(function (doc) {
  db.products.updateOne(
    { _id: doc._id },
    { $set: { categoryId: new ObjectId(doc.categoryId) } }
  );
});

Chi tiết ở bài 03 và bài 06.


BSONError: input must be a 24 character hex string

Bạn truyền chuỗi không hợp lệ vào new ObjectId().

// Luôn kiểm tra trước
if (!ObjectId.isValid(req.params.id)) {
  return res.status(400).json({ message: "ID không hợp lệ" });
}

E11000 duplicate key error collection: ... index: _id_ dup key: { _id: null }

Bạn đặt _id: null khi insert. Chỉ thêm được 1 document, cái thứ 2 sẽ lỗi.

Sửa: xóa dòng _id: null — MongoDB tự sinh _id.

Đây là lỗi trong POST /products của source gốc (bài 07).


updateOne không thay đổi gì

Quên toán tử $set:

updateOne(filter, { price: 100 })          // ❌ lỗi
updateOne(filter, { $set: { price: 100 } }) // ✅

C. Lỗi Express / Backend

Cannot GET /products

Router chưa được gắn vào app.js:

var productsRouter = require('./routes/products');
app.use('/products', productsRouter);

Và file router phải có module.exports = router; ở cuối.


req.body is undefined

Thiếu middleware parse body:

app.use(express.json());                          // cho JSON
app.use(express.urlencoded({ extended: false }));  // cho form

Phải đặt TRƯỚC các app.use(...router).

Nếu đang upload file → dùng Multer, không phải express.json().


Request bị treo, không có phản hồi

Ba nguyên nhân:

  1. Middleware quên gọi next() — request dừng giữa chừng
  2. Route async ném lỗi mà không có try/catch — Express 4 không bắt được
  3. MongoDB không kết nối được — await connectDb() treo mãi

Cách phòng: bọc mọi route async trong try/catch + next(error).


Cannot set headers after they are sent to the client

Bạn gọi res.json() hai lần. Thường do quên return:

// ❌ Sai
if (!product) {
  res.status(404).json({ message: "..." });   // gửi lần 1
}
res.status(200).json(product);                 // gửi lần 2 → LỖI

// ✅ Đúng
if (!product) {
  return res.status(404).json({ message: "..." });
}
res.status(200).json(product);

Route không bao giờ chạy

Thứ tự route sai — route tổng quát "nuốt" route cụ thể:

// ❌
router.get("/:id", ...);          // khớp cả "/topRating"
router.get("/topRating", ...);    // không bao giờ chạy

// ✅ cụ thể trước, tổng quát sau
router.get("/topRating", ...);
router.get("/:id", ...);

D. Lỗi React / Next.js

Warning: Invalid DOM property 'class'. Did you mean 'className'?

JSX dùng className, không dùng class.

Find & Replace với regex:

  • Find: \sclass=" → Replace: className="
  • Find: for=" → Replace: htmlFor="
  • tabindex → tabIndex, colspan → colSpan, crossorigin → crossOrigin, enctype → encType

Nothing was returned from render

Component không return gì, hoặc return giá trị không phải JSX:

// ❌
export default function Product() { }
if (error) return (console.log('lỗi'));   // console.log trả về undefined

// ✅
export default function Product() { return <div>...</div>; }
if (error) return <div>Đã có lỗi</div>;

Hai lỗi này đều có trong source gốc (bài 10, bài 12).


You're importing a component that needs useState. It only works in a Client Component

Thiếu "use client" ở dòng đầu tiên của file.

⚠️ Kiểm tra chính tả: "use clinet" (lỗi trong source gốc) không có tác dụng gì.


You are attempting to export "metadata" from a component marked with "use client"

export const metadata chỉ dùng được trong Server Component.

Cách sửa: tách phần cần client ra component riêng, giữ layout là server component (bài 17).


data.map is not a function

data không phải mảng. Debug:

console.log("data =", data);

Nguyên nhân thường gặp:

  1. API trả { message: "..." } thay vì mảng (lỗi 404 nhưng fetcher không bắt)
  2. NEXT_PUBLIC_API_URL là undefined → fetch tới URL sai
  3. API đã đổi sang trả { data, pagination } (bài 21)

Phòng thủ:

const list = Array.isArray(data) ? data : [];

Warning: Each child in a list should have a unique "key" prop

Thiếu key khi .map():

{data.map((item) => <Product key={item._id} data={item} />)}

Encountered two children with the same key

Key bị trùng. Trong dự án này xảy ra ở giỏ hàng — cùng sản phẩm khác size:

key={product._id}                            // ❌ trùng
key={`${product._id}-${product.size}`}       // ✅

Hydration failed because the initial UI does not match what was rendered on the server

HTML ở server khác HTML ở client. Nguyên nhân:

Nguyên nhân Cách sửa
Đọc localStorage khi render Dùng PersistGate (bài 14)
Date.now(), Math.random() trong JSX Đưa vào useEffect
HTML không hợp lệ (<div> trong <p>, <Link> trong <ul>) Sửa cấu trúc HTML
<html>/<body> lồng nhau Tách route group (bài 17)

React has detected a change in the order of Hooks

Bạn gọi hook sau câu return hoặc bên trong if:

// ❌
if (isLoading) return <p>Loading</p>;
const [x, setX] = useState(0);    // hook sau return

// ✅ mọi hook phải ở TRÊN CÙNG
const [x, setX] = useState(0);
if (isLoading) return <p>Loading</p>;

NextRouter was not mounted

Import sai. App Router dùng:

import { useRouter } from "next/navigation";   // ✅
import { useRouter } from "next/router";       // ❌ (Pages Router cũ)

useSearchParams() should be wrapped in a suspense boundary

import { Suspense } from "react";

export default function Page() {
  return <Suspense fallback={<div>Loading...</div>}><Content /></Suspense>;
}

Lỗi này chỉ xuất hiện khi npm run build, không thấy khi npm run dev.


Modal / dropdown / carousel không hoạt động

Bootstrap JS chưa được nạp, hoặc đường dẫn sai:

<script src="js/bootstrap.bundle.min.js"></script>    {/* ❌ đường dẫn tương đối */}
<script src="/js/bootstrap.bundle.min.js"></script>   {/* ✅ tuyệt đối */}

Với đường dẫn tương đối, ở trang /cart trình duyệt tìm /cart/js/bootstrap... → 404. Đây là lỗi trong source gốc (bài 10).

Kiểm tra: mở Console gõ bootstrap — phải ra object, không phải undefined.


Icon Font Awesome không hiển thị (chỉ thấy ô vuông)

  1. Kit CDN đã hết hạn hoặc bị giới hạn domain → dùng kit riêng hoặc CDN công khai
  2. Dùng icon Pro (fa-light, fal, fa-duotone) với tài khoản miễn phí → đổi sang fa-solid / fas
  3. File font trong public/font/ bị thiếu → kiểm tra tab Network, lọc "Font"

E. Lỗi Redux

could not find react-redux context value; please ensure the component is wrapped in a <Provider>

Component dùng useSelector/useDispatch nhưng nằm ngoài <Provider>.

<Providers>
  <Navbar />       {/* ← phải nằm TRONG */}
  {children}
</Providers>

Cannot read properties of undefined (reading 'reduce')

state.cart là undefined — tên key không khớp:

combineReducers({ cart: cartSlicer })    // định nghĩa
useSelector((state) => state.carts)      // ❌ dùng sai tên

A non-serializable value was detected in an action

Cảnh báo do redux-persist. Sửa trong configureStore:

middleware: (getDefaultMiddleware) =>
  getDefaultMiddleware({
    serializableCheck: {
      ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
    },
  }),

Dispatch action nhưng state không đổi

  1. Mở Redux DevTools — action có xuất hiện không?
  2. Có action, state không đổi → reducer sai (quên state.push, sai điều kiện findIndex)
  3. Không có action → onClick chưa gắn, hoặc thiếu "use client"

Giỏ hàng mất khi F5

Chưa cấu hình redux-persist, hoặc quên <PersistGate> (bài 14).


F. Lỗi kết nối Frontend ↔ Backend

Access to fetch at '...' has been blocked by CORS policy

Backend chưa bật CORS, hoặc cors() đặt sau router:

var cors = require("cors");
app.use(cors());              // ← phải đặt TRƯỚC mọi app.use(...router)

Kiểm tra: DevTools → Network → request → Response Headers phải có access-control-allow-origin.


Failed to fetch / ERR_CONNECTION_REFUSED

Backend chưa chạy. Mở terminal thứ hai:

cd backend && npm run dev

Kiểm tra bằng cách mở thẳng http://localhost:5000/products trong trình duyệt.


URL fetch là undefined/products

Biến môi trường chưa được nạp:

  1. File phải tên đúng .env.local, đặt ở frontend/ (không phải thư mục gốc)
  2. Biến phải có tiền tố NEXT_PUBLIC_
  3. Phải khởi động lại npm run dev sau khi sửa .env.local

Ảnh không hiển thị

Chuột phải vào ảnh vỡ → "Open image in new tab", xem URL thực tế:

URL thấy được Nguyên nhân
undefinedsp1.jpg NEXT_PUBLIC_IMA_URL chưa nạp → restart dev server
localhost:5000/imgsp1.jpg Thiếu / cuối trong IMA_URL
localhost:5000/img/sp1.jpg nhưng 404 File không có trong backend/public/img/
localhost:3000/img/sp1.jpg Ảnh sản phẩm nằm ở backend, không phải frontend

G. Lỗi upload file

req.file is undefined

  1. Thiếu middleware Multer trên route:
    router.post("/", upload.single("image"), handler);
    
  2. Tên trường không khớp:
    formData.append("image", file);   // frontend
    upload.single("image")            // backend — phải cùng tên
    
  3. Đặt header Content-Type thủ công — xem lỗi tiếp theo

req.body rỗng khi upload file

Bạn tự đặt Content-Type:

// ❌ SAI — thiếu boundary, Multer không parse được
fetch(url, {
  method: "POST",
  headers: { "Content-Type": "multipart/form-data" },
  body: formData,
});

// ✅ ĐÚNG — để trình duyệt tự đặt
fetch(url, { method: "POST", body: formData });

MulterError: LIMIT_UNEXPECTED_FILE

Tên trường trong formData.append() khác với upload.single().


MulterError: LIMIT_FILE_SIZE

File vượt quá limits.fileSize. Tăng giới hạn hoặc nén ảnh trước khi upload.


Ảnh upload xong nhưng không hiển thị

  1. Kiểm tra file có thật trong backend/public/img/
  2. Kiểm tra image lưu trong DB là req.file.filename (tên thật trên đĩa), không phải req.file.originalname
  3. Kiểm tra express.static đã cấu hình trong app.js

File lưu sai thư mục

destination: "./public/img" là đường dẫn tương đối với thư mục đang chạy lệnh.

Sửa thành:

const path = require("path");
destination: (req, file, cb) => cb(null, path.join(__dirname, "../public/img"))

H. Lỗi khi deploy

Build lỗi Module not found trên Vercel nhưng chạy được ở local

Phân biệt hoa thường. Windows không phân biệt, Linux có.

import Product from "./component/Product";   // file thật: product.js → lỗi trên Linux

Chạy npm run build ở local trước khi push để phát hiện sớm.


bad auth : Authentication failed (MongoDB Atlas)

  1. Sai mật khẩu trong connection string
  2. Ký tự đặc biệt chưa mã hóa URL: @→%40, #→%23, /→%2F
  3. User chưa có quyền đọc/ghi

Kết nối Atlas timeout

Network Access chưa cho phép 0.0.0.0/0.


Request đầu tiên mất 60 giây

Render gói Free đang "thức dậy" sau 15 phút ngủ. Dùng UptimeRobot ping /health.


Ảnh upload lên production rồi biến mất

Filesystem của Render/Heroku là ephemeral. Bắt buộc phải chuyển sang Cloudinary hoặc S3 (bài 22).


NEXT_PUBLIC_* là undefined trên production

Biến NEXT_PUBLIC_* được nhúng vào bundle lúc build. Thêm biến trên Vercel xong phải Redeploy thì mới có tác dụng.


🔍 I. Phương pháp debug

Danh sách lỗi ở trên không bao giờ đủ. Đây mới là thứ giúp bạn tự giải quyết mọi lỗi.

Quy tắc 1 — Đọc thông báo lỗi. Thật sự đọc.

Đa số người mới nhìn thấy màu đỏ là hoảng và bỏ qua. Nhưng 80% thông báo lỗi đã nói thẳng vấn đề là gì.

TypeError: Cannot read properties of null (reading 'categoryId')
    at D:\backend\routes\products.js:67:45

Đọc được: dòng 67, có gì đó là null, và bạn đang đọc .categoryId của nó. Nhìn dòng 67 là ra ngay.

Quy tắc 2 — Xác định lỗi ở tầng nào

Trình duyệt (React)  →  HTTP  →  Express  →  MongoDB

Cách xác định nhanh:

Kiểm tra Nếu OK thì Nếu lỗi thì
Mở localhost:5000/products trong trình duyệt Backend + DB ổn → lỗi ở frontend Lỗi ở backend hoặc DB
Chạy query trong Compass DB ổn → lỗi ở backend Lỗi ở dữ liệu/DB
DevTools → Network Xem request có gửi đi không, trả về gì

Quy tắc 3 — console.log là bạn thân

console.log("=== VÀO ROUTE ===");
console.log("req.params:", req.params);
console.log("req.body:", req.body);
console.log("req.file:", req.file);
const products = await productCollection.find(filter).toArray();
console.log("Tìm được:", products.length, "sản phẩm");

Đặt log trước và sau chỗ bạn nghi ngờ. Log nào không in ra → chương trình chưa chạy tới đó, hoặc nó ném lỗi ở giữa.

Ở frontend, thêm nhãn để dễ tìm:

console.log("🔵 Productlist props.data:", props.data?.length);
console.log("🟢 sortType:", sortType, "| min:", min, "| max:", max);

Quy tắc 4 — Thu hẹp phạm vi (nhị phân)

Không biết lỗi ở đâu trong 200 dòng? Cắt đôi:

  • Comment nửa dưới → còn lỗi không?
  • Còn → lỗi ở nửa trên. Không còn → lỗi ở nửa dưới
  • Lặp lại

5–6 lần là khoanh vùng được dòng gây lỗi.

Quy tắc 5 — Bốn công cụ phải thành thạo

Công cụ Mở bằng Dùng để
Console F12 → Console Đọc lỗi JS, chạy lệnh thử
Network F12 → Network Xem request gửi gì, nhận gì, status code
Redux DevTools F12 → Redux Xem action, state trước/sau
MongoDB Compass App riêng Xem dữ liệu thật trong DB

Cách dùng tab Network hiệu quả:

  1. Lọc Fetch/XHR để chỉ thấy request API
  2. Bấm vào một request:
    • Headers — URL, method, status code
    • Payload — dữ liệu bạn gửi lên
    • Response — dữ liệu server trả về
  3. Chuột phải → Copy as cURL để chạy lại trong terminal hoặc Postman

Quy tắc 6 — Cô lập vấn đề

Tạo file test nhỏ nhất tái hiện được lỗi:

// test.js — chạy: node test.js
const connectDb = require("./model/db");

(async () => {
  const db = await connectDb();
  const p = await db.collection("products").findOne();
  console.log("categoryId:", p.categoryId, "| type:", typeof p.categoryId);
  process.exit(0);
})();

Bỏ hết Express, React, chỉ còn phần bạn nghi ngờ.

Quy tắc 7 — Kiểm tra giả định

Bug thường nằm ở chỗ bạn chắc chắn là đúng.

  • "Chắc chắn categoryId là ObjectId" → in ra typeof xem
  • "Chắc chắn API trả về mảng" → console.log(Array.isArray(data))
  • "Chắc chắn .env.local đã được đọc" → console.log(process.env.NEXT_PUBLIC_API_URL)

Quy tắc 8 — Tìm kiếm đúng cách

❌ "web bán hàng lỗi"
✅ "MulterError LIMIT_UNEXPECTED_FILE nodejs"
✅ "next.js hydration failed redux persist"

Copy nguyên văn thông báo lỗi (bỏ phần đường dẫn riêng của máy bạn) và dán vào Google. Ưu tiên kết quả từ Stack Overflow và GitHub Issues.

Quy tắc 9 — Ghi lại lỗi đã gặp

Tạo file NOTES.md riêng:

## 16/08 — byCategory trả về rỗng
Nguyên nhân: categoryId trong DB là string, query dùng ObjectId
Cách sửa: script updateMany chuyển sang ObjectId
Bài học: luôn kiểm tra KIỂU dữ liệu, không chỉ giá trị

Sau 3 tháng bạn sẽ có cuốn sổ tay vô giá — và sẽ nhận ra mình gặp lại cùng một loại lỗi nhiều lần.


📋 J. Bảng tra lỗi riêng của dự án này

Tổng hợp toàn bộ vấn đề đã phát hiện trong source gốc:

# File Vấn đề Bài
1 backend/bin/www Port mặc định 3000, trùng Next.js 04
2 backend/app.js routes/index.js không được mount → code chết 04
3 backend/model/db.js Mỗi request tạo kết nối mới, không đóng 05
4 mọi route Không có try/catch → request treo khi lỗi 05
5 routes/products.js products.categoryId đọc trước khi kiểm tra null 06
6 routes/products.js byCategory dùng ObjectId, DB lưu chuỗi → luôn rỗng 06
7 routes/products.js .map() dùng sai mục đích (nên là .forEach()) 06
8 routes/products.js _id: null → chỉ thêm được 1 sản phẩm 07
9 routes/products.js price lưu dạng chuỗi 07
10 routes/products.js req.file.originalname — trùng tên thì ghi đè 07
11 routes/products.js Không validate dữ liệu đầu vào 07
12 routes/oder.js 3 route copy từ products, vô nghĩa và crash 08
13 routes/oder.js Nhận gì lưu nấy — khách sửa được giá 08
14 component/navbar.js Tên component viết thường 09
15 toàn bộ frontend Dùng class thay className 09
16 component/addcardbuton.js Bỏ quên children, hardcode chữ nút 09
17 app/(user)/layout.js src="js/..." thiếu / → modal không chạy ở trang con 10
18 app/(user)/product/page.js Component rỗng, không return → crash 10
19 app/admin/layout.js "use clinet" sai chính tả 10
20 app/admin/layout.js <html>/<body> lồng nhau 17
21 app/(user)/page.js Lặp 9 khối Link, mọi link đều href="/" 11
22 repo Thiếu .env.local mà code phụ thuộc 12
23 product/[id]/page.js return console.log(...) → trang trắng 12
24 product/[id]/page.js 2 nút "thêm vào giỏ" trùng nhau 12
25 product/[id]/page.js /categpry/ + categoryID — sai chính tả và sai tên biến 12
26 slices/sortSlide.js name: 'cart' trùng với cartSlicer 13
27 slices/cartSlicer.js Vừa sửa state vừa return (sai quy tắc Immer) 13
28 slices/cartSlicer.js splice(-1, 1) xóa nhầm phần tử cuối 13
29 slices/cartSlicer.js decreaseItem là hàm rỗng 13
30 redux/store.js Thiếu serializableCheck, persist cả sort/filter 14
31 component/productlist.js useRef giữ dữ liệu cũ → sort "Mặc định" sai 15
32 component/productlist.js .sort() biến đổi mảng gốc 15
33 component/productlist.js Nhánh mặc định vứt bỏ kết quả filter 15
34 component/productlist.js row-cols-ms-2 — class không tồn tại 15
35 menu/layout.js max={100000} nhỏ hơn giá sản phẩm rẻ nhất 15
36 cart/page.js Công thức size không nhất quán (m cố định, l theo SL) 16
37 cart/page.js Tổng tiền không tính phụ phí size 16
38 cart/page.js Báo "thành công" kể cả khi API lỗi → mất giỏ hàng 16
39 cart/page.js key={product._id} trùng khi cùng SP khác size 16
40 admin/page.js Toàn số giả, nội dung mẫu về iPhone/Samsung 17
41 admin/component/recentOrder.js toLOcaString() sai chính tả 17
42 admin/product/page.js product.category.name crash nếu category null 17
43 admin/product/page.js <> bọc <tr> trong map → key sai chỗ 17
44 admin/product/add/page.js .then() rỗng, không xử lý kết quả 18
45 admin/product/add/page.js try/catch không bắt được lỗi Promise 18
46 admin/product/add/page.js Input thiếu value → không reset được 18
47 admin/product/add/page.js <option selected> không có value 18
48 login, register Form tĩnh, chưa nối API 19
49 component/navbar.js Ô tìm kiếm không hoạt động 21
50 routes/index.js Regex tìm kiếm không escape → nguy cơ ReDoS 21

🎓 Lời kết

Bạn đã đi hết 24 bài học. Nhìn lại, bạn đã:

  • Xây một REST API hoàn chỉnh với Express + MongoDB
  • Dựng giao diện thương mại điện tử với Next.js App Router
  • Quản lý state phức tạp với Redux Toolkit
  • Xử lý upload file, xác thực JWT, phân quyền
  • Tìm ra và sửa 50 lỗi thật trong một dự án thật
  • Đưa sản phẩm lên internet

Điều quan trọng nhất bạn học được không phải cú pháp — cú pháp tra được. Đó là:

  1. Tư duy tách tầng — biết lỗi nằm ở frontend, backend hay database
  2. Không tin dữ liệu từ client — backend luôn phải kiểm tra lại
  3. Dữ liệu dẫn xuất thì tính lại, đừng lưu — nguồn gốc của rất nhiều bug
  4. Đọc thông báo lỗi — nó thường đã nói thẳng vấn đề
  5. Nhìn ra code chưa chuẩn — kể cả code đang "chạy được"

Nên học gì tiếp?

Chủ đề Vì sao
TypeScript Bắt lỗi kiểu dữ liệu ngay khi gõ. 50 lỗi ở trên, TypeScript chặn được ~15
Kiểm thử (Jest, Playwright) Sửa code mà không sợ hỏng chỗ khác
Mongoose Schema + validation cho MongoDB
Docker "Chạy được trên máy tôi" không còn là vấn đề
React Query Mạnh hơn SWR cho ứng dụng lớn
Tailwind CSS Thay thế Bootstrap, linh hoạt hơn nhiều

Ý tưởng nâng cấp dự án

  • Thanh toán online (VNPay, MoMo)
  • Đánh giá & bình luận sản phẩm
  • Gửi email xác nhận đơn hàng (Nodemailer)
  • Chat hỗ trợ realtime (Socket.io)
  • Gợi ý sản phẩm theo lịch sử xem
  • Ứng dụng di động (React Native dùng chung API)

Chúc bạn code vui và ít bug! 🌸


⬅️ Bài trước | 🏠 Về mục lục


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí